• 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 #include <stdint.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include "securec.h"
24 #include "os/os.h"
25 #include "mem/mem.h"
26 #include "nimble/ble_hci_trans.h"
27 #include "host/ble_monitor.h"
28 #include "ble_hs_priv.h"
29 #include "ble_monitor_priv.h"
30 
31 #define BLE_HCI_CMD_TIMEOUT_MS  2000
32 
33 static struct ble_npl_mutex ble_hs_hci_mutex;
34 static struct ble_npl_sem ble_hs_hci_sem;
35 
36 static struct ble_hci_ev *ble_hs_hci_ack;
37 static uint16_t ble_hs_hci_buf_sz;
38 static uint8_t ble_hs_hci_max_pkts;
39 
40 /* For now 32-bits of features is enough */
41 static uint32_t ble_hs_hci_sup_feat;
42 
43 static uint8_t ble_hs_hci_version;
44 
45 #define BLE_HS_HCI_FRAG_DATABUF_SIZE    \
46     (BLE_ACL_MAX_PKT_SIZE +             \
47      BLE_HCI_DATA_HDR_SZ +              \
48      sizeof (struct os_mbuf_pkthdr) +   \
49      sizeof (struct os_mbuf))
50 
51 #define BLE_HS_HCI_FRAG_MEMBLOCK_SIZE   \
52     (OS_ALIGN(BLE_HS_HCI_FRAG_DATABUF_SIZE, 4))
53 
54 #define BLE_HS_HCI_FRAG_MEMPOOL_SIZE    \
55     OS_MEMPOOL_SIZE(1, BLE_HS_HCI_FRAG_MEMBLOCK_SIZE)
56 
57 /**
58  *  A one-element mbuf pool dedicated to holding outgoing ACL data packets.
59  *  This dedicated pool prevents a deadlock caused by mbuf exhaustion.  Without
60  *  this pool, all msys mbufs could be permanently allocated, preventing us
61  *  from fragmenting outgoing packets and sending them (and ultimately freeing
62  *  them).
63  */
64 static os_membuf_t ble_hs_hci_frag_data[BLE_HS_HCI_FRAG_MEMPOOL_SIZE];
65 static struct os_mbuf_pool ble_hs_hci_frag_mbuf_pool;
66 static struct os_mempool ble_hs_hci_frag_mempool;
67 
68 /**
69  * The number of available ACL transmit buffers on the controller.  This
70  * variable must only be accessed while the host mutex is locked.
71  */
72 uint16_t ble_hs_hci_avail_pkts;
73 
74 #if MYNEWT_VAL(BLE_HS_PHONY_HCI_ACKS)
75 static ble_hs_hci_phony_ack_fn *ble_hs_hci_phony_ack_cb;
76 #endif
77 
78 #if MYNEWT_VAL(BLE_HS_PHONY_HCI_ACKS)
ble_hs_hci_set_phony_ack_cb(ble_hs_hci_phony_ack_fn * cb)79 void ble_hs_hci_set_phony_ack_cb(ble_hs_hci_phony_ack_fn *cb)
80 {
81     ble_hs_hci_phony_ack_cb = cb;
82 }
83 #endif
84 
ble_hs_hci_lock(void)85 static void ble_hs_hci_lock(void)
86 {
87     int rc;
88     rc = ble_npl_mutex_pend(&ble_hs_hci_mutex, BLE_NPL_TIME_FOREVER);
89     BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED);
90 }
91 
ble_hs_hci_unlock(void)92 static void ble_hs_hci_unlock(void)
93 {
94     int rc;
95     rc = ble_npl_mutex_release(&ble_hs_hci_mutex);
96     BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED);
97 }
98 
ble_hs_hci_set_buf_sz(uint16_t pktlen,uint16_t max_pkts)99 int ble_hs_hci_set_buf_sz(uint16_t pktlen, uint16_t max_pkts)
100 {
101     if (pktlen == 0 || max_pkts == 0) {
102         return BLE_HS_EINVAL;
103     }
104 
105     ble_hs_hci_buf_sz = pktlen;
106     ble_hs_hci_max_pkts = max_pkts;
107     ble_hs_hci_avail_pkts = max_pkts;
108     return 0;
109 }
110 
ble_hs_hci_set_tx_buf_sz(uint16_t pktlen)111 int ble_hs_hci_set_tx_buf_sz(uint16_t pktlen)
112 {
113     if (pktlen > BLE_HCI_SET_DATALEN_TX_OCTETS_MAX || pktlen < BLE_HCI_SET_DATALEN_TX_OCTETS_MIN) {
114         return BLE_HS_EINVAL;
115     }
116 
117     ble_hs_hci_buf_sz = pktlen;
118     return 0;
119 }
120 
121 /**
122  * Increases the count of available controller ACL buffers.
123  */
ble_hs_hci_add_avail_pkts(uint16_t delta)124 void ble_hs_hci_add_avail_pkts(uint16_t delta)
125 {
126     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
127 
128     if (ble_hs_hci_avail_pkts + delta > UINT16_MAX) {
129         ble_hs_sched_reset(BLE_HS_ECONTROLLER);
130     } else {
131         ble_hs_hci_avail_pkts += delta;
132     }
133 }
134 
ble_hs_hci_rx_cmd_complete(const void * data,int len,struct ble_hs_hci_ack * out_ack)135 static int ble_hs_hci_rx_cmd_complete(const void *data, int len, struct ble_hs_hci_ack *out_ack)
136 {
137     const struct ble_hci_ev_command_complete *ev = data;
138     const struct ble_hci_ev_command_complete_nop *nop = data;
139     uint16_t opcode;
140 
141     if (len < sizeof(*ev)) {
142         if (len < sizeof(*nop)) {
143             return BLE_HS_ECONTROLLER;
144         }
145 
146         /* nop is special as it doesn't have status and response */
147         opcode = le16toh(nop->opcode);
148         if (opcode != BLE_HCI_OPCODE_NOP) {
149             return BLE_HS_ECONTROLLER;
150         }
151 
152         /* Process num_pkts field. */
153         out_ack->bha_status = 0;
154         out_ack->bha_params = NULL;
155         out_ack->bha_params_len = 0;
156         return 0;
157     }
158 
159     opcode = le16toh(ev->opcode);
160     /* Process num_pkts field. */
161     out_ack->bha_opcode = opcode;
162     out_ack->bha_status = BLE_HS_HCI_ERR(ev->status);
163     out_ack->bha_params_len = len - sizeof(*ev);
164 
165     if (out_ack->bha_params_len) {
166         out_ack->bha_params = ev->return_params;
167     } else {
168         out_ack->bha_params = NULL;
169     }
170 
171     return 0;
172 }
173 
ble_hs_hci_rx_cmd_status(const void * data,int len,struct ble_hs_hci_ack * out_ack)174 static int ble_hs_hci_rx_cmd_status(const void *data, int len, struct ble_hs_hci_ack *out_ack)
175 {
176     const struct ble_hci_ev_command_status *ev = data;
177 
178     if (len != sizeof(*ev)) {
179         return BLE_HS_ECONTROLLER;
180     }
181 
182     /* XXX: Process num_pkts field. */
183     out_ack->bha_opcode = le16toh(ev->opcode);
184     out_ack->bha_params = NULL;
185     out_ack->bha_params_len = 0;
186     out_ack->bha_status = BLE_HS_HCI_ERR(ev->status);
187     return 0;
188 }
189 
ble_hs_hci_process_ack(uint16_t expected_opcode,uint8_t * params_buf,uint8_t params_buf_len,struct ble_hs_hci_ack * out_ack)190 static int ble_hs_hci_process_ack(uint16_t expected_opcode,
191                                   uint8_t *params_buf, uint8_t params_buf_len,
192                                   struct ble_hs_hci_ack *out_ack)
193 {
194     int rc;
195     BLE_HS_DBG_ASSERT(ble_hs_hci_ack != NULL);
196     /* Count events received */
197     STATS_INC(ble_hs_stats, hci_event);
198     /* Clear ack fields up front to silence spurious gcc warnings. */
199     memset_s(out_ack, sizeof * out_ack, 0, sizeof * out_ack);
200 
201     switch (ble_hs_hci_ack->opcode) {
202         case BLE_HCI_EVCODE_COMMAND_COMPLETE:
203             rc = ble_hs_hci_rx_cmd_complete(ble_hs_hci_ack->data,
204                                             ble_hs_hci_ack->length, out_ack);
205             break;
206 
207         case BLE_HCI_EVCODE_COMMAND_STATUS:
208             rc = ble_hs_hci_rx_cmd_status(ble_hs_hci_ack->data,
209                                           ble_hs_hci_ack->length, out_ack);
210             break;
211 
212         default:
213             BLE_HS_DBG_ASSERT(0);
214             rc = BLE_HS_EUNKNOWN;
215             break;
216     }
217 
218     if (rc == 0) {
219         if (params_buf == NULL || out_ack->bha_params == NULL) {
220             out_ack->bha_params_len = 0;
221         } else {
222             if (out_ack->bha_params_len > params_buf_len) {
223                 out_ack->bha_params_len = params_buf_len;
224                 rc = BLE_HS_ECONTROLLER;
225             }
226 
227             memcpy_s(params_buf, sizeof(params_buf), out_ack->bha_params, out_ack->bha_params_len);
228         }
229 
230         out_ack->bha_params = params_buf;
231 
232         if (out_ack->bha_opcode != expected_opcode) {
233             rc = BLE_HS_ECONTROLLER;
234         }
235     }
236 
237     if (rc != 0) {
238         STATS_INC(ble_hs_stats, hci_invalid_ack);
239     }
240 
241     return rc;
242 }
243 
ble_hs_hci_wait_for_ack(void)244 static int ble_hs_hci_wait_for_ack(void)
245 {
246     int rc;
247 #if MYNEWT_VAL(BLE_HS_PHONY_HCI_ACKS)
248 
249     if (ble_hs_hci_phony_ack_cb == NULL) {
250         rc = BLE_HS_ETIMEOUT_HCI;
251     } else {
252         ble_hs_hci_ack = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD);
253         BLE_HS_DBG_ASSERT(ble_hs_hci_ack != NULL);
254         rc = ble_hs_hci_phony_ack_cb((void *)ble_hs_hci_ack, 260); // 260:byte alignment
255     }
256 
257 #else
258     rc = ble_npl_sem_pend(&ble_hs_hci_sem,
259                           ble_npl_time_ms_to_ticks32(BLE_HCI_CMD_TIMEOUT_MS));
260     switch (rc) {
261         case 0:
262             BLE_HS_DBG_ASSERT(ble_hs_hci_ack != NULL);
263 #if BLE_MONITOR
264             ble_monitor_send(BLE_MONITOR_OPCODE_EVENT_PKT, (void *) ble_hs_hci_ack,
265                              sizeof(*ble_hs_hci_ack) + ble_hs_hci_ack->length);
266 #endif
267             break;
268 
269         case OS_TIMEOUT:
270             rc = BLE_HS_ETIMEOUT_HCI;
271             STATS_INC(ble_hs_stats, hci_timeout);
272             break;
273 
274         default:
275             rc = BLE_HS_EOS;
276             break;
277     }
278 
279 #endif
280     return rc;
281 }
282 
ble_hs_hci_cmd_tx(uint16_t opcode,const void * cmd,uint8_t cmd_len,void * rsp,uint8_t rsp_len)283 int ble_hs_hci_cmd_tx(uint16_t opcode, const void *cmd, uint8_t cmd_len,
284                       void *rsp, uint8_t rsp_len)
285 {
286     struct ble_hs_hci_ack ack;
287     int rc;
288     BLE_HS_DBG_ASSERT(ble_hs_hci_ack == NULL);
289     ble_hs_hci_lock();
290     rc = ble_hs_hci_cmd_send_buf(opcode, cmd, cmd_len);
291     if (rc != 0) {
292         goto done;
293     }
294 
295     rc = ble_hs_hci_wait_for_ack();
296     if (rc != 0) {
297         ble_hs_sched_reset(rc);
298         goto done;
299     }
300 
301     rc = ble_hs_hci_process_ack(opcode, rsp, rsp_len, &ack);
302     if (rc != 0) {
303         ble_hs_sched_reset(rc);
304         goto done;
305     }
306 
307     rc = ack.bha_status;
308     /* on success we should always get full response */
309     if (!rc && (ack.bha_params_len != rsp_len)) {
310         ble_hs_sched_reset(rc);
311         goto done;
312     }
313 
314 done:
315 
316     if (ble_hs_hci_ack != NULL) {
317         ble_hci_trans_buf_free((uint8_t *) ble_hs_hci_ack);
318         ble_hs_hci_ack = NULL;
319     }
320 
321     ble_hs_hci_unlock();
322     return rc;
323 }
324 
ble_hs_hci_rx_ack(uint8_t * ack_ev)325 static void ble_hs_hci_rx_ack(uint8_t *ack_ev)
326 {
327     if (ble_npl_sem_get_count(&ble_hs_hci_sem) > 0) {
328         /* This ack is unexpected; ignore it. */
329         ble_hci_trans_buf_free(ack_ev);
330         return;
331     }
332 
333     BLE_HS_DBG_ASSERT(ble_hs_hci_ack == NULL);
334     /* Unblock the application now that the HCI command buffer is populated
335      * with the acknowledgement.
336      */
337     ble_hs_hci_ack = (struct ble_hci_ev *) ack_ev;
338     ble_npl_sem_release(&ble_hs_hci_sem);
339 }
340 
ble_hs_hci_rx_evt(uint8_t * hci_ev,void * arg)341 int ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg)
342 {
343     struct ble_hci_ev *ev = (void *) hci_ev;
344     struct ble_hci_ev_command_complete *cmd_complete = (void *) ev->data;
345     struct ble_hci_ev_command_status *cmd_status = (void *) ev->data;
346     int enqueue;
347     BLE_HS_DBG_ASSERT(hci_ev != NULL);
348 
349     switch (ev->opcode) {
350         case BLE_HCI_EVCODE_COMMAND_COMPLETE:
351             enqueue = (cmd_complete->opcode == BLE_HCI_OPCODE_NOP);
352             break;
353 
354         case BLE_HCI_EVCODE_COMMAND_STATUS:
355             enqueue = (cmd_status->opcode == BLE_HCI_OPCODE_NOP);
356             break;
357 
358         default:
359             enqueue = 1;
360             break;
361     }
362 
363     if (enqueue) {
364         ble_hs_enqueue_hci_event(hci_ev);
365     } else {
366         ble_hs_hci_rx_ack(hci_ev);
367     }
368 
369     return 0;
370 }
371 
372 /**
373  * Calculates the largest ACL payload that the controller can accept.
374  */
ble_hs_hci_max_acl_payload_sz(struct ble_hs_conn * conn)375 static uint16_t ble_hs_hci_max_acl_payload_sz(struct ble_hs_conn *conn)
376 {
377     /* per connection, use their own mtu is reasonable */
378     return conn->tx_ll_mtu;
379 
380     /* As per BLE 5.1 Standard, Vol. 2, Part E, section 7.8.2:
381     * The LE_Read_Buffer_Size command is used to read the maximum size of the
382     * data portion of HCI LE ACL Data Packets sent from the Host to the
383     * Controller.
384     */
385     if (conn->supported_feat & BLE_HS_HCI_LE_FEAT_DATA_PACKET_LENGTH_EXT) {
386         return ble_hs_hci_buf_sz;
387     } else {
388         return 27; // 27:byte alignment
389     }
390 }
391 
392 /**
393  * Allocates an mbuf to contain an outgoing ACL data fragment.
394  */
ble_hs_hci_frag_alloc(uint16_t frag_size,void * arg)395 static struct os_mbuf *ble_hs_hci_frag_alloc(uint16_t frag_size, void *arg)
396 {
397     struct os_mbuf *om;
398     /* Prefer the dedicated one-element fragment pool. */
399     om = os_mbuf_get_pkthdr(&ble_hs_hci_frag_mbuf_pool, 0);
400     if (om != NULL) {
401         om->om_data += BLE_HCI_DATA_HDR_SZ;
402         return om;
403     }
404 
405     /* Otherwise, fall back to msys. */
406     om = ble_hs_mbuf_acl_pkt();
407     if (om != NULL) {
408         return om;
409     }
410 
411     return NULL;
412 }
413 
414 /**
415  * Retrieves the total capacity of the ACL fragment pool (always 1).
416  */
ble_hs_hci_frag_num_mbufs(void)417 int ble_hs_hci_frag_num_mbufs(void)
418 {
419     return ble_hs_hci_frag_mempool.mp_num_blocks;
420 }
421 
422 /**
423  * Retrieves the the count of free buffers in the ACL fragment pool.
424  */
ble_hs_hci_frag_num_mbufs_free(void)425 int ble_hs_hci_frag_num_mbufs_free(void)
426 {
427     return ble_hs_hci_frag_mempool.mp_num_free;
428 }
429 
ble_hs_hci_acl_hdr_prepend(struct os_mbuf * om,uint16_t handle,uint8_t pb_flag)430 static struct os_mbuf *ble_hs_hci_acl_hdr_prepend(struct os_mbuf *om, uint16_t handle, uint8_t pb_flag)
431 {
432     struct os_mbuf *om_tmp = om;
433     struct hci_data_hdr hci_hdr;
434     struct os_mbuf *om2;
435     hci_hdr.hdh_handle_pb_bc = ble_hs_hci_util_handle_pb_bc_join(handle, pb_flag, 0);
436     put_le16(&hci_hdr.hdh_len, OS_MBUF_PKTHDR(om_tmp)->omp_len);
437     om2 = os_mbuf_prepend(om_tmp, sizeof hci_hdr);
438     if (om2 == NULL) {
439         return NULL;
440     }
441 
442     om_tmp = om2;
443     om_tmp = os_mbuf_pullup(om_tmp, sizeof hci_hdr);
444     if (om_tmp == NULL) {
445         return NULL;
446     }
447 
448     memcpy_s(om_tmp->om_data, sizeof(om_tmp->om_data), &hci_hdr, sizeof hci_hdr);
449 #if !BLE_MONITOR
450     BLE_HS_LOG(DEBUG, "host tx hci data; handle=%d length=%d\r\n", handle,
451                get_le16(&hci_hdr.hdh_len));
452 #endif
453     return om_tmp;
454 }
455 
ble_hs_hci_acl_tx_now(struct ble_hs_conn * conn,struct os_mbuf ** om)456 int ble_hs_hci_acl_tx_now(struct ble_hs_conn *conn, struct os_mbuf **om)
457 {
458     struct os_mbuf *txom;
459     struct os_mbuf *frag;
460     uint8_t pb;
461     int rc;
462     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
463     txom = *om;
464     *om = NULL;
465 
466     if (!(conn->bhc_flags & BLE_HS_CONN_F_TX_FRAG)) {
467         /* The first fragment uses the first-non-flush packet boundary value.
468          * After sending the first fragment, pb gets set appropriately for all
469          * subsequent fragments in this packet.
470          */
471         pb = BLE_HCI_PB_FIRST_NON_FLUSH;
472     } else {
473         pb = BLE_HCI_PB_MIDDLE;
474     }
475 
476     /* Send fragments until the entire packet has been sent. */
477     while (txom != NULL && ble_hs_hci_avail_pkts > 0) {
478         frag = mem_split_frag(&txom, ble_hs_hci_max_acl_payload_sz(conn),
479                               ble_hs_hci_frag_alloc, NULL);
480         if (frag == NULL) {
481             *om = txom;
482             return BLE_HS_EAGAIN;
483         }
484 
485         frag = ble_hs_hci_acl_hdr_prepend(frag, conn->bhc_handle, pb);
486         if (frag == NULL) {
487             rc = BLE_HS_ENOMEM;
488             goto err;
489         }
490 
491 #if !BLE_MONITOR
492         BLE_HS_LOG(DEBUG, "ble_hs_hci_acl_tx(): ");
493         ble_hs_log_mbuf(frag);
494         BLE_HS_LOG(DEBUG, "\r\n");
495 #endif
496         rc = ble_hs_tx_data(frag);
497         if (rc != 0) {
498             goto err;
499         }
500 
501         /* If any fragments remain, they should be marked as 'middle'
502          * fragments.
503          */
504         conn->bhc_flags |= BLE_HS_CONN_F_TX_FRAG;
505         pb = BLE_HCI_PB_MIDDLE;
506         /* Account for the controller buf that will hold the txed fragment. */
507         conn->bhc_outstanding_pkts++;
508         ble_hs_hci_avail_pkts--;
509     }
510 
511     if (txom != NULL) {
512         /* The controller couldn't accommodate some or all of the packet. */
513         *om = txom;
514         return BLE_HS_EAGAIN;
515     }
516 
517     /* The entire packet was transmitted. */
518     conn->bhc_flags &= ~BLE_HS_CONN_F_TX_FRAG;
519     return 0;
520 err:
521     BLE_HS_DBG_ASSERT(rc != 0);
522     conn->bhc_flags &= ~BLE_HS_CONN_F_TX_FRAG;
523     os_mbuf_free_chain(txom);
524     return rc;
525 }
526 
527 /**
528  * Transmits an HCI ACL data packet.  This function consumes the supplied mbuf,
529  * regardless of the outcome.
530  *
531  * @return                      0 on success;
532  *                              BLE_HS_EAGAIN if the packet could not be sent
533  *                                  in its entirety due to controller buffer
534  *                                  exhaustion.  The unsent data is pointed to
535  *                                  by the `om` parameter.
536  *                              A BLE host core return code on unexpected
537  *                                  error.
538  *
539  */
ble_hs_hci_acl_tx(struct ble_hs_conn * conn,struct os_mbuf ** om)540 int ble_hs_hci_acl_tx(struct ble_hs_conn *conn, struct os_mbuf **om)
541 {
542     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
543 
544     /* If this conn is already backed up, don't even try to send. */
545     if (STAILQ_FIRST(&conn->bhc_tx_q) != NULL) {
546         return BLE_HS_EAGAIN;
547     }
548 
549     return ble_hs_hci_acl_tx_now(conn, om);
550 }
551 
ble_hs_hci_set_le_supported_feat(uint32_t feat)552 void ble_hs_hci_set_le_supported_feat(uint32_t feat)
553 {
554     ble_hs_hci_sup_feat = feat;
555 }
556 
ble_hs_hci_get_le_supported_feat(void)557 uint32_t ble_hs_hci_get_le_supported_feat(void)
558 {
559     return ble_hs_hci_sup_feat;
560 }
561 
ble_hs_hci_set_hci_version(uint8_t hci_version)562 void ble_hs_hci_set_hci_version(uint8_t hci_version)
563 {
564     ble_hs_hci_version = hci_version;
565 }
566 
ble_hs_hci_get_hci_version(void)567 uint8_t ble_hs_hci_get_hci_version(void)
568 {
569     return ble_hs_hci_version;
570 }
571 
ble_hs_hci_init(void)572 void ble_hs_hci_init(void)
573 {
574     int rc;
575     rc = ble_npl_sem_init(&ble_hs_hci_sem, 0);
576     BLE_HS_DBG_ASSERT_EVAL(rc == 0);
577     rc = ble_npl_mutex_init(&ble_hs_hci_mutex);
578     BLE_HS_DBG_ASSERT_EVAL(rc == 0);
579     rc = mem_init_mbuf_pool(ble_hs_hci_frag_data,
580                             &ble_hs_hci_frag_mempool,
581                             &ble_hs_hci_frag_mbuf_pool,
582                             1,
583                             BLE_HS_HCI_FRAG_MEMBLOCK_SIZE,
584                             "ble_hs_hci_frag");
585     BLE_HS_DBG_ASSERT_EVAL(rc == 0);
586 }
ble_hs_hci_deinit(void)587 void ble_hs_hci_deinit(void)
588 {
589     ble_npl_mutex_deinit(&ble_hs_hci_mutex);
590     ble_npl_sem_deinit(&ble_hs_hci_sem);
591 }