1 /* ipm_stm32wb.c - HCI driver for stm32wb shared ram */
2
3 /*
4 * Copyright (c) 2019 Linaro Ltd.
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9
10 #include <init.h>
11 #include <sys/util.h>
12 #include <bluetooth/hci.h>
13 #include <drivers/bluetooth/hci_driver.h>
14 #include "bluetooth/addr.h"
15 #include <drivers/clock_control/stm32_clock_control.h>
16
17 #include "app_conf.h"
18 #include "stm32_wpan_common.h"
19 #include "shci.h"
20 #include "shci_tl.h"
21
22 #define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH * 4 * \
23 DIVC((sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE), 4))
24
25 /* Private variables ---------------------------------------------------------*/
26 PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer;
27 PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t EvtPool[POOL_SIZE];
28 PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t SystemCmdBuffer;
29 PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t
30 SystemSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
31 PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t
32 BleSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
33 PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t
34 HciAclDataBuffer[sizeof(TL_PacketHeader_t) + 5 + 251];
35
36 static void syscmd_status_not(SHCI_TL_CmdStatus_t status);
37 static void sysevt_received(void *pdata);
38
39 #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
40 #define LOG_MODULE_NAME hci_ipm
41 #include "common/log.h"
42
43 #define HCI_CMD 0x01
44 #define HCI_ACL 0x02
45 #define HCI_SCO 0x03
46 #define HCI_EVT 0x04
47
48 #define STM32WB_C2_LOCK_TIMEOUT K_MSEC(500)
49
50 static K_SEM_DEFINE(c2_started, 0, 1);
51 static K_SEM_DEFINE(ble_sys_wait_cmd_rsp, 0, 1);
52 static K_SEM_DEFINE(acl_data_ack, 1, 1);
53 static K_SEM_DEFINE(ipm_busy, 1, 1);
54
55 struct aci_set_tx_power {
56 uint8_t cmd;
57 uint8_t value[2];
58 };
59
60 struct aci_set_ble_addr {
61 uint8_t config_offset;
62 uint8_t length;
63 uint8_t value[6];
64 } __packed;
65
66 #define ACI_WRITE_SET_TX_POWER_LEVEL BT_OP(BT_OGF_VS, 0xFC0F)
67 #define ACI_HAL_WRITE_CONFIG_DATA BT_OP(BT_OGF_VS, 0xFC0C)
68
69 #define HCI_CONFIG_DATA_PUBADDR_OFFSET 0
70 #define HCI_CONFIG_DATA_RANDOM_ADDRESS_OFFSET 0x2E
71
72 static bt_addr_t bd_addr_udn;
73
74 /* Rx thread definitions */
75 K_FIFO_DEFINE(ipm_rx_events_fifo);
76 static K_KERNEL_STACK_DEFINE(ipm_rx_stack, CONFIG_BT_STM32_IPM_RX_STACK_SIZE);
77 static struct k_thread ipm_rx_thread_data;
78
stm32wb_start_ble(void)79 static void stm32wb_start_ble(void)
80 {
81 SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
82 { { 0, 0, 0 } }, /**< Header unused */
83 { 0, /** pBleBufferAddress not used */
84 0, /** BleBufferSize not used */
85 CFG_BLE_NUM_GATT_ATTRIBUTES,
86 CFG_BLE_NUM_GATT_SERVICES,
87 CFG_BLE_ATT_VALUE_ARRAY_SIZE,
88 CFG_BLE_NUM_LINK,
89 CFG_BLE_DATA_LENGTH_EXTENSION,
90 CFG_BLE_PREPARE_WRITE_LIST_SIZE,
91 CFG_BLE_MBLOCK_COUNT,
92 CFG_BLE_MAX_ATT_MTU,
93 CFG_BLE_SLAVE_SCA,
94 CFG_BLE_MASTER_SCA,
95 CFG_BLE_LSE_SOURCE,
96 CFG_BLE_MAX_CONN_EVENT_LENGTH,
97 CFG_BLE_HSE_STARTUP_TIME,
98 CFG_BLE_VITERBI_MODE,
99 CFG_BLE_OPTIONS,
100 0 }
101 };
102
103 /**
104 * Starts the BLE Stack on CPU2
105 */
106 SHCI_C2_BLE_Init(&ble_init_cmd_packet);
107 }
108
sysevt_received(void * pdata)109 static void sysevt_received(void *pdata)
110 {
111 k_sem_give(&c2_started);
112 }
113
syscmd_status_not(SHCI_TL_CmdStatus_t status)114 static void syscmd_status_not(SHCI_TL_CmdStatus_t status)
115 {
116 BT_DBG("status:%d", status);
117 }
118
119 /*
120 * https://github.com/zephyrproject-rtos/zephyr/issues/19509
121 * Tested on nucleo_wb55rg (stm32wb55rg) BLE stack (v1.2.0)
122 * Unresolved Resolvable Private Addresses (RPA)
123 * is reported in the peer_rpa field, and not in the peer address,
124 * as it should, when this happens the peer address is set to all FFs
125 * 0A 00 01 08 01 01 FF FF FF FF FF FF 00 00 00 00 00 00 0C AA C5 B3 3D 6B ...
126 * If such message is passed to HCI core than pairing will essentially fail.
127 * Solution: Rewrite the event with the RPA in the PEER address field
128 */
tryfix_event(TL_Evt_t * tev)129 static void tryfix_event(TL_Evt_t *tev)
130 {
131 struct bt_hci_evt_le_meta_event *mev = (void *)&tev->payload;
132
133 if (tev->evtcode != BT_HCI_EVT_LE_META_EVENT ||
134 mev->subevent != BT_HCI_EVT_LE_ENH_CONN_COMPLETE) {
135 return;
136 }
137
138 struct bt_hci_evt_le_enh_conn_complete *evt =
139 (void *)((uint8_t *)mev + (sizeof(*mev)));
140
141 if (!bt_addr_cmp(&evt->peer_addr.a, BT_ADDR_NONE)) {
142 BT_WARN("Invalid peer addr %s", bt_addr_le_str(&evt->peer_addr));
143 bt_addr_copy(&evt->peer_addr.a, &evt->peer_rpa);
144 evt->peer_addr.type = BT_ADDR_LE_RANDOM;
145 }
146 }
147
TM_EvtReceivedCb(TL_EvtPacket_t * hcievt)148 void TM_EvtReceivedCb(TL_EvtPacket_t *hcievt)
149 {
150 k_fifo_put(&ipm_rx_events_fifo, hcievt);
151 }
152
bt_ipm_rx_thread(void)153 static void bt_ipm_rx_thread(void)
154 {
155 while (true) {
156 bool discardable = false;
157 k_timeout_t timeout = K_FOREVER;
158 static TL_EvtPacket_t *hcievt;
159 struct net_buf *buf = NULL;
160 struct bt_hci_acl_hdr acl_hdr;
161 TL_AclDataSerial_t *acl;
162 struct bt_hci_evt_le_meta_event *mev;
163
164 hcievt = k_fifo_get(&ipm_rx_events_fifo, K_FOREVER);
165
166 k_sem_take(&ipm_busy, K_FOREVER);
167
168 switch (hcievt->evtserial.type) {
169 case HCI_EVT:
170 BT_DBG("EVT: hcievt->evtserial.evt.evtcode: 0x%02x",
171 hcievt->evtserial.evt.evtcode);
172 switch (hcievt->evtserial.evt.evtcode) {
173 case BT_HCI_EVT_VENDOR:
174 /* Vendor events are currently unsupported */
175 BT_ERR("Unknown evtcode type 0x%02x",
176 hcievt->evtserial.evt.evtcode);
177 TL_MM_EvtDone(hcievt);
178 goto end_loop;
179 default:
180 mev = (void *)&hcievt->evtserial.evt.payload;
181 if (hcievt->evtserial.evt.evtcode == BT_HCI_EVT_LE_META_EVENT &&
182 (mev->subevent == BT_HCI_EVT_LE_ADVERTISING_REPORT ||
183 mev->subevent == BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT)) {
184 discardable = true;
185 timeout = K_NO_WAIT;
186 }
187
188 buf = bt_buf_get_evt(
189 hcievt->evtserial.evt.evtcode,
190 discardable, timeout);
191 if (!buf) {
192 BT_DBG("Discard adv report due to insufficient buf");
193 goto end_loop;
194 }
195 }
196
197 tryfix_event(&hcievt->evtserial.evt);
198 net_buf_add_mem(buf, &hcievt->evtserial.evt,
199 hcievt->evtserial.evt.plen + 2);
200 break;
201 case HCI_ACL:
202 acl = &(((TL_AclDataPacket_t *)hcievt)->AclDataSerial);
203 buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER);
204 acl_hdr.handle = acl->handle;
205 acl_hdr.len = acl->length;
206 BT_DBG("ACL: handle %x, len %x",
207 acl_hdr.handle, acl_hdr.len);
208 net_buf_add_mem(buf, &acl_hdr, sizeof(acl_hdr));
209 net_buf_add_mem(buf, (uint8_t *)&acl->acl_data,
210 acl_hdr.len);
211 break;
212 default:
213 BT_ERR("Unknown BT buf type %d",
214 hcievt->evtserial.type);
215 TL_MM_EvtDone(hcievt);
216 goto end_loop;
217 }
218
219 TL_MM_EvtDone(hcievt);
220
221 bt_recv(buf);
222 end_loop:
223 k_sem_give(&ipm_busy);
224 }
225
226 }
227
TM_AclDataAck(void)228 static void TM_AclDataAck(void)
229 {
230 k_sem_give(&acl_data_ack);
231 }
232
shci_notify_asynch_evt(void * pdata)233 void shci_notify_asynch_evt(void *pdata)
234 {
235 shci_user_evt_proc();
236 }
237
shci_cmd_resp_release(uint32_t flag)238 void shci_cmd_resp_release(uint32_t flag)
239 {
240 k_sem_give(&ble_sys_wait_cmd_rsp);
241 }
242
shci_cmd_resp_wait(uint32_t timeout)243 void shci_cmd_resp_wait(uint32_t timeout)
244 {
245 k_sem_take(&ble_sys_wait_cmd_rsp, K_MSEC(timeout));
246 }
247
ipcc_reset(void)248 void ipcc_reset(void)
249 {
250 /* Reset IPCC */
251 LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC);
252
253 LL_C1_IPCC_ClearFlag_CHx(
254 IPCC,
255 LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 |
256 LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
257
258 LL_C2_IPCC_ClearFlag_CHx(
259 IPCC,
260 LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 |
261 LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
262
263 LL_C1_IPCC_DisableTransmitChannel(
264 IPCC,
265 LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 |
266 LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
267
268 LL_C2_IPCC_DisableTransmitChannel(
269 IPCC,
270 LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 |
271 LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
272
273 LL_C1_IPCC_DisableReceiveChannel(
274 IPCC,
275 LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 |
276 LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
277
278 LL_C2_IPCC_DisableReceiveChannel(
279 IPCC,
280 LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 |
281 LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
282
283 /* Set IPCC default IRQ handlers */
284 IRQ_CONNECT(IPCC_C1_RX_IRQn, 0, HW_IPCC_Rx_Handler, NULL, 0);
285 IRQ_CONNECT(IPCC_C1_TX_IRQn, 0, HW_IPCC_Tx_Handler, NULL, 0);
286 }
287
transport_init(void)288 void transport_init(void)
289 {
290 TL_MM_Config_t tl_mm_config;
291 TL_BLE_InitConf_t tl_ble_config;
292 SHCI_TL_HciInitConf_t shci_init_config;
293
294 BT_DBG("BleCmdBuffer: %p", (void *)&BleCmdBuffer);
295 BT_DBG("HciAclDataBuffer: %p", (void *)&HciAclDataBuffer);
296 BT_DBG("SystemCmdBuffer: %p", (void *)&SystemCmdBuffer);
297 BT_DBG("EvtPool: %p", (void *)&EvtPool);
298 BT_DBG("SystemSpareEvtBuffer: %p", (void *)&SystemSpareEvtBuffer);
299 BT_DBG("BleSpareEvtBuffer: %p", (void *)&BleSpareEvtBuffer);
300
301 /**< Reference table initialization */
302 TL_Init();
303
304 /**< System channel initialization */
305 shci_init_config.p_cmdbuffer = (uint8_t *)&SystemCmdBuffer;
306 shci_init_config.StatusNotCallBack = syscmd_status_not;
307 shci_init(sysevt_received, (void *) &shci_init_config);
308
309 /**< Memory Manager channel initialization */
310 tl_mm_config.p_BleSpareEvtBuffer = BleSpareEvtBuffer;
311 tl_mm_config.p_SystemSpareEvtBuffer = SystemSpareEvtBuffer;
312 tl_mm_config.p_AsynchEvtPool = EvtPool;
313 tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
314 TL_MM_Init(&tl_mm_config);
315
316 /**< BLE channel initialization */
317 tl_ble_config.p_cmdbuffer = (uint8_t *)&BleCmdBuffer;
318 tl_ble_config.p_AclDataBuffer = HciAclDataBuffer;
319 tl_ble_config.IoBusEvtCallBack = TM_EvtReceivedCb;
320 tl_ble_config.IoBusAclDataTxAck = TM_AclDataAck;
321 TL_BLE_Init((void *)&tl_ble_config);
322
323 TL_Enable();
324 }
325
bt_ipm_send(struct net_buf * buf)326 static int bt_ipm_send(struct net_buf *buf)
327 {
328 TL_CmdPacket_t *ble_cmd_buff = &BleCmdBuffer;
329
330 k_sem_take(&ipm_busy, K_FOREVER);
331
332 switch (bt_buf_get_type(buf)) {
333 case BT_BUF_ACL_OUT:
334 BT_DBG("ACL: buf %p type %u len %u", buf, bt_buf_get_type(buf),
335 buf->len);
336 k_sem_take(&acl_data_ack, K_FOREVER);
337 net_buf_push_u8(buf, HCI_ACL);
338 memcpy((void *)
339 &((TL_AclDataPacket_t *)HciAclDataBuffer)->AclDataSerial,
340 buf->data, buf->len);
341 TL_BLE_SendAclData(NULL, 0);
342 break;
343 case BT_BUF_CMD:
344 BT_DBG("CMD: buf %p type %u len %u", buf, bt_buf_get_type(buf),
345 buf->len);
346 ble_cmd_buff->cmdserial.type = HCI_CMD;
347 ble_cmd_buff->cmdserial.cmd.plen = buf->len;
348 memcpy((void *)&ble_cmd_buff->cmdserial.cmd, buf->data,
349 buf->len);
350 TL_BLE_SendCmd(NULL, 0);
351 break;
352 default:
353 k_sem_give(&ipm_busy);
354 BT_ERR("Unsupported type");
355 return -EINVAL;
356 }
357
358 k_sem_give(&ipm_busy);
359
360 net_buf_unref(buf);
361
362 return 0;
363 }
364
start_ble_rf(void)365 static void start_ble_rf(void)
366 {
367 if ((LL_RCC_IsActiveFlag_PINRST()) && (!LL_RCC_IsActiveFlag_SFTRST())) {
368 /* Simulate power off reset */
369 LL_PWR_EnableBkUpAccess();
370 LL_PWR_EnableBkUpAccess();
371 LL_RCC_ForceBackupDomainReset();
372 LL_RCC_ReleaseBackupDomainReset();
373 }
374
375 #if STM32_LSE_CLOCK
376 /* Select LSE clock */
377 LL_RCC_LSE_Enable();
378 while (!LL_RCC_LSE_IsReady()) {
379 }
380
381 /* Select wakeup source of BLE RF */
382 LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE);
383 LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
384
385 /* Switch OFF LSI */
386 LL_RCC_LSI2_Disable();
387 #else
388 LL_RCC_LSI2_Enable();
389 while (!LL_RCC_LSI2_IsReady()) {
390 }
391
392 /* Select wakeup source of BLE RF */
393 LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSI);
394 LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI);
395 #endif
396
397 /* Set RNG on HSI48 */
398 LL_RCC_HSI48_Enable();
399 while (!LL_RCC_HSI48_IsReady()) {
400 }
401
402 LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_HSI48);
403 }
404
bt_get_ble_addr(void)405 bt_addr_t *bt_get_ble_addr(void)
406 {
407 bt_addr_t *bd_addr;
408 uint32_t udn;
409 uint32_t company_id;
410 uint32_t device_id;
411
412 /* Get the 64 bit Unique Device Number UID */
413 /* The UID is used by firmware to derive */
414 /* 48-bit Device Address EUI-48 */
415 udn = LL_FLASH_GetUDN();
416
417 if (udn != 0xFFFFFFFF) {
418 /* Get the ST Company ID */
419 company_id = LL_FLASH_GetSTCompanyID();
420 /* Get the STM32 Device ID */
421 device_id = LL_FLASH_GetDeviceID();
422 bd_addr_udn.val[0] = (uint8_t)(udn & 0x000000FF);
423 bd_addr_udn.val[1] = (uint8_t)((udn & 0x0000FF00) >> 8);
424 bd_addr_udn.val[2] = (uint8_t)((udn & 0x00FF0000) >> 16);
425 bd_addr_udn.val[3] = (uint8_t)device_id;
426 bd_addr_udn.val[4] = (uint8_t)(company_id & 0x000000FF);
427 bd_addr_udn.val[5] = (uint8_t)((company_id & 0x0000FF00) >> 8);
428 bd_addr = &bd_addr_udn;
429 } else {
430 bd_addr = NULL;
431 }
432
433 return bd_addr;
434 }
435
bt_ipm_set_addr(void)436 static int bt_ipm_set_addr(void)
437 {
438 bt_addr_t *uid_addr;
439 struct aci_set_ble_addr *param;
440 struct net_buf *buf, *rsp;
441 int err;
442
443 uid_addr = bt_get_ble_addr();
444 if (!uid_addr) {
445 return -ENOMSG;
446 }
447
448 buf = bt_hci_cmd_create(ACI_HAL_WRITE_CONFIG_DATA, sizeof(*param));
449
450 if (!buf) {
451 return -ENOBUFS;
452 }
453
454 param = net_buf_add(buf, sizeof(*param));
455 param->config_offset = HCI_CONFIG_DATA_PUBADDR_OFFSET;
456 param->length = 6;
457 param->value[0] = uid_addr->val[0];
458 param->value[1] = uid_addr->val[1];
459 param->value[2] = uid_addr->val[2];
460 param->value[3] = uid_addr->val[3];
461 param->value[4] = uid_addr->val[4];
462 param->value[5] = uid_addr->val[5];
463
464 err = bt_hci_cmd_send_sync(ACI_HAL_WRITE_CONFIG_DATA, buf, &rsp);
465 if (err) {
466 return err;
467 }
468 net_buf_unref(rsp);
469 return 0;
470 }
471
bt_ipm_ble_init(void)472 static int bt_ipm_ble_init(void)
473 {
474 struct aci_set_tx_power *param;
475 struct net_buf *buf, *rsp;
476 int err;
477
478 /* Send HCI_RESET */
479 err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp);
480 if (err) {
481 return err;
482 }
483 /* TDB: Something to do on reset complete? */
484 net_buf_unref(rsp);
485 err = bt_ipm_set_addr();
486 if (err) {
487 BT_ERR("Can't set BLE UID addr");
488 }
489 /* Send ACI_WRITE_SET_TX_POWER_LEVEL */
490 buf = bt_hci_cmd_create(ACI_WRITE_SET_TX_POWER_LEVEL, 3);
491 if (!buf) {
492 return -ENOBUFS;
493 }
494 param = net_buf_add(buf, sizeof(*param));
495 param->cmd = 0x0F;
496 param->value[0] = 0x18;
497 param->value[1] = 0x01;
498
499 err = bt_hci_cmd_send_sync(ACI_WRITE_SET_TX_POWER_LEVEL, buf, &rsp);
500 if (err) {
501 return err;
502 }
503 net_buf_unref(rsp);
504
505 return 0;
506 }
507
bt_ipm_open(void)508 static int bt_ipm_open(void)
509 {
510 int err;
511
512 err = bt_ipm_ble_init();
513 if (err) {
514 return err;
515 }
516
517 BT_DBG("IPM Channel Open Completed");
518
519 return 0;
520 }
521
522 static const struct bt_hci_driver drv = {
523 .name = "BT IPM",
524 .bus = BT_HCI_DRIVER_BUS_IPM,
525 .quirks = BT_QUIRK_NO_RESET,
526 .open = bt_ipm_open,
527 .send = bt_ipm_send,
528 };
529
_bt_ipm_init(const struct device * unused)530 static int _bt_ipm_init(const struct device *unused)
531 {
532 ARG_UNUSED(unused);
533
534 bt_hci_driver_register(&drv);
535
536 start_ble_rf();
537
538 /* Start RX thread */
539 k_thread_create(&ipm_rx_thread_data, ipm_rx_stack,
540 K_KERNEL_STACK_SIZEOF(ipm_rx_stack),
541 (k_thread_entry_t)bt_ipm_rx_thread, NULL, NULL, NULL,
542 K_PRIO_COOP(CONFIG_BT_DRIVER_RX_HIGH_PRIO),
543 0, K_NO_WAIT);
544
545 /* Take BLE out of reset */
546 ipcc_reset();
547
548 transport_init();
549
550 /* Device will let us know when it's ready */
551 if (k_sem_take(&c2_started, STM32WB_C2_LOCK_TIMEOUT)) {
552 return -ETIMEDOUT;
553 }
554 BT_DBG("C2 unlocked");
555
556 stm32wb_start_ble();
557
558 return 0;
559 }
560
561 SYS_INIT(_bt_ipm_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
562