| /* ipm_stm32wb.c - HCI driver for stm32wb shared ram */ |
| |
| /* |
| * Copyright (c) 2019 Linaro Ltd. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| |
| #include <zephyr/init.h> |
| #include <zephyr/sys/util.h> |
| #include <zephyr/bluetooth/hci.h> |
| #include <zephyr/drivers/bluetooth/hci_driver.h> |
| #include "bluetooth/addr.h" |
| #include <zephyr/drivers/clock_control/stm32_clock_control.h> |
| |
| #include "app_conf.h" |
| #include "stm32_wpan_common.h" |
| #include "shci.h" |
| #include "shci_tl.h" |
| |
| #define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH * 4 * \ |
| DIVC((sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE), 4)) |
| |
| /* Private variables ---------------------------------------------------------*/ |
| PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; |
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t EvtPool[POOL_SIZE]; |
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t SystemCmdBuffer; |
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t |
| SystemSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; |
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t |
| BleSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255]; |
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t |
| HciAclDataBuffer[sizeof(TL_PacketHeader_t) + 5 + 251]; |
| |
| static void syscmd_status_not(SHCI_TL_CmdStatus_t status); |
| static void sysevt_received(void *pdata); |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) |
| #define LOG_MODULE_NAME hci_ipm |
| #include "common/log.h" |
| |
| #define HCI_CMD 0x01 |
| #define HCI_ACL 0x02 |
| #define HCI_SCO 0x03 |
| #define HCI_EVT 0x04 |
| |
| #define STM32WB_C2_LOCK_TIMEOUT K_MSEC(500) |
| |
| static K_SEM_DEFINE(c2_started, 0, 1); |
| static K_SEM_DEFINE(ble_sys_wait_cmd_rsp, 0, 1); |
| static K_SEM_DEFINE(acl_data_ack, 1, 1); |
| static K_SEM_DEFINE(ipm_busy, 1, 1); |
| |
| struct aci_set_tx_power { |
| uint8_t cmd; |
| uint8_t value[2]; |
| }; |
| |
| struct aci_set_ble_addr { |
| uint8_t config_offset; |
| uint8_t length; |
| uint8_t value[6]; |
| } __packed; |
| |
| #define ACI_WRITE_SET_TX_POWER_LEVEL BT_OP(BT_OGF_VS, 0xFC0F) |
| #define ACI_HAL_WRITE_CONFIG_DATA BT_OP(BT_OGF_VS, 0xFC0C) |
| #define ACI_HAL_STACK_RESET BT_OP(BT_OGF_VS, 0xFC3B) |
| |
| #define HCI_CONFIG_DATA_PUBADDR_OFFSET 0 |
| #define HCI_CONFIG_DATA_RANDOM_ADDRESS_OFFSET 0x2E |
| |
| static bt_addr_t bd_addr_udn; |
| |
| /* Rx thread definitions */ |
| K_FIFO_DEFINE(ipm_rx_events_fifo); |
| static K_KERNEL_STACK_DEFINE(ipm_rx_stack, CONFIG_BT_STM32_IPM_RX_STACK_SIZE); |
| static struct k_thread ipm_rx_thread_data; |
| |
| static bool c2_started_flag; |
| |
| static void stm32wb_start_ble(void) |
| { |
| SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { |
| { { 0, 0, 0 } }, /**< Header unused */ |
| { 0, /** pBleBufferAddress not used */ |
| 0, /** BleBufferSize not used */ |
| CFG_BLE_NUM_GATT_ATTRIBUTES, |
| CFG_BLE_NUM_GATT_SERVICES, |
| CFG_BLE_ATT_VALUE_ARRAY_SIZE, |
| CFG_BLE_NUM_LINK, |
| CFG_BLE_DATA_LENGTH_EXTENSION, |
| CFG_BLE_PREPARE_WRITE_LIST_SIZE, |
| CFG_BLE_MBLOCK_COUNT, |
| CFG_BLE_MAX_ATT_MTU, |
| CFG_BLE_SLAVE_SCA, |
| CFG_BLE_MASTER_SCA, |
| CFG_BLE_LSE_SOURCE, |
| CFG_BLE_MAX_CONN_EVENT_LENGTH, |
| CFG_BLE_HSE_STARTUP_TIME, |
| CFG_BLE_VITERBI_MODE, |
| CFG_BLE_OPTIONS, |
| 0 } |
| }; |
| |
| /** |
| * Starts the BLE Stack on CPU2 |
| */ |
| SHCI_C2_BLE_Init(&ble_init_cmd_packet); |
| } |
| |
| static void sysevt_received(void *pdata) |
| { |
| k_sem_give(&c2_started); |
| } |
| |
| static void syscmd_status_not(SHCI_TL_CmdStatus_t status) |
| { |
| BT_DBG("status:%d", status); |
| } |
| |
| /* |
| * https://github.com/zephyrproject-rtos/zephyr/issues/19509 |
| * Tested on nucleo_wb55rg (stm32wb55rg) BLE stack (v1.2.0) |
| * Unresolved Resolvable Private Addresses (RPA) |
| * is reported in the peer_rpa field, and not in the peer address, |
| * as it should, when this happens the peer address is set to all FFs |
| * 0A 00 01 08 01 01 FF FF FF FF FF FF 00 00 00 00 00 00 0C AA C5 B3 3D 6B ... |
| * If such message is passed to HCI core than pairing will essentially fail. |
| * Solution: Rewrite the event with the RPA in the PEER address field |
| */ |
| static void tryfix_event(TL_Evt_t *tev) |
| { |
| struct bt_hci_evt_le_meta_event *mev = (void *)&tev->payload; |
| |
| if (tev->evtcode != BT_HCI_EVT_LE_META_EVENT || |
| mev->subevent != BT_HCI_EVT_LE_ENH_CONN_COMPLETE) { |
| return; |
| } |
| |
| struct bt_hci_evt_le_enh_conn_complete *evt = |
| (void *)((uint8_t *)mev + (sizeof(*mev))); |
| |
| if (!bt_addr_cmp(&evt->peer_addr.a, BT_ADDR_NONE)) { |
| BT_WARN("Invalid peer addr %s", bt_addr_le_str(&evt->peer_addr)); |
| bt_addr_copy(&evt->peer_addr.a, &evt->peer_rpa); |
| evt->peer_addr.type = BT_ADDR_LE_RANDOM; |
| } |
| } |
| |
| void TM_EvtReceivedCb(TL_EvtPacket_t *hcievt) |
| { |
| k_fifo_put(&ipm_rx_events_fifo, hcievt); |
| } |
| |
| static void bt_ipm_rx_thread(void) |
| { |
| while (true) { |
| bool discardable = false; |
| k_timeout_t timeout = K_FOREVER; |
| static TL_EvtPacket_t *hcievt; |
| struct net_buf *buf = NULL; |
| struct bt_hci_acl_hdr acl_hdr; |
| TL_AclDataSerial_t *acl; |
| struct bt_hci_evt_le_meta_event *mev; |
| size_t buf_tailroom; |
| size_t buf_add_len; |
| |
| hcievt = k_fifo_get(&ipm_rx_events_fifo, K_FOREVER); |
| |
| k_sem_take(&ipm_busy, K_FOREVER); |
| |
| switch (hcievt->evtserial.type) { |
| case HCI_EVT: |
| BT_DBG("EVT: hcievt->evtserial.evt.evtcode: 0x%02x", |
| hcievt->evtserial.evt.evtcode); |
| switch (hcievt->evtserial.evt.evtcode) { |
| case BT_HCI_EVT_VENDOR: |
| /* Vendor events are currently unsupported */ |
| BT_ERR("Unknown evtcode type 0x%02x", |
| hcievt->evtserial.evt.evtcode); |
| TL_MM_EvtDone(hcievt); |
| goto end_loop; |
| default: |
| mev = (void *)&hcievt->evtserial.evt.payload; |
| if (hcievt->evtserial.evt.evtcode == BT_HCI_EVT_LE_META_EVENT && |
| (mev->subevent == BT_HCI_EVT_LE_ADVERTISING_REPORT)) { |
| discardable = true; |
| timeout = K_NO_WAIT; |
| } |
| |
| buf = bt_buf_get_evt( |
| hcievt->evtserial.evt.evtcode, |
| discardable, timeout); |
| if (!buf) { |
| BT_DBG("Discard adv report due to insufficient buf"); |
| goto end_loop; |
| } |
| } |
| |
| tryfix_event(&hcievt->evtserial.evt); |
| |
| buf_tailroom = net_buf_tailroom(buf); |
| buf_add_len = hcievt->evtserial.evt.plen + 2; |
| if (buf_tailroom < buf_add_len) { |
| BT_ERR("Not enough space in buffer %zu/%zu", |
| buf_add_len, buf_tailroom); |
| net_buf_unref(buf); |
| goto end_loop; |
| } |
| |
| net_buf_add_mem(buf, &hcievt->evtserial.evt, |
| buf_add_len); |
| break; |
| case HCI_ACL: |
| acl = &(((TL_AclDataPacket_t *)hcievt)->AclDataSerial); |
| buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER); |
| acl_hdr.handle = acl->handle; |
| acl_hdr.len = acl->length; |
| BT_DBG("ACL: handle %x, len %x", |
| acl_hdr.handle, acl_hdr.len); |
| net_buf_add_mem(buf, &acl_hdr, sizeof(acl_hdr)); |
| |
| buf_tailroom = net_buf_tailroom(buf); |
| buf_add_len = acl_hdr.len; |
| if (buf_tailroom < buf_add_len) { |
| BT_ERR("Not enough space in buffer %zu/%zu", |
| buf_add_len, buf_tailroom); |
| net_buf_unref(buf); |
| goto end_loop; |
| } |
| |
| net_buf_add_mem(buf, (uint8_t *)&acl->acl_data, |
| buf_add_len); |
| break; |
| default: |
| BT_ERR("Unknown BT buf type %d", |
| hcievt->evtserial.type); |
| TL_MM_EvtDone(hcievt); |
| goto end_loop; |
| } |
| |
| TL_MM_EvtDone(hcievt); |
| |
| bt_recv(buf); |
| end_loop: |
| k_sem_give(&ipm_busy); |
| } |
| |
| } |
| |
| static void TM_AclDataAck(void) |
| { |
| k_sem_give(&acl_data_ack); |
| } |
| |
| void shci_notify_asynch_evt(void *pdata) |
| { |
| shci_user_evt_proc(); |
| } |
| |
| void shci_cmd_resp_release(uint32_t flag) |
| { |
| k_sem_give(&ble_sys_wait_cmd_rsp); |
| } |
| |
| void shci_cmd_resp_wait(uint32_t timeout) |
| { |
| k_sem_take(&ble_sys_wait_cmd_rsp, K_MSEC(timeout)); |
| } |
| |
| void ipcc_reset(void) |
| { |
| /* Reset IPCC */ |
| LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC); |
| |
| LL_C1_IPCC_ClearFlag_CHx( |
| IPCC, |
| LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | |
| LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); |
| |
| LL_C2_IPCC_ClearFlag_CHx( |
| IPCC, |
| LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | |
| LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); |
| |
| LL_C1_IPCC_DisableTransmitChannel( |
| IPCC, |
| LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | |
| LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); |
| |
| LL_C2_IPCC_DisableTransmitChannel( |
| IPCC, |
| LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | |
| LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); |
| |
| LL_C1_IPCC_DisableReceiveChannel( |
| IPCC, |
| LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | |
| LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); |
| |
| LL_C2_IPCC_DisableReceiveChannel( |
| IPCC, |
| LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | |
| LL_IPCC_CHANNEL_4 | LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6); |
| |
| /* Set IPCC default IRQ handlers */ |
| IRQ_CONNECT(IPCC_C1_RX_IRQn, 0, HW_IPCC_Rx_Handler, NULL, 0); |
| IRQ_CONNECT(IPCC_C1_TX_IRQn, 0, HW_IPCC_Tx_Handler, NULL, 0); |
| } |
| |
| void transport_init(void) |
| { |
| TL_MM_Config_t tl_mm_config; |
| TL_BLE_InitConf_t tl_ble_config; |
| SHCI_TL_HciInitConf_t shci_init_config; |
| |
| BT_DBG("BleCmdBuffer: %p", (void *)&BleCmdBuffer); |
| BT_DBG("HciAclDataBuffer: %p", (void *)&HciAclDataBuffer); |
| BT_DBG("SystemCmdBuffer: %p", (void *)&SystemCmdBuffer); |
| BT_DBG("EvtPool: %p", (void *)&EvtPool); |
| BT_DBG("SystemSpareEvtBuffer: %p", (void *)&SystemSpareEvtBuffer); |
| BT_DBG("BleSpareEvtBuffer: %p", (void *)&BleSpareEvtBuffer); |
| |
| /**< Reference table initialization */ |
| TL_Init(); |
| |
| /**< System channel initialization */ |
| shci_init_config.p_cmdbuffer = (uint8_t *)&SystemCmdBuffer; |
| shci_init_config.StatusNotCallBack = syscmd_status_not; |
| shci_init(sysevt_received, (void *) &shci_init_config); |
| |
| /**< Memory Manager channel initialization */ |
| tl_mm_config.p_BleSpareEvtBuffer = BleSpareEvtBuffer; |
| tl_mm_config.p_SystemSpareEvtBuffer = SystemSpareEvtBuffer; |
| tl_mm_config.p_AsynchEvtPool = EvtPool; |
| tl_mm_config.AsynchEvtPoolSize = POOL_SIZE; |
| TL_MM_Init(&tl_mm_config); |
| |
| /**< BLE channel initialization */ |
| tl_ble_config.p_cmdbuffer = (uint8_t *)&BleCmdBuffer; |
| tl_ble_config.p_AclDataBuffer = HciAclDataBuffer; |
| tl_ble_config.IoBusEvtCallBack = TM_EvtReceivedCb; |
| tl_ble_config.IoBusAclDataTxAck = TM_AclDataAck; |
| TL_BLE_Init((void *)&tl_ble_config); |
| |
| TL_Enable(); |
| } |
| |
| static int bt_ipm_send(struct net_buf *buf) |
| { |
| TL_CmdPacket_t *ble_cmd_buff = &BleCmdBuffer; |
| |
| k_sem_take(&ipm_busy, K_FOREVER); |
| |
| switch (bt_buf_get_type(buf)) { |
| case BT_BUF_ACL_OUT: |
| BT_DBG("ACL: buf %p type %u len %u", buf, bt_buf_get_type(buf), |
| buf->len); |
| k_sem_take(&acl_data_ack, K_FOREVER); |
| net_buf_push_u8(buf, HCI_ACL); |
| memcpy((void *) |
| &((TL_AclDataPacket_t *)HciAclDataBuffer)->AclDataSerial, |
| buf->data, buf->len); |
| TL_BLE_SendAclData(NULL, 0); |
| break; |
| case BT_BUF_CMD: |
| BT_DBG("CMD: buf %p type %u len %u", buf, bt_buf_get_type(buf), |
| buf->len); |
| ble_cmd_buff->cmdserial.type = HCI_CMD; |
| ble_cmd_buff->cmdserial.cmd.plen = buf->len; |
| memcpy((void *)&ble_cmd_buff->cmdserial.cmd, buf->data, |
| buf->len); |
| TL_BLE_SendCmd(NULL, 0); |
| break; |
| default: |
| k_sem_give(&ipm_busy); |
| BT_ERR("Unsupported type"); |
| return -EINVAL; |
| } |
| |
| k_sem_give(&ipm_busy); |
| |
| net_buf_unref(buf); |
| |
| return 0; |
| } |
| |
| static void start_ble_rf(void) |
| { |
| if ((LL_RCC_IsActiveFlag_PINRST()) && (!LL_RCC_IsActiveFlag_SFTRST())) { |
| /* Simulate power off reset */ |
| LL_PWR_EnableBkUpAccess(); |
| LL_PWR_EnableBkUpAccess(); |
| LL_RCC_ForceBackupDomainReset(); |
| LL_RCC_ReleaseBackupDomainReset(); |
| } |
| |
| #if STM32_LSE_ENABLED |
| /* Configure driving capability */ |
| LL_RCC_LSE_SetDriveCapability(STM32_LSE_DRIVING << RCC_BDCR_LSEDRV_Pos); |
| /* Select LSE clock */ |
| LL_RCC_LSE_Enable(); |
| while (!LL_RCC_LSE_IsReady()) { |
| } |
| |
| /* Select wakeup source of BLE RF */ |
| LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); |
| LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); |
| |
| /* Switch OFF LSI */ |
| LL_RCC_LSI2_Disable(); |
| #else |
| LL_RCC_LSI2_Enable(); |
| while (!LL_RCC_LSI2_IsReady()) { |
| } |
| |
| /* Select wakeup source of BLE RF */ |
| LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSI); |
| LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); |
| #endif |
| |
| /* Set RNG on HSI48 */ |
| LL_RCC_HSI48_Enable(); |
| while (!LL_RCC_HSI48_IsReady()) { |
| } |
| |
| LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_HSI48); |
| } |
| |
| bt_addr_t *bt_get_ble_addr(void) |
| { |
| bt_addr_t *bd_addr; |
| uint32_t udn; |
| uint32_t company_id; |
| uint32_t device_id; |
| |
| /* Get the 64 bit Unique Device Number UID */ |
| /* The UID is used by firmware to derive */ |
| /* 48-bit Device Address EUI-48 */ |
| udn = LL_FLASH_GetUDN(); |
| |
| if (udn != 0xFFFFFFFF) { |
| /* Get the ST Company ID */ |
| company_id = LL_FLASH_GetSTCompanyID(); |
| /* Get the STM32 Device ID */ |
| device_id = LL_FLASH_GetDeviceID(); |
| bd_addr_udn.val[0] = (uint8_t)(udn & 0x000000FF); |
| bd_addr_udn.val[1] = (uint8_t)((udn & 0x0000FF00) >> 8); |
| bd_addr_udn.val[2] = (uint8_t)((udn & 0x00FF0000) >> 16); |
| bd_addr_udn.val[3] = (uint8_t)device_id; |
| bd_addr_udn.val[4] = (uint8_t)(company_id & 0x000000FF); |
| bd_addr_udn.val[5] = (uint8_t)((company_id & 0x0000FF00) >> 8); |
| bd_addr = &bd_addr_udn; |
| } else { |
| bd_addr = NULL; |
| } |
| |
| return bd_addr; |
| } |
| |
| static int bt_ipm_set_addr(void) |
| { |
| bt_addr_t *uid_addr; |
| struct aci_set_ble_addr *param; |
| struct net_buf *buf, *rsp; |
| int err; |
| |
| uid_addr = bt_get_ble_addr(); |
| if (!uid_addr) { |
| return -ENOMSG; |
| } |
| |
| buf = bt_hci_cmd_create(ACI_HAL_WRITE_CONFIG_DATA, sizeof(*param)); |
| |
| if (!buf) { |
| return -ENOBUFS; |
| } |
| |
| param = net_buf_add(buf, sizeof(*param)); |
| param->config_offset = HCI_CONFIG_DATA_PUBADDR_OFFSET; |
| param->length = 6; |
| param->value[0] = uid_addr->val[0]; |
| param->value[1] = uid_addr->val[1]; |
| param->value[2] = uid_addr->val[2]; |
| param->value[3] = uid_addr->val[3]; |
| param->value[4] = uid_addr->val[4]; |
| param->value[5] = uid_addr->val[5]; |
| |
| err = bt_hci_cmd_send_sync(ACI_HAL_WRITE_CONFIG_DATA, buf, &rsp); |
| if (err) { |
| return err; |
| } |
| net_buf_unref(rsp); |
| return 0; |
| } |
| |
| static int bt_ipm_ble_init(void) |
| { |
| struct aci_set_tx_power *param; |
| struct net_buf *buf, *rsp; |
| int err; |
| |
| /* Send HCI_RESET */ |
| err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp); |
| if (err) { |
| return err; |
| } |
| /* TDB: Something to do on reset complete? */ |
| net_buf_unref(rsp); |
| err = bt_ipm_set_addr(); |
| if (err) { |
| BT_ERR("Can't set BLE UID addr"); |
| } |
| /* Send ACI_WRITE_SET_TX_POWER_LEVEL */ |
| buf = bt_hci_cmd_create(ACI_WRITE_SET_TX_POWER_LEVEL, 3); |
| if (!buf) { |
| return -ENOBUFS; |
| } |
| param = net_buf_add(buf, sizeof(*param)); |
| param->cmd = 0x0F; |
| param->value[0] = 0x18; |
| param->value[1] = 0x01; |
| |
| err = bt_hci_cmd_send_sync(ACI_WRITE_SET_TX_POWER_LEVEL, buf, &rsp); |
| if (err) { |
| return err; |
| } |
| net_buf_unref(rsp); |
| |
| return 0; |
| } |
| |
| static int c2_reset(void) |
| { |
| start_ble_rf(); |
| |
| /* Take BLE out of reset */ |
| ipcc_reset(); |
| |
| transport_init(); |
| |
| /* Device will let us know when it's ready */ |
| if (k_sem_take(&c2_started, STM32WB_C2_LOCK_TIMEOUT)) { |
| return -ETIMEDOUT; |
| } |
| BT_DBG("C2 unlocked"); |
| |
| stm32wb_start_ble(); |
| |
| c2_started_flag = true; |
| |
| return 0; |
| } |
| |
| static int bt_ipm_open(void) |
| { |
| int err; |
| |
| if (!c2_started_flag) { |
| /* C2 has been teared down. Reinit required */ |
| SHCI_C2_Reinit(); |
| while (LL_PWR_IsActiveFlag_C2DS() == 0) { |
| }; |
| |
| err = c2_reset(); |
| if (err) { |
| return err; |
| } |
| } |
| |
| /* Start RX thread */ |
| k_thread_create(&ipm_rx_thread_data, ipm_rx_stack, |
| K_KERNEL_STACK_SIZEOF(ipm_rx_stack), |
| (k_thread_entry_t)bt_ipm_rx_thread, NULL, NULL, NULL, |
| K_PRIO_COOP(CONFIG_BT_DRIVER_RX_HIGH_PRIO), |
| 0, K_NO_WAIT); |
| |
| err = bt_ipm_ble_init(); |
| if (err) { |
| return err; |
| } |
| |
| BT_DBG("IPM Channel Open Completed"); |
| |
| return 0; |
| } |
| |
| static int bt_ipm_close(void) |
| { |
| int err; |
| struct net_buf *rsp; |
| |
| err = bt_hci_cmd_send_sync(ACI_HAL_STACK_RESET, NULL, &rsp); |
| if (err) { |
| BT_ERR("IPM Channel Close Issue"); |
| return err; |
| } |
| net_buf_unref(rsp); |
| |
| /* Wait till C2DS set */ |
| while (LL_PWR_IsActiveFlag_C2DS() == 0) { |
| }; |
| |
| c2_started_flag = false; |
| |
| k_thread_abort(&ipm_rx_thread_data); |
| |
| BT_DBG("IPM Channel Close Completed"); |
| |
| return err; |
| } |
| |
| static const struct bt_hci_driver drv = { |
| .name = "BT IPM", |
| .bus = BT_HCI_DRIVER_BUS_IPM, |
| .quirks = BT_QUIRK_NO_RESET, |
| .open = bt_ipm_open, |
| .close = bt_ipm_close, |
| .send = bt_ipm_send, |
| }; |
| |
| static int _bt_ipm_init(const struct device *unused) |
| { |
| int err; |
| |
| ARG_UNUSED(unused); |
| |
| bt_hci_driver_register(&drv); |
| |
| err = c2_reset(); |
| if (err) { |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| SYS_INIT(_bt_ipm_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |