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 }