1*042d53a7SEvalZero /*
2*042d53a7SEvalZero * Licensed to the Apache Software Foundation (ASF) under one
3*042d53a7SEvalZero * or more contributor license agreements. See the NOTICE file
4*042d53a7SEvalZero * distributed with this work for additional information
5*042d53a7SEvalZero * regarding copyright ownership. The ASF licenses this file
6*042d53a7SEvalZero * to you under the Apache License, Version 2.0 (the
7*042d53a7SEvalZero * "License"); you may not use this file except in compliance
8*042d53a7SEvalZero * with the License. You may obtain a copy of the License at
9*042d53a7SEvalZero *
10*042d53a7SEvalZero * http://www.apache.org/licenses/LICENSE-2.0
11*042d53a7SEvalZero *
12*042d53a7SEvalZero * Unless required by applicable law or agreed to in writing,
13*042d53a7SEvalZero * software distributed under the License is distributed on an
14*042d53a7SEvalZero * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15*042d53a7SEvalZero * KIND, either express or implied. See the License for the
16*042d53a7SEvalZero * specific language governing permissions and limitations
17*042d53a7SEvalZero * under the License.
18*042d53a7SEvalZero */
19*042d53a7SEvalZero
20*042d53a7SEvalZero #include <assert.h>
21*042d53a7SEvalZero #include "sysinit/sysinit.h"
22*042d53a7SEvalZero #include "syscfg/syscfg.h"
23*042d53a7SEvalZero #include "ble_hs_priv.h"
24*042d53a7SEvalZero
25*042d53a7SEvalZero static ble_npl_event_fn ble_hs_stop_term_event_cb;
26*042d53a7SEvalZero static struct ble_npl_event ble_hs_stop_term_ev;
27*042d53a7SEvalZero
28*042d53a7SEvalZero static struct ble_gap_event_listener ble_hs_stop_gap_listener;
29*042d53a7SEvalZero
30*042d53a7SEvalZero /**
31*042d53a7SEvalZero * List of stop listeners. These are notified when a stop procedure completes.
32*042d53a7SEvalZero */
33*042d53a7SEvalZero SLIST_HEAD(ble_hs_stop_listener_slist, ble_hs_stop_listener);
34*042d53a7SEvalZero static struct ble_hs_stop_listener_slist ble_hs_stop_listeners;
35*042d53a7SEvalZero
36*042d53a7SEvalZero /**
37*042d53a7SEvalZero * Called when a stop procedure has completed.
38*042d53a7SEvalZero */
39*042d53a7SEvalZero static void
ble_hs_stop_done(int status)40*042d53a7SEvalZero ble_hs_stop_done(int status)
41*042d53a7SEvalZero {
42*042d53a7SEvalZero struct ble_hs_stop_listener_slist slist;
43*042d53a7SEvalZero struct ble_hs_stop_listener *listener;
44*042d53a7SEvalZero
45*042d53a7SEvalZero ble_hs_lock();
46*042d53a7SEvalZero
47*042d53a7SEvalZero ble_gap_event_listener_unregister(&ble_hs_stop_gap_listener);
48*042d53a7SEvalZero
49*042d53a7SEvalZero slist = ble_hs_stop_listeners;
50*042d53a7SEvalZero SLIST_INIT(&ble_hs_stop_listeners);
51*042d53a7SEvalZero
52*042d53a7SEvalZero ble_hs_enabled_state = BLE_HS_ENABLED_STATE_OFF;
53*042d53a7SEvalZero
54*042d53a7SEvalZero ble_hs_unlock();
55*042d53a7SEvalZero
56*042d53a7SEvalZero SLIST_FOREACH(listener, &slist, link) {
57*042d53a7SEvalZero listener->fn(status, listener->arg);
58*042d53a7SEvalZero }
59*042d53a7SEvalZero }
60*042d53a7SEvalZero
61*042d53a7SEvalZero /**
62*042d53a7SEvalZero * Terminates the first open connection.
63*042d53a7SEvalZero *
64*042d53a7SEvalZero * If there are no open connections, signals completion of the close procedure.
65*042d53a7SEvalZero */
66*042d53a7SEvalZero static void
ble_hs_stop_terminate_next_conn(void)67*042d53a7SEvalZero ble_hs_stop_terminate_next_conn(void)
68*042d53a7SEvalZero {
69*042d53a7SEvalZero uint16_t handle;
70*042d53a7SEvalZero int rc;
71*042d53a7SEvalZero
72*042d53a7SEvalZero handle = ble_hs_atomic_first_conn_handle();
73*042d53a7SEvalZero if (handle == BLE_HS_CONN_HANDLE_NONE) {
74*042d53a7SEvalZero /* No open connections. Signal completion of the stop procedure. */
75*042d53a7SEvalZero ble_hs_stop_done(0);
76*042d53a7SEvalZero return;
77*042d53a7SEvalZero }
78*042d53a7SEvalZero
79*042d53a7SEvalZero rc = ble_gap_terminate(handle, BLE_ERR_REM_USER_CONN_TERM);
80*042d53a7SEvalZero if (rc == 0) {
81*042d53a7SEvalZero /* Terminate procedure successfully initiated. Let the GAP event
82*042d53a7SEvalZero * handler deal with the result.
83*042d53a7SEvalZero */
84*042d53a7SEvalZero } else {
85*042d53a7SEvalZero BLE_HS_LOG(ERROR,
86*042d53a7SEvalZero "ble_hs_stop: failed to terminate connection; rc=%d\n", rc);
87*042d53a7SEvalZero ble_hs_stop_done(rc);
88*042d53a7SEvalZero }
89*042d53a7SEvalZero }
90*042d53a7SEvalZero
91*042d53a7SEvalZero /**
92*042d53a7SEvalZero * Event handler. Attempts to terminate the first open connection if there is
93*042d53a7SEvalZero * one. All additional connections are terminated elsewhere in the GAP event
94*042d53a7SEvalZero * handler.
95*042d53a7SEvalZero *
96*042d53a7SEvalZero * If there are no connections, signals completion of the stop procedure.
97*042d53a7SEvalZero */
98*042d53a7SEvalZero static void
ble_hs_stop_term_event_cb(struct ble_npl_event * ev)99*042d53a7SEvalZero ble_hs_stop_term_event_cb(struct ble_npl_event *ev)
100*042d53a7SEvalZero {
101*042d53a7SEvalZero ble_hs_stop_terminate_next_conn();
102*042d53a7SEvalZero }
103*042d53a7SEvalZero
104*042d53a7SEvalZero /**
105*042d53a7SEvalZero * GAP event callback. Listens for connection termination and then terminates
106*042d53a7SEvalZero * the next one.
107*042d53a7SEvalZero *
108*042d53a7SEvalZero * If there are no connections, signals completion of the stop procedure.
109*042d53a7SEvalZero */
110*042d53a7SEvalZero static int
ble_hs_stop_gap_event(struct ble_gap_event * event,void * arg)111*042d53a7SEvalZero ble_hs_stop_gap_event(struct ble_gap_event *event, void *arg)
112*042d53a7SEvalZero {
113*042d53a7SEvalZero /* Only process connection termination events. */
114*042d53a7SEvalZero if (event->type == BLE_GAP_EVENT_DISCONNECT ||
115*042d53a7SEvalZero event->type == BLE_GAP_EVENT_TERM_FAILURE) {
116*042d53a7SEvalZero
117*042d53a7SEvalZero ble_hs_stop_terminate_next_conn();
118*042d53a7SEvalZero }
119*042d53a7SEvalZero
120*042d53a7SEvalZero return 0;
121*042d53a7SEvalZero }
122*042d53a7SEvalZero
123*042d53a7SEvalZero /**
124*042d53a7SEvalZero * Registers a listener to listen for completion of the current stop procedure.
125*042d53a7SEvalZero */
126*042d53a7SEvalZero static void
ble_hs_stop_register_listener(struct ble_hs_stop_listener * listener,ble_hs_stop_fn * fn,void * arg)127*042d53a7SEvalZero ble_hs_stop_register_listener(struct ble_hs_stop_listener *listener,
128*042d53a7SEvalZero ble_hs_stop_fn *fn, void *arg)
129*042d53a7SEvalZero {
130*042d53a7SEvalZero BLE_HS_DBG_ASSERT(fn != NULL);
131*042d53a7SEvalZero
132*042d53a7SEvalZero listener->fn = fn;
133*042d53a7SEvalZero listener->arg = arg;
134*042d53a7SEvalZero SLIST_INSERT_HEAD(&ble_hs_stop_listeners, listener, link);
135*042d53a7SEvalZero }
136*042d53a7SEvalZero
137*042d53a7SEvalZero static int
ble_hs_stop_begin(struct ble_hs_stop_listener * listener,ble_hs_stop_fn * fn,void * arg)138*042d53a7SEvalZero ble_hs_stop_begin(struct ble_hs_stop_listener *listener,
139*042d53a7SEvalZero ble_hs_stop_fn *fn, void *arg)
140*042d53a7SEvalZero {
141*042d53a7SEvalZero switch (ble_hs_enabled_state) {
142*042d53a7SEvalZero case BLE_HS_ENABLED_STATE_ON:
143*042d53a7SEvalZero /* Host is enabled; proceed with the stop procedure. */
144*042d53a7SEvalZero ble_hs_enabled_state = BLE_HS_ENABLED_STATE_STOPPING;
145*042d53a7SEvalZero if (listener != NULL) {
146*042d53a7SEvalZero ble_hs_stop_register_listener(listener, fn, arg);
147*042d53a7SEvalZero }
148*042d53a7SEvalZero
149*042d53a7SEvalZero /* Put the host in the "stopping" state and ensure the host timer is
150*042d53a7SEvalZero * not running.
151*042d53a7SEvalZero */
152*042d53a7SEvalZero ble_hs_timer_resched();
153*042d53a7SEvalZero return 0;
154*042d53a7SEvalZero
155*042d53a7SEvalZero case BLE_HS_ENABLED_STATE_STOPPING:
156*042d53a7SEvalZero /* A stop procedure is already in progress. Just listen for the
157*042d53a7SEvalZero * procedure's completion.
158*042d53a7SEvalZero */
159*042d53a7SEvalZero if (listener != NULL) {
160*042d53a7SEvalZero ble_hs_stop_register_listener(listener, fn, arg);
161*042d53a7SEvalZero }
162*042d53a7SEvalZero return BLE_HS_EBUSY;
163*042d53a7SEvalZero
164*042d53a7SEvalZero case BLE_HS_ENABLED_STATE_OFF:
165*042d53a7SEvalZero /* Host already stopped. */
166*042d53a7SEvalZero return BLE_HS_EALREADY;
167*042d53a7SEvalZero
168*042d53a7SEvalZero default:
169*042d53a7SEvalZero assert(0);
170*042d53a7SEvalZero return BLE_HS_EUNKNOWN;
171*042d53a7SEvalZero }
172*042d53a7SEvalZero }
173*042d53a7SEvalZero
174*042d53a7SEvalZero int
ble_hs_stop(struct ble_hs_stop_listener * listener,ble_hs_stop_fn * fn,void * arg)175*042d53a7SEvalZero ble_hs_stop(struct ble_hs_stop_listener *listener,
176*042d53a7SEvalZero ble_hs_stop_fn *fn, void *arg)
177*042d53a7SEvalZero {
178*042d53a7SEvalZero int rc;
179*042d53a7SEvalZero
180*042d53a7SEvalZero ble_hs_lock();
181*042d53a7SEvalZero rc = ble_hs_stop_begin(listener, fn, arg);
182*042d53a7SEvalZero ble_hs_unlock();
183*042d53a7SEvalZero
184*042d53a7SEvalZero switch (rc) {
185*042d53a7SEvalZero case 0:
186*042d53a7SEvalZero break;
187*042d53a7SEvalZero
188*042d53a7SEvalZero case BLE_HS_EBUSY:
189*042d53a7SEvalZero return 0;
190*042d53a7SEvalZero
191*042d53a7SEvalZero default:
192*042d53a7SEvalZero return rc;
193*042d53a7SEvalZero }
194*042d53a7SEvalZero
195*042d53a7SEvalZero /* Abort all active GAP procedures. */
196*042d53a7SEvalZero ble_gap_preempt();
197*042d53a7SEvalZero ble_gap_preempt_done();
198*042d53a7SEvalZero
199*042d53a7SEvalZero rc = ble_gap_event_listener_register(&ble_hs_stop_gap_listener,
200*042d53a7SEvalZero ble_hs_stop_gap_event, NULL);
201*042d53a7SEvalZero if (rc != 0) {
202*042d53a7SEvalZero return rc;
203*042d53a7SEvalZero }
204*042d53a7SEvalZero
205*042d53a7SEvalZero /* Schedule termination of all open connections in the host task. This is
206*042d53a7SEvalZero * done even if there are no open connections so that the result of the
207*042d53a7SEvalZero * stop procedure is signaled in a consistent manner (asynchronously).
208*042d53a7SEvalZero */
209*042d53a7SEvalZero ble_npl_eventq_put(ble_hs_evq_get(), &ble_hs_stop_term_ev);
210*042d53a7SEvalZero
211*042d53a7SEvalZero return 0;
212*042d53a7SEvalZero }
213*042d53a7SEvalZero
214*042d53a7SEvalZero void
ble_hs_stop_init(void)215*042d53a7SEvalZero ble_hs_stop_init(void)
216*042d53a7SEvalZero {
217*042d53a7SEvalZero ble_npl_event_init(&ble_hs_stop_term_ev, ble_hs_stop_term_event_cb, NULL);
218*042d53a7SEvalZero }
219