xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_hs_stop.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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