• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ble_hs_conn_can_alloc(void)40 int ble_hs_conn_can_alloc(void)
41 {
42 #if !NIMBLE_BLE_CONNECT
43     return 0;
44 #endif
45     return ble_hs_conn_pool.mp_num_free >= 1 &&
46            ble_l2cap_chan_pool.mp_num_free >= BLE_HS_CONN_MIN_CHANS &&
47            ble_gatts_conn_can_alloc();
48 }
49 
ble_hs_conn_chan_find_by_scid(struct ble_hs_conn * conn,uint16_t cid)50 struct ble_l2cap_chan *ble_hs_conn_chan_find_by_scid(struct ble_hs_conn *conn, uint16_t cid)
51 {
52 #if !NIMBLE_BLE_CONNECT
53     return NULL;
54 #endif
55     struct ble_l2cap_chan *chan;
56     SLIST_FOREACH(chan, &conn->bhc_channels, next) {
57         if (chan->scid == cid) {
58             return chan;
59         }
60 
61         if (chan->scid > cid) {
62             return NULL;
63         }
64     }
65     return NULL;
66 }
67 
ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn * conn,uint16_t cid)68 struct ble_l2cap_chan *ble_hs_conn_chan_find_by_dcid(struct ble_hs_conn *conn, uint16_t cid)
69 {
70 #if !NIMBLE_BLE_CONNECT
71     return NULL;
72 #endif
73     struct ble_l2cap_chan *chan;
74     SLIST_FOREACH(chan, &conn->bhc_channels, next) {
75         if (chan->dcid == cid) {
76             return chan;
77         }
78     }
79     return NULL;
80 }
81 
ble_hs_conn_chan_exist(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)82 bool ble_hs_conn_chan_exist(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
83 {
84 #if !NIMBLE_BLE_CONNECT
85     return NULL;
86 #endif
87     struct ble_l2cap_chan *tmp;
88     SLIST_FOREACH(tmp, &conn->bhc_channels, next) {
89         if (chan == tmp) {
90             return true;
91         }
92     }
93     return false;
94 }
95 
ble_hs_conn_chan_insert(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)96 int ble_hs_conn_chan_insert(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
97 {
98 #if !NIMBLE_BLE_CONNECT
99     return BLE_HS_ENOTSUP;
100 #endif
101     struct ble_l2cap_chan *prev;
102     struct ble_l2cap_chan *cur;
103     prev = NULL;
104     SLIST_FOREACH(cur, &conn->bhc_channels, next) {
105         if (cur->scid == chan->scid) {
106             return BLE_HS_EALREADY;
107         }
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 
ble_hs_conn_alloc(uint16_t conn_handle)125 struct ble_hs_conn *ble_hs_conn_alloc(uint16_t conn_handle)
126 {
127 #if !NIMBLE_BLE_CONNECT
128     return NULL;
129 #endif
130     struct ble_l2cap_chan *chan;
131     struct ble_hs_conn *conn;
132     int rc;
133     conn = os_memblock_get(&ble_hs_conn_pool);
134     if (conn == NULL) {
135         goto err;
136     }
137 
138     memset_s(conn, sizeof * conn, 0, sizeof * conn);
139     conn->bhc_handle = conn_handle;
140     conn->tx_ll_mtu = MYNEWT_VAL(BLE_LL_CONN_INIT_MAX_TX_BYTES);
141     SLIST_INIT(&conn->bhc_channels);
142     chan = ble_att_create_chan(conn_handle);
143     if (chan == NULL) {
144         goto err;
145     }
146 
147     rc = ble_hs_conn_chan_insert(conn, chan);
148     if (rc != 0) {
149         goto err;
150     }
151 
152     chan = ble_l2cap_sig_create_chan(conn_handle);
153     if (chan == NULL) {
154         goto err;
155     }
156 
157     rc = ble_hs_conn_chan_insert(conn, chan);
158     if (rc != 0) {
159         goto err;
160     }
161 
162     /* Create the SM channel even if not configured. We need it to reject SM
163      * messages.
164      */
165     chan = ble_sm_create_chan(conn_handle);
166     if (chan == NULL) {
167         goto err;
168     }
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     STATS_INC(ble_hs_stats, conn_create);
182     return conn;
183 err:
184     ble_hs_conn_free(conn);
185     return NULL;
186 }
187 
ble_hs_conn_delete_chan(struct ble_hs_conn * conn,struct ble_l2cap_chan * chan)188 void ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
189 {
190     if (conn->bhc_rx_chan == chan) {
191         conn->bhc_rx_chan = NULL;
192     }
193 
194     SLIST_REMOVE(&conn->bhc_channels, chan, ble_l2cap_chan, next);
195     ble_l2cap_chan_free(conn, chan);
196 }
197 
ble_hs_conn_foreach(ble_hs_conn_foreach_fn * cb,void * arg)198 void ble_hs_conn_foreach(ble_hs_conn_foreach_fn *cb, void *arg)
199 {
200     struct ble_hs_conn *conn;
201     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
202     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
203         if (cb(conn, arg) != 0) {
204             return;
205         }
206     }
207 }
208 
ble_hs_conn_free(struct ble_hs_conn * conn)209 void ble_hs_conn_free(struct ble_hs_conn *conn)
210 {
211 #if !NIMBLE_BLE_CONNECT
212     return;
213 #endif
214     struct ble_l2cap_chan *chan;
215     struct os_mbuf_pkthdr *omp;
216     int rc;
217 
218     if (conn == NULL) {
219         return;
220     }
221 
222     ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
223 
224     while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {
225         ble_hs_conn_delete_chan(conn, chan);
226     }
227 
228     while ((omp = STAILQ_FIRST(&conn->bhc_tx_q)) != NULL) {
229         STAILQ_REMOVE_HEAD(&conn->bhc_tx_q, omp_next);
230         os_mbuf_free_chain(OS_MBUF_PKTHDR_TO_MBUF(omp));
231     }
232 
233 #if MYNEWT_VAL(BLE_HS_DEBUG)
234     memset_s(conn, sizeof * conn, 0xff, sizeof * conn);
235 #endif
236     rc = os_memblock_put(&ble_hs_conn_pool, conn);
237     BLE_HS_DBG_ASSERT_EVAL(rc == 0);
238     STATS_INC(ble_hs_stats, conn_delete);
239 }
240 
ble_hs_conn_insert(struct ble_hs_conn * conn)241 void ble_hs_conn_insert(struct ble_hs_conn *conn)
242 {
243 #if !NIMBLE_BLE_CONNECT
244     return;
245 #endif
246     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
247     BLE_HS_DBG_ASSERT_EVAL(ble_hs_conn_find(conn->bhc_handle) == NULL);
248     SLIST_INSERT_HEAD(&ble_hs_conns, conn, bhc_next);
249 }
250 
ble_hs_conn_remove(struct ble_hs_conn * conn)251 void ble_hs_conn_remove(struct ble_hs_conn *conn)
252 {
253 #if !NIMBLE_BLE_CONNECT
254     return;
255 #endif
256     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
257     SLIST_REMOVE(&ble_hs_conns, conn, ble_hs_conn, bhc_next);
258 }
259 
ble_hs_conn_find(uint16_t conn_handle)260 struct ble_hs_conn *ble_hs_conn_find(uint16_t conn_handle)
261 {
262 #if !NIMBLE_BLE_CONNECT
263     return NULL;
264 #endif
265     struct ble_hs_conn *conn;
266     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
267     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
268         if (conn->bhc_handle == conn_handle) {
269             return conn;
270         }
271     }
272     return NULL;
273 }
274 
ble_hs_conn_find_assert(uint16_t conn_handle)275 struct ble_hs_conn *ble_hs_conn_find_assert(uint16_t conn_handle)
276 {
277     struct ble_hs_conn *conn;
278     conn = ble_hs_conn_find(conn_handle);
279     BLE_HS_DBG_ASSERT(conn != NULL);
280     return conn;
281 }
282 
ble_hs_conn_find_by_addr(const ble_addr_t * addr)283 struct ble_hs_conn *ble_hs_conn_find_by_addr(const ble_addr_t *addr)
284 {
285 #if !NIMBLE_BLE_CONNECT
286     return NULL;
287 #endif
288     struct ble_hs_conn *conn;
289     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
290 
291     if (!addr) {
292         return NULL;
293     }
294 
295     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
296         if (BLE_ADDR_IS_RPA(addr)) {
297             if (ble_addr_cmp(&conn->bhc_peer_rpa_addr, addr) == 0) {
298                 return conn;
299             }
300         } else {
301             if (ble_addr_cmp(&conn->bhc_peer_addr, addr) == 0) {
302                 return conn;
303             }
304         }
305     }
306     return NULL;
307 }
308 
ble_hs_conn_find_by_idx(int idx)309 struct ble_hs_conn *ble_hs_conn_find_by_idx(int idx)
310 {
311 #if !NIMBLE_BLE_CONNECT
312     return NULL;
313 #endif
314     struct ble_hs_conn *conn;
315     int i;
316     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
317     i = 0;
318     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
319         if (i == idx) {
320             return conn;
321         }
322 
323         i++;
324     }
325     return NULL;
326 }
327 
ble_hs_conn_exists(uint16_t conn_handle)328 int ble_hs_conn_exists(uint16_t conn_handle)
329 {
330 #if !NIMBLE_BLE_CONNECT
331     return 0;
332 #endif
333     return ble_hs_conn_find(conn_handle) != NULL;
334 }
335 
336 /**
337  * Retrieves the first connection in the list.
338  */
ble_hs_conn_first(void)339 struct ble_hs_conn *ble_hs_conn_first(void)
340 {
341 #if !NIMBLE_BLE_CONNECT
342     return NULL;
343 #endif
344     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
345     return SLIST_FIRST(&ble_hs_conns);
346 }
347 
ble_hs_conn_addrs(const struct ble_hs_conn * conn,struct ble_hs_conn_addrs * addrs)348 void ble_hs_conn_addrs(const struct ble_hs_conn *conn, struct ble_hs_conn_addrs *addrs)
349 {
350     const uint8_t *our_id_addr_val;
351     int rc;
352     /* Determine our address information. */
353     addrs->our_id_addr.type = ble_hs_misc_own_addr_type_to_id(conn->bhc_our_addr_type);
354 #if MYNEWT_VAL(BLE_EXT_ADV)
355     /* With EA enabled random address for slave connection is per advertising
356      * instance and requires special handling here.
357      */
358 
359     if (!(conn->bhc_flags & BLE_HS_CONN_F_MASTER) &&
360             addrs->our_id_addr.type == BLE_ADDR_RANDOM) {
361         our_id_addr_val = conn->bhc_our_rnd_addr;
362     } else {
363         rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
364         assert(rc == 0);
365     }
366 
367 #else
368     rc = ble_hs_id_addr(addrs->our_id_addr.type, &our_id_addr_val, NULL);
369     assert(rc == 0);
370 #endif
371     memcpy_s(addrs->our_id_addr.val, sizeof(addrs->our_id_addr.val), our_id_addr_val, 6); // 6:size
372 
373     if (memcmp(conn->bhc_our_rpa_addr.val, ble_hs_conn_null_addr, 6) == 0) { // 6:size
374         addrs->our_ota_addr = addrs->our_id_addr;
375     } else {
376         addrs->our_ota_addr = conn->bhc_our_rpa_addr;
377     }
378 
379     /* Determine peer address information. */
380     addrs->peer_id_addr = conn->bhc_peer_addr;
381     addrs->peer_ota_addr = conn->bhc_peer_addr;
382 
383     switch (conn->bhc_peer_addr.type) {
384         case BLE_ADDR_PUBLIC:
385         case BLE_ADDR_RANDOM:
386             break;
387 
388         case BLE_ADDR_PUBLIC_ID:
389             addrs->peer_id_addr.type = BLE_ADDR_PUBLIC;
390             addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
391             break;
392 
393         case BLE_ADDR_RANDOM_ID:
394             addrs->peer_id_addr.type = BLE_ADDR_RANDOM;
395             addrs->peer_ota_addr = conn->bhc_peer_rpa_addr;
396             break;
397 
398         default:
399             BLE_HS_DBG_ASSERT(0);
400             break;
401     }
402 }
403 
ble_hs_conn_timer(void)404 int32_t ble_hs_conn_timer(void)
405 {
406     /* If there are no timeouts configured, then there is nothing to check. */
407 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) == 0 && \
408     BLE_HS_ATT_SVR_QUEUED_WRITE_TMO == 0
409     return BLE_HS_FOREVER;
410 #endif
411     struct ble_hs_conn *conn;
412     ble_npl_time_t now;
413     int32_t next_exp_in;
414     int32_t time_diff;
415     uint16_t conn_handle;
416     conn_handle = BLE_HS_CONN_HANDLE_NONE;
417     next_exp_in = BLE_HS_FOREVER;
418     now = ble_npl_time_get();
419     ble_hs_lock();
420     /* This loop performs one of two tasks:
421      * 1. Determine if any connections need to be terminated due to timeout.
422      *    If so, break out of the loop and terminate the connection.  This
423      *    function will need to be executed again.
424      * 2. Otherwise, determine when the next timeout will occur.
425      */
426     SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
427         if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
428 #if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
429 
430             /* Check each connection's rx fragment timer.  If too much time
431              * passes after a partial packet is received, the connection is
432              * terminated.
433              */
434             if (conn->bhc_rx_chan != NULL) {
435                 time_diff = conn->bhc_rx_timeout - now;
436                 if (time_diff <= 0) {
437                     /* ACL reassembly has timed out.  Remember the connection
438                      * handle so it can be terminated after the mutex is
439                      * unlocked.
440                      */
441                     conn_handle = conn->bhc_handle;
442                     break;
443                 }
444 
445                 /* Determine if this connection is the soonest to time out. */
446                 if (time_diff < next_exp_in) {
447                     next_exp_in = time_diff;
448                 }
449             }
450 
451 #endif
452 #if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
453             /* Check each connection's rx queued write timer.  If too much
454              * time passes after a prep write is received, the queue is
455              * cleared.
456              */
457             time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
458             if (time_diff <= 0) {
459                 /* ACL reassembly has timed out.  Remember the connection
460                  * handle so it can be terminated after the mutex is
461                  * unlocked.
462                  */
463                 conn_handle = conn->bhc_handle;
464                 break;
465             }
466 
467             /* Determine if this connection is the soonest to time out. */
468             if (time_diff < next_exp_in) {
469                 next_exp_in = time_diff;
470             }
471 
472 #endif
473         }
474     }
475     ble_hs_unlock();
476 
477     /* If a connection has timed out, terminate it.  We need to recursively
478      * call this function again to determine when the next timeout is.  This
479      * is a tail-recursive call, so it should be optimized to execute in the
480      * same stack frame.
481      */
482     if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
483         ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
484         return ble_hs_conn_timer();
485     }
486 
487     return next_exp_in;
488 }
489 
ble_hs_conn_init(void)490 int ble_hs_conn_init(void)
491 {
492     int rc;
493     rc = os_mempool_init(&ble_hs_conn_pool, MYNEWT_VAL(BLE_MAX_CONNECTIONS),
494                          sizeof(struct ble_hs_conn),
495                          ble_hs_conn_elem_mem, "ble_hs_conn_pool");
496     if (rc != 0) {
497         return BLE_HS_EOS;
498     }
499 
500     SLIST_INIT(&ble_hs_conns);
501     return 0;
502 }