| /**************************************************************************** |
| * arch/arm/src/stm32wb/stm32wb_blehci.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/wireless/bluetooth/bt_hci.h> |
| #include <nuttx/wireless/bluetooth/bt_driver.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/wqueue.h> |
| |
| #include "stm32wb_ipcc.h" |
| #include "stm32wb_mbox.h" |
| #include "stm32wb_mbox_shci.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* HCI event header fields helpers */ |
| |
| #define STM32WB_BLEHCI_CCEVT_OPCODE(e) (*(uint16_t *)((uint8_t *)(e) + 3)) |
| #define STM32WB_BLEHCI_CCEVT_STATUS(e) (*((uint8_t *)(e) + 5)) |
| |
| #define STM32WB_BLEHCI_CSEVT_OPCODE(e) (*(uint16_t *)((uint8_t *)(e) + 4)) |
| #define STM32WB_BLEHCI_CSEVT_STATUS(e) (*((uint8_t *)(e) + 2)) |
| |
| /* BLE init configuration params */ |
| |
| #define STM32WB_BLE_PREP_WRITE_NUM \ |
| STM32WB_MBOX_DEFAULT_BLE_PREP_WRITE_NUM(CONFIG_STM32WB_BLE_MAX_ATT_MTU) |
| |
| #define STM32WB_C2_MEM_BLOCK_NUM \ |
| STM32WB_MBOX_DEFAULT_C2_MEM_BLOCK_NUM(CONFIG_STM32WB_BLE_MAX_ATT_MTU, \ |
| CONFIG_STM32WB_BLE_MAX_CONN, \ |
| STM32WB_BLE_PREP_WRITE_NUM) |
| |
| #ifdef CONFIG_STM32WB_BLE_C2HOST |
| # define STM32WB_BLE_C2HOST STM32WB_SHCI_BLE_INIT_OPT_STACK_LL_HOST |
| #else |
| # define STM32WB_BLE_C2HOST STM32WB_SHCI_BLE_INIT_OPT_STACK_LL |
| #endif |
| |
| #ifdef CONFIG_STM32WB_BLE_SVC_CHANGED_CHAR |
| # define STM32WB_BLE_SVC_CHANGED_CHAR STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_ENABLED |
| #else |
| # define STM32WB_BLE_SVC_CHANGED_CHAR STM32WB_SHCI_BLE_INIT_OPT_SVC_CHCHAR_DISABLED |
| #endif |
| |
| #ifdef CONFIG_STM32WB_BLE_WRITABLE_DEVICE_NAME |
| # define STM32WB_BLE_DEVICE_NAME_MODE STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_RW |
| #else |
| # define STM32WB_BLE_DEVICE_NAME_MODE STM32WB_SHCI_BLE_INIT_OPT_DEVICE_NAME_MODE_RO |
| #endif |
| |
| #ifdef CONFIG_STM32WB_BLE_CHAN_SEL_ALG2 |
| # define STM32WB_BLE_CS_ALG2 STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_ENABLED |
| #else |
| # define STM32WB_BLE_CS_ALG2 STM32WB_SHCI_BLE_INIT_OPT_CS_ALG2_DISABLED |
| #endif |
| |
| #ifdef CONFIG_STM32WB_BLE_POWER_CLASS_1 |
| # define STM32WB_BLE_POWER_CLASS STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_1 |
| #else |
| # define STM32WB_BLE_POWER_CLASS STM32WB_SHCI_BLE_INIT_OPT_POWER_CLASS_2_3 |
| #endif |
| |
| #define STM32WB_BLE_INIT_OPTIONS \ |
| (STM32WB_BLE_C2HOST | STM32WB_BLE_SVC_CHANGED_CHAR | \ |
| STM32WB_BLE_DEVICE_NAME_MODE | STM32WB_BLE_CS_ALG2 | \ |
| STM32WB_BLE_POWER_CLASS) |
| |
| #ifdef CONFIG_STM32WB_BLE_AGC_RSSI_IMPROVED |
| # define STM32WB_BLE_RXMOD_AGC_RSSI STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_IMPROVED |
| #else |
| # define STM32WB_BLE_RXMOD_AGC_RSSI STM32WB_SHCI_BLE_INIT_RXMOD_AGC_RSSI_LEGACY |
| #endif |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int stm32wb_blehci_driveropen(struct bt_driver_s *btdev); |
| static int stm32wb_blehci_driversend(struct bt_driver_s *btdev, |
| enum bt_buf_type_e type, |
| void *data, size_t len); |
| static int stm32wb_blehci_rxevt(struct stm32wb_mbox_evt_s *evt); |
| static void stm32wb_blehci_bleinit(void); |
| static int stm32wb_blehci_driverinitialize(void); |
| static void stm32wb_blehci_drvinitworker(void *arg); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static struct bt_driver_s g_blehci_driver = |
| { |
| .head_reserve = 0, |
| .open = stm32wb_blehci_driveropen, |
| .send = stm32wb_blehci_driversend |
| }; |
| |
| static mutex_t g_lock = NXMUTEX_INITIALIZER; |
| struct work_s g_drv_init_work; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_driveropen |
| ****************************************************************************/ |
| |
| static int stm32wb_blehci_driveropen(struct bt_driver_s *btdev) |
| { |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_driversend |
| ****************************************************************************/ |
| |
| static int stm32wb_blehci_driversend(struct bt_driver_s *btdev, |
| enum bt_buf_type_e type, |
| void *data, size_t len) |
| { |
| int ret = -EIO; |
| |
| if (type == BT_CMD || type == BT_ACL_OUT) |
| { |
| if (type == BT_CMD) |
| { |
| wlinfo("passing CMD 0x%04x (len: %u) to mailbox driver\n", |
| ((struct bt_hci_cmd_hdr_s *)data)->opcode, |
| ((struct bt_hci_cmd_hdr_s *)data)->param_len); |
| } |
| else |
| { |
| wlinfo("passing ACL (handle: 0x%04x, len: %u) to mailbox driver\n", |
| ((struct bt_hci_acl_hdr_s *)data)->handle, |
| ((struct bt_hci_acl_hdr_s *)data)->len); |
| } |
| |
| /* Ensure non-concurrent access */ |
| |
| ret = nxmutex_lock(&g_lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| if (type == BT_CMD) |
| { |
| ret = stm32wb_mbox_blecmd(data, len); |
| } |
| else |
| { |
| ret = stm32wb_mbox_bleacl(data, len); |
| } |
| |
| nxmutex_unlock(&g_lock); |
| } |
| |
| return ret < 0 ? ret : (int)len; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_rxevt |
| ****************************************************************************/ |
| |
| static int stm32wb_blehci_rxevt(struct stm32wb_mbox_evt_s *evt) |
| { |
| size_t len; |
| |
| switch (evt->type) |
| { |
| case STM32WB_MBOX_HCIEVT: |
| len = sizeof(evt->evt_hdr) + evt->evt_hdr.len; |
| if (evt->evt_hdr.evt == BT_HCI_EVT_CMD_COMPLETE) |
| { |
| wlinfo("received command COMPLETE event from mailbox " |
| "(opcode: 0x%04x, status: %u)\n", |
| STM32WB_BLEHCI_CCEVT_OPCODE(&evt->evt_hdr), |
| STM32WB_BLEHCI_CCEVT_STATUS(&evt->evt_hdr)); |
| } |
| else if (evt->evt_hdr.evt == BT_HCI_EVT_CMD_STATUS) |
| { |
| wlinfo("received command STATUS event from mailbox " |
| "(opcode: 0x%04x, status: %u)\n", |
| STM32WB_BLEHCI_CSEVT_OPCODE(&evt->evt_hdr), |
| STM32WB_BLEHCI_CSEVT_STATUS(&evt->evt_hdr)); |
| |
| #ifdef CONFIG_NIMBLE |
| /* During initialisation NimBLE host stack sends unsupported |
| * 'Set Event Mask Page' HCI command, and a failing response |
| * causes the stack initialisation to fail. As a workaround |
| * with minimal impact we shim the response as succeeded. |
| */ |
| |
| if (STM32WB_BLEHCI_CSEVT_STATUS(&evt->evt_hdr) != 0 && |
| (STM32WB_BLEHCI_CSEVT_OPCODE(&evt->evt_hdr) == |
| BT_OP(BT_OGF_BASEBAND, 0x0063))) |
| { |
| wlwarn("suppress FAILED command STATUS event from mailbox, " |
| "(opcode: 0x%04x, status: %u) \n", |
| STM32WB_BLEHCI_CSEVT_OPCODE(&evt->evt_hdr), |
| STM32WB_BLEHCI_CSEVT_STATUS(&evt->evt_hdr)); |
| |
| /* Suppress status field error value */ |
| |
| STM32WB_BLEHCI_CSEVT_STATUS(&evt->evt_hdr) = 0; |
| } |
| #endif |
| } |
| else |
| { |
| wlinfo("received HCI EVT 0x%02x from mailbox (len: %u)\n", |
| evt->evt_hdr.evt, evt->evt_hdr.len); |
| } |
| |
| bt_netdev_receive(&g_blehci_driver, BT_EVT, &evt->evt_hdr, len); |
| break; |
| |
| case STM32WB_MBOX_HCIACL: |
| len = sizeof(evt->acl_hdr) + evt->acl_hdr.len; |
| wlinfo("received HCI ACL from mailbox (handle: 0x%04x, len: %u)\n", |
| evt->acl_hdr.handle, evt->acl_hdr.len); |
| bt_netdev_receive(&g_blehci_driver, BT_ACL_IN, &evt->acl_hdr, len); |
| break; |
| |
| case STM32WB_MBOX_SYSEVT: |
| wlinfo("received SYS EVT 0x%02x from mailbox\n", evt->evt_hdr.evt); |
| if (evt->evt_hdr.evt == STM32WB_SHCI_ASYNC_EVT && |
| *(uint16_t *)(&evt->evt_hdr + 1) == STM32WB_SHCI_ASYNC_EVT_C2RDY) |
| { |
| stm32wb_blehci_bleinit(); |
| } |
| break; |
| |
| case STM32WB_MBOX_SYSACK: |
| |
| /* CPU2 Ready is the only expected response */ |
| |
| DEBUGASSERT(evt->evt_hdr.evt == STM32WB_SHCI_ACK_EVT_C2RDY); |
| |
| if (evt->evt_hdr.evt == STM32WB_SHCI_ACK_EVT_C2RDY) |
| { |
| wlinfo("system command ACK response"); |
| |
| /* Make driver initialisation in low priority work queue */ |
| |
| work_queue(LPWORK, &g_drv_init_work, |
| stm32wb_blehci_drvinitworker, NULL, 0); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_bleinit |
| ****************************************************************************/ |
| |
| static void stm32wb_blehci_bleinit(void) |
| { |
| /* Prepare BLE configuration */ |
| |
| struct stm32wb_shci_ble_init_cfg_s params = |
| { |
| .ble_buf = NULL, |
| .ble_buf_size = 0, |
| .gatt_attr_num = CONFIG_STM32WB_BLE_GATT_MAX_ATTR_NUM, |
| .gatt_srv_num = CONFIG_STM32WB_BLE_GATT_MAX_SVC_NUM, |
| .gatt_attr_buf_size = CONFIG_STM32WB_BLE_GATT_ATTR_BUF_SIZE, |
| .max_conn = CONFIG_STM32WB_BLE_MAX_CONN, |
| .dle_enable = CONFIG_STM32WB_BLE_DLE, |
| .prep_write_op_num = STM32WB_BLE_PREP_WRITE_NUM, |
| .mem_block_num = STM32WB_C2_MEM_BLOCK_NUM, |
| .att_max_mtu_size = CONFIG_STM32WB_BLE_MAX_ATT_MTU, |
| .slave_sca = CONFIG_STM32WB_BLE_SLAVE_SCA, |
| .master_sca_range = CONFIG_STM32WB_BLE_MASTER_SCA, |
| .ls_clock_source = CONFIG_STM32WB_BLE_LS_CLK_SRC, |
| .conn_event_length = CONFIG_STM32WB_BLE_MAX_CONN_EVT_LENGTH, |
| .hse_startup = CONFIG_STM32WB_BLE_HSE_STARTUP, |
| .viterbi_enable = CONFIG_STM32WB_BLE_VITERBI, |
| .options = STM32WB_BLE_INIT_OPTIONS, |
| .hw_version = 0, |
| .max_initor_coc_num = CONFIG_STM32WB_BLE_MAX_INITOR_COC_NUM, |
| .tx_power_min = CONFIG_STM32WB_BLE_MIN_TX_POWER, |
| .tx_power_max = CONFIG_STM32WB_BLE_MAX_TX_POWER, |
| .rx_model_config = STM32WB_BLE_RXMOD_AGC_RSSI |
| }; |
| |
| /* Initialise BLE */ |
| |
| stm32wb_mbox_bleinit(¶ms); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_driverinitialize |
| ****************************************************************************/ |
| |
| static int stm32wb_blehci_driverinitialize(void) |
| { |
| int ret = 0; |
| |
| ret = bt_driver_register(&g_blehci_driver); |
| if (ret < 0) |
| { |
| wlerr("bt_driver_register error: %d\n", ret); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_drvinitworker |
| ****************************************************************************/ |
| |
| static void stm32wb_blehci_drvinitworker(void *arg) |
| { |
| stm32wb_blehci_driverinitialize(); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: stm32wb_blehci_initialize |
| * |
| * Description: |
| * Initialize and register BLE HCI driver which interfaces a BLE host |
| * stack to a BLE controller running on CPU2 via HCI protocol. Driver |
| * registration occurs later when CPU2 notifies its ready status. |
| * |
| ****************************************************************************/ |
| |
| void stm32wb_blehci_initialize(void) |
| { |
| /* Initialize mbox internal data structures and set |
| * event receive handler. |
| */ |
| |
| stm32wb_mboxinitialize(stm32wb_blehci_rxevt); |
| |
| /* Enable communication hardware and boot up CPU2 */ |
| |
| stm32wb_mboxenable(); |
| } |