xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_hs_conn.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <string.h>
21 #include <errno.h>
22 #include "syscfg/syscfg.h"
23 #include "os/os.h"
24 #include "host/ble_hs_id.h"
25 #include "ble_hs_priv.h"
26 
27 /** At least three channels required per connection (sig, att, sm). */
28 #define BLE_HS_CONN_MIN_CHANS       3
29 
30 static SLIST_HEAD(, ble_hs_conn) ble_hs_conns;
31 static struct os_mempool ble_hs_conn_pool;
32 
33 static os_membuf_t ble_hs_conn_elem_mem[
34     OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_CONNECTIONS),
35                     sizeof (struct ble_hs_conn))
36 ];
37 
38 static const uint8_t ble_hs_conn_null_addr[6];
39 
40 int
ble_hs_conn_can_alloc(void)41 ble_hs_conn_can_alloc(void)
42 {
43 #if !NIMBLE_BLE_CONNECT
44     return 0;
45 #endif
46 
47     return ble_hs_conn_pool.mp_num_free >= 1 &&
48            ble_l2cap_chan_pool.mp_num_free >= BLE_HS_CONN_MIN_CHANS &&
49            ble_gatts_conn_can_alloc();
50 }
51 
52 struct ble_l2cap_chan *
ble_hs_conn_chan_find_by_scid(struct ble_hs_conn * conn,uint16_t cid)53 ble_hs_conn_chan_find_by_scid(struct ble_hs_conn *conn, uint16_t cid)
54 {
55 #if !NIMBLE_BLE_CONNECT
56     return NULL;
57 #endif
58 
59     struct ble_l2cap_chan *chan;
60 
61     SLIST_FOREACH(chan, &conn->bhc_channels, next) {
62         if (chan->scid == cid) {
63             return chan;
64         }
65         if (chan->scid > cid) {
66             return NULL;
67         }
68     }
69 
70     return NULL;
71 }
72 
73 struct ble_l2cap_chan *
ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn * conn,uint16_t cid)74 ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, uint16_t cid)
75 {
76 #if !NIMBLE_BLE_CONNECT
77     return NULL;
78 #endif
79 
80     struct ble_l2cap_chan *chan;
81 
82     SLIST_FOREACH(chan, &conn->bhc_channels, next) {
83         if (chan->dcid == cid) {
84             return chan;
85         }
86         if (chan->dcid > cid) {
87             return NULL;
88         }
89     }
90 
91     return NULL;
92 }
93 
94 int
ble_hs_conn_chan_insert(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)95 ble_hs_conn_chan_insert(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
96 {
97 #if !NIMBLE_BLE_CONNECT
98     return BLE_HS_ENOTSUP;
99 #endif
100 
101     struct ble_l2cap_chan *prev;
102     struct ble_l2cap_chan *cur;
103 
104     prev = NULL;
105     SLIST_FOREACH(cur, &conn->bhc_channels, next) {
106         if (cur->scid == chan->scid) {
107             return BLE_HS_EALREADY;
108         }
109         if (cur->scid > chan->scid) {
110             break;
111         }
112 
113         prev = cur;
114     }
115 
116     if (prev == NULL) {
117         SLIST_INSERT_HEAD(&conn->bhc_channels, chan, next);
118     } else {
119         SLIST_INSERT_AFTER(prev, chan, next);
120     }
121 
122     return 0;
123 }
124 
125 struct ble_hs_conn *
ble_hs_conn_alloc(uint16_t conn_handle)126 ble_hs_conn_alloc(uint16_t conn_handle)
127 {
128 #if !NIMBLE_BLE_CONNECT
129     return NULL;
130 #endif
131 
132     struct ble_l2cap_chan *chan;
133     struct ble_hs_conn *conn;
134     int rc;
135 
136     conn = os_memblock_get(&ble_hs_conn_pool);
137     if (conn == NULL) {
138         goto err;
139     }
140     memset(conn, 0, sizeof *conn);
141     conn->bhc_handle = conn_handle;
142 
143     SLIST_INIT(&conn->bhc_channels);
144 
145     chan = ble_att_create_chan(conn_handle);
146     if (chan == NULL) {
147         goto err;
148     }
149     rc = ble_hs_conn_chan_insert(conn, chan);
150     if (rc != 0) {
151         goto err;
152     }
153 
154     chan = ble_l2cap_sig_create_chan(conn_handle);
155     if (chan == NULL) {
156         goto err;
157     }
158     rc = ble_hs_conn_chan_insert(conn, chan);
159     if (rc != 0) {
160         goto err;
161     }
162 
163     /* Create the SM channel even if not configured. We need it to reject SM
164      * messages.
165      */
166     chan = ble_sm_create_chan(conn_handle);
167     if (chan == NULL) {
168         goto err;
169     }
170     rc = ble_hs_conn_chan_insert(conn, chan);
171     if (rc != 0) {
172         goto err;
173     }
174 
175     rc = ble_gatts_conn_init(&conn->bhc_gatt_svr);
176     if (rc != 0) {
177         goto err;
178     }
179 
180     STAILQ_INIT(&conn->bhc_tx_q);
181 
182     STATS_INC(ble_hs_stats, conn_create);
183 
184     return conn;
185 
186 err:
187     ble_hs_conn_free(conn);
188     return NULL;
189 }
190 
191 void
ble_hs_conn_delete_chan(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)192 ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
193 {
194     if (conn->bhc_rx_chan == chan) {
195         conn->bhc_rx_chan = NULL;
196     }
197 
198     SLIST_REMOVE(&conn->bhc_channels, chan, ble_l2cap_chan, next);
199     ble_l2cap_chan_free(chan);
200 }
201 
202 void
ble_hs_conn_free(struct ble_hs_conn * conn)203 ble_hs_conn_free(struct ble_hs_conn *conn)
204 {
205 #if !NIMBLE_BLE_CONNECT
206     return;
207 #endif
208 
209     struct ble_l2cap_chan *chan;
210     int rc;
211 
212     if (conn == NULL) {
213         return;
214     }
215 
216     ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
217 
218     while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {
219         ble_hs_conn_delete_chan(conn, chan);
220     }
221 
222 #if MYNEWT_VAL(BLE_HS_DEBUG)
223     memset(conn, 0xff, sizeof *conn);
224 #endif
225     rc = os_memblock_put(&ble_hs_conn_pool, conn);
226     BLE_HS_DBG_ASSERT_EVAL(rc == 0);
227 
228     STATS_INC(ble_hs_stats, conn_delete);
229 }
230 
231 void
ble_hs_conn_insert(struct ble_hs_conn * conn)232 ble_hs_conn_insert(struct ble_hs_conn *conn)
233 {
234 #if !NIMBLE_BLE_CONNECT
235     return;
236 #endif
237 
238     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
239 
240     BLE_HS_DBG_ASSERT_EVAL(ble_hs_conn_find(conn->bhc_handle) == NULL);
241     SLIST_INSERT_HEAD(&ble_hs_conns, conn, bhc_next);
242 }
243 
244 void
ble_hs_conn_remove(struct ble_hs_conn * conn)245 ble_hs_conn_remove(struct ble_hs_conn *conn)
246 {
247 #if !NIMBLE_BLE_CONNECT
248     return;
249 #endif
250 
251     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
252 
253     SLIST_REMOVE(&ble_hs_conns, conn, ble_hs_conn, bhc_next);
254 }
255 
256 struct ble_hs_conn *
ble_hs_conn_find(uint16_t conn_handle)257 ble_hs_conn_find(uint16_t conn_handle)
258 {
259 #if !NIMBLE_BLE_CONNECT
260     return NULL;
261 #endif
262 
263     struct ble_hs_conn *conn;
264 
265     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
266 
267     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
268         if (conn->bhc_handle == conn_handle) {
269             return conn;
270         }
271     }
272 
273     return NULL;
274 }
275 
276 struct ble_hs_conn *
ble_hs_conn_find_assert(uint16_t conn_handle)277 ble_hs_conn_find_assert(uint16_t conn_handle)
278 {
279     struct ble_hs_conn *conn;
280 
281     conn = ble_hs_conn_find(conn_handle);
282     BLE_HS_DBG_ASSERT(conn != NULL);
283 
284     return conn;
285 }
286 
287 struct ble_hs_conn *
ble_hs_conn_find_by_addr(const ble_addr_t * addr)288 ble_hs_conn_find_by_addr(const ble_addr_t *addr)
289 {
290 #if !NIMBLE_BLE_CONNECT
291     return NULL;
292 #endif
293 
294     struct ble_hs_conn *conn;
295 
296     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
297 
298     if (!addr) {
299         return NULL;
300     }
301 
302     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
303         if (ble_addr_cmp(&conn->bhc_peer_addr, addr) == 0) {
304             return conn;
305         }
306     }
307 
308     return NULL;
309 }
310 
311 struct ble_hs_conn *
ble_hs_conn_find_by_idx(int idx)312 ble_hs_conn_find_by_idx(int idx)
313 {
314 #if !NIMBLE_BLE_CONNECT
315     return NULL;
316 #endif
317 
318     struct ble_hs_conn *conn;
319     int i;
320 
321     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
322 
323     i = 0;
324     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
325         if (i == idx) {
326             return conn;
327         }
328 
329         i++;
330     }
331 
332     return NULL;
333 }
334 
335 int
ble_hs_conn_exists(uint16_t conn_handle)336 ble_hs_conn_exists(uint16_t conn_handle)
337 {
338 #if !NIMBLE_BLE_CONNECT
339     return 0;
340 #endif
341     return ble_hs_conn_find(conn_handle) != NULL;
342 }
343 
344 /**
345  * Retrieves the first connection in the list.
346  */
347 struct ble_hs_conn *
ble_hs_conn_first(void)348 ble_hs_conn_first(void)
349 {
350 #if !NIMBLE_BLE_CONNECT
351     return NULL;
352 #endif
353 
354     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
355     return SLIST_FIRST(&ble_hs_conns);
356 }
357 
358 void
ble_hs_conn_addrs(const struct ble_hs_conn * conn,struct ble_hs_conn_addrs * addrs)359 ble_hs_conn_addrs(const struct ble_hs_conn *conn,
360                   struct ble_hs_conn_addrs *addrs)
361 {
362     const uint8_t *our_id_addr_val;
363     int rc;
364 
365     /* Determine our address information. */
366     addrs->our_id_addr.type =
367         ble_hs_misc_addr_type_to_id(conn->bhc_our_addr_type);
368 
369 #if MYNEWT_VAL(BLE_EXT_ADV)
370     /* With EA enabled random address for slave connection is per advertising
371      * instance and requires special handling here.
372      */
373 
374     if (!(conn->bhc_flags & BLE_HS_CONN_F_MASTER) &&
375             addrs->our_id_addr.type == BLE_ADDR_RANDOM) {
376         our_id_addr_val = conn->bhc_our_rnd_addr;
377     } else {
378         rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
379         assert(rc == 0);
380     }
381 #else
382     rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
383     assert(rc == 0);
384 #endif
385 
386     memcpy(addrs->our_id_addr.val, our_id_addr_val, 6);
387 
388     if (memcmp(conn->bhc_our_rpa_addr.val, ble_hs_conn_null_addr, 6) == 0) {
389         addrs->our_ota_addr = addrs->our_id_addr;
390     } else {
391         addrs->our_ota_addr = conn->bhc_our_rpa_addr;
392     }
393 
394     /* Determine peer address information. */
395     addrs->peer_id_addr = conn->bhc_peer_addr;
396     addrs->peer_ota_addr = conn->bhc_peer_addr;
397     switch (conn->bhc_peer_addr.type) {
398     case BLE_ADDR_PUBLIC:
399     case BLE_ADDR_RANDOM:
400         break;
401 
402     case BLE_ADDR_PUBLIC_ID:
403         addrs->peer_id_addr.type = BLE_ADDR_PUBLIC;
404         addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
405         break;
406 
407     case BLE_ADDR_RANDOM_ID:
408         addrs->peer_id_addr.type = BLE_ADDR_RANDOM;
409         addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
410         break;
411 
412     default:
413         BLE_HS_DBG_ASSERT(0);
414         break;
415     }
416 }
417 
418 int32_t
ble_hs_conn_timer(void)419 ble_hs_conn_timer(void)
420 {
421     /* If there are no timeouts configured, then there is nothing to check. */
422 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) == 0 && \
423     BLE_HS_ATT_SVR_QUEUED_WRITE_TMO == 0
424 
425     return BLE_HS_FOREVER;
426 #endif
427 
428     struct ble_hs_conn *conn;
429     ble_npl_time_t now;
430     int32_t next_exp_in;
431     int32_t time_diff;
432     uint16_t conn_handle;
433 
434     conn_handle = BLE_HS_CONN_HANDLE_NONE;
435     next_exp_in = BLE_HS_FOREVER;
436     now = ble_npl_time_get();
437 
438     ble_hs_lock();
439 
440     /* This loop performs one of two tasks:
441      * 1. Determine if any connections need to be terminated due to timeout.
442      *    If so, break out of the loop and terminate the connection.  This
443      *    function will need to be executed again.
444      * 2. Otherwise, determine when the next timeout will occur.
445      */
446     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
447         if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
448 
449 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
450             /* Check each connection's rx fragment timer.  If too much time
451              * passes after a partial packet is received, the connection is
452              * terminated.
453              */
454             if (conn->bhc_rx_chan != NULL) {
455                 time_diff = conn->bhc_rx_timeout - now;
456 
457                 if (time_diff <= 0) {
458                     /* ACL reassembly has timed out.  Remember the connection
459                      * handle so it can be terminated after the mutex is
460                      * unlocked.
461                      */
462                     conn_handle = conn->bhc_handle;
463                     break;
464                 }
465 
466                 /* Determine if this connection is the soonest to time out. */
467                 if (time_diff < next_exp_in) {
468                     next_exp_in = time_diff;
469                 }
470             }
471 #endif
472 
473 #if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
474             /* Check each connection's rx queued write timer.  If too much
475              * time passes after a prep write is received, the queue is
476              * cleared.
477              */
478             time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
479             if (time_diff <= 0) {
480                 /* ACL reassembly has timed out.  Remember the connection
481                  * handle so it can be terminated after the mutex is
482                  * unlocked.
483                  */
484                 conn_handle = conn->bhc_handle;
485                 break;
486             }
487 
488             /* Determine if this connection is the soonest to time out. */
489             if (time_diff < next_exp_in) {
490                 next_exp_in = time_diff;
491             }
492 #endif
493         }
494     }
495 
496     ble_hs_unlock();
497 
498     /* If a connection has timed out, terminate it.  We need to recursively
499      * call this function again to determine when the next timeout is.  This
500      * is a tail-recursive call, so it should be optimized to execute in the
501      * same stack frame.
502      */
503     if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
504         ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
505         return ble_hs_conn_timer();
506     }
507 
508     return next_exp_in;
509 }
510 
511 int
ble_hs_conn_init(void)512 ble_hs_conn_init(void)
513 {
514     int rc;
515 
516     rc = os_mempool_init(&ble_hs_conn_pool, MYNEWT_VAL(BLE_MAX_CONNECTIONS),
517                          sizeof (struct ble_hs_conn),
518                          ble_hs_conn_elem_mem, "ble_hs_conn_pool");
519     if (rc != 0) {
520         return BLE_HS_EOS;
521     }
522 
523     SLIST_INIT(&ble_hs_conns);
524 
525     return 0;
526 }
527