From d42fc7ae216824018b241e4e410eb2f7697792f1 Mon Sep 17 00:00:00 2001 From: Ali Hozhabri Date: Fri, 12 Jul 2024 17:37:27 +0200 Subject: [PATCH] drivers: bluetooth: hci: Add Bluetooth HCI driver for STM32WB0x Add Bluetooth HCI driver for STM32WB0x series. Modify CMakeLists.txt to compile the driver based on its kconfig parameter. Signed-off-by: Ali Hozhabri --- drivers/bluetooth/hci/CMakeLists.txt | 3 +- drivers/bluetooth/hci/hci_stm32wb0.c | 503 +++++++++++++++++++++++++++ 2 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 drivers/bluetooth/hci/hci_stm32wb0.c diff --git a/drivers/bluetooth/hci/CMakeLists.txt b/drivers/bluetooth/hci/CMakeLists.txt index b205fbc4e13..48a46ead739 100644 --- a/drivers/bluetooth/hci/CMakeLists.txt +++ b/drivers/bluetooth/hci/CMakeLists.txt @@ -14,7 +14,8 @@ zephyr_library_sources_ifdef(CONFIG_BT_CYW208XX hci_ifx_cyw208xx.c) zephyr_library_sources_ifdef(CONFIG_BT_STM32_IPM ipm_stm32wb.c) zephyr_library_sources_ifdef(CONFIG_BT_STM32WBA hci_stm32wba.c) -if(CONFIG_DT_HAS_ST_HCI_STM32WBA_ENABLED) +zephyr_library_sources_ifdef(CONFIG_BT_STM32WB0 hci_stm32wb0.c) +if((CONFIG_DT_HAS_ST_HCI_STM32WBA_ENABLED) OR (CONFIG_DT_HAS_ST_HCI_STM32WB0_ENABLED)) zephyr_blobs_verify(MODULE hal_stm32 REQUIRED) endif() zephyr_library_sources_ifdef(CONFIG_BT_USERCHAN userchan.c) diff --git a/drivers/bluetooth/hci/hci_stm32wb0.c b/drivers/bluetooth/hci/hci_stm32wb0.c new file mode 100644 index 00000000000..b47de037a2d --- /dev/null +++ b/drivers/bluetooth/hci/hci_stm32wb0.c @@ -0,0 +1,503 @@ +/* hci_stm32wb0.c - HCI driver for stm32wb0x */ + +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "bleplat_cntr.h" +#include "ble_stack.h" +#include "stm32wb0x_hal_radio_timer.h" +#include "miscutil.h" +#include "pka_manager.h" +#include "app_conf.h" +#include "dtm_cmd_db.h" +#include "dm_alloc.h" +#include "aci_adv_nwk.h" +#include "app_common.h" +#include "hw_rng.h" +#include "hw_aes.h" +#include "hw_pka.h" + +#define LOG_LEVEL CONFIG_BT_HCI_DRIVER_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_driver); + +#define DT_DRV_COMPAT st_hci_stm32wb0 + +/* Max HS startup time expressed in system time (1953 us / 2.4414 us) */ +#define MAX_HS_STARTUP_TIME 320 +#define BLE_WKUP_PRIO 0 +#define BLE_WKUP_FLAGS 0 +#define BLE_TX_RX_PRIO 0 +#define BLE_TX_RX_FLAGS 0 +#define CPU_WKUP_PRIO 1 +#define CPU_WKUP_FLAGS 0 +#define BLE_ERROR_PRIO 3 +#define BLE_ERROR_FLAGS 0 +#define BLE_RXTX_SEQ_PRIO 3 +#define BLE_RXTX_SEQ_FLAGS 0 +#define PKA_PRIO 2 +#define PKA_FLAGS 0 + +#define MAX_EVENT_SIZE 259 +#define MAX_ISO_DATA_LOAD_LENGTH 512 + +#define PACKET_TYPE 0 +#define EVT_HEADER_TYPE 0 +#define EVT_HEADER_EVENT 1 +#define EVT_HEADER_SIZE 2 +#define EVT_LE_META_SUBEVENT 3 +#define EVT_VENDOR_CODE_LSB 3 +#define EVT_VENDOR_CODE_MSB 4 + +static uint32_t __noinit dyn_alloc_a[BLE_DYN_ALLOC_SIZE >> 2]; +static uint8_t buffer_out_mem[MAX_EVENT_SIZE]; +static struct k_work_delayable hal_radio_timer_work, ble_stack_work; + +static struct net_buf *get_rx(uint8_t *msg); +static PKA_HandleTypeDef hpka; + +#if CONFIG_BT_EXT_ADV +static uint32_t __noinit aci_adv_nwk_buffer[CFG_BLE_ADV_NWK_BUFFER_SIZE >> 2]; +#endif /* CONFIG_BT_EXT_ADV */ + +struct hci_data { + bt_hci_recv_t recv; +}; + +/* Dummy implementation */ +int BLEPLAT_NvmGet(void) +{ + return 0; +} + +static void blestack_process(struct k_work *work) +{ + BLE_STACK_Tick(); + if (BLE_STACK_SleepCheck() == 0) { + k_work_reschedule(&ble_stack_work, K_NO_WAIT); + } +} + +static void vtimer_process(struct k_work *work) +{ + HAL_RADIO_TIMER_Tick(); +} + +/* "If, since the last power-on or reset, the Host has ever issued a legacy + * advertising command and then issues an extended advertising command, or + * has ever issued an extended advertising command and then issues a legacy + * advertising command, the Controller shall return the error code Command + * Disallowed (0x0C)." + * This function returns 1 if an error has to be given. + */ +static uint8_t check_legacy_extended_call(uint16_t opcode, uint8_t *buffer_out) +{ + static bool legacy_cmd_issued, extended_cmd_issued; + bool allowed = true; + + if (IN_RANGE(opcode, BT_HCI_OP_LE_SET_ADV_PARAM, BT_HCI_OP_LE_CREATE_CONN)) { + if (extended_cmd_issued) { + allowed = false; /* Error */ + LOG_ERR("Extended not allowed"); + } else { + legacy_cmd_issued = true; + allowed = true; /* OK */ + } + } else if ((opcode >= BT_HCI_OP_LE_SET_EXT_ADV_PARAM) && + (opcode <= BT_HCI_OP_LE_READ_PER_ADV_LIST_SIZE)) { + if (legacy_cmd_issued) { + allowed = false; /* Error */ + LOG_ERR("Legacy not allowed"); + } else { + extended_cmd_issued = true; + allowed = true; /* OK */ + } + } + + if (!allowed) { + struct bt_hci_evt_hdr *evt_header = (struct bt_hci_evt_hdr *)(buffer_out + 1); + + *buffer_out = BT_HCI_H4_EVT; + if (opcode == BT_HCI_OP_LE_CREATE_CONN || opcode == BT_HCI_OP_LE_EXT_CREATE_CONN || + opcode == BT_HCI_OP_LE_PER_ADV_CREATE_SYNC) { + struct bt_hci_evt_cmd_status *params = + (struct bt_hci_evt_cmd_status *)(buffer_out + 3); + + evt_header->evt = BT_HCI_EVT_CMD_STATUS; + evt_header->len = 4; + params->status = BT_HCI_ERR_CMD_DISALLOWED; + params->ncmd = 1; + params->opcode = sys_cpu_to_le16(opcode); + } else { + struct bt_hci_evt_cmd_complete *params = + (struct bt_hci_evt_cmd_complete *)(buffer_out + 3); + + evt_header->evt = BT_HCI_EVT_CMD_COMPLETE; + evt_header->len = 4; + params->ncmd = 1; + params->opcode = sys_cpu_to_le16(opcode); + buffer_out[6] = BT_HCI_ERR_CMD_DISALLOWED; + } + return 7; + } + return 0; +} + +/* Process Commands */ +static uint16_t process_command(uint8_t *buffer, uint16_t buffer_in_length, uint8_t *buffer_out, + uint16_t buffer_out_max_length) +{ + uint32_t i; + uint16_t ret_val; + uint16_t op_code; + uint8_t *buffer_in = buffer + sizeof(struct bt_hci_cmd_hdr); + struct bt_hci_cmd_hdr *hdr = (struct bt_hci_cmd_hdr *)buffer; + + buffer_in_length -= sizeof(struct bt_hci_cmd_hdr); + op_code = hdr->opcode; + ret_val = check_legacy_extended_call(op_code, buffer_out); + if (ret_val != 0) { + LOG_ERR("ret_val: %d", ret_val); + return ret_val; + } + + for (i = 0; hci_command_table[i].opcode != 0; i++) { + if (op_code == hci_command_table[i].opcode) { + ret_val = hci_command_table[i].execute(buffer_in, buffer_in_length, + buffer_out, buffer_out_max_length); + /* add get crash handler */ + return ret_val; + } + } + + struct bt_hci_evt_hdr *evt_header = (struct bt_hci_evt_hdr *)(buffer_out + 1); + struct bt_hci_evt_cmd_status *params = (struct bt_hci_evt_cmd_status *)(buffer_out + 3); + + *buffer_out = BT_HCI_H4_EVT; + evt_header->evt = BT_HCI_EVT_CMD_STATUS; + evt_header->len = 4; + params->status = BT_HCI_ERR_UNKNOWN_CMD; + params->ncmd = 1; + params->opcode = sys_cpu_to_le16(op_code); + return 7; +} + +void send_event(uint8_t *buffer_out, uint16_t buffer_out_length, int8_t overflow_index) +{ + ARG_UNUSED(buffer_out_length); + ARG_UNUSED(overflow_index); + + const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); + struct hci_data *hci = dev->data; + /* Construct net_buf from event data */ + struct net_buf *buf = get_rx(buffer_out); + + if (buf) { + /* Handle the received HCI data */ + LOG_DBG("New event %p len %u type %u", buf, buf->len, bt_buf_get_type(buf)); + hci->recv(dev, buf); + } else { + LOG_ERR("Buf is null"); + } +} + +void HAL_RADIO_TIMER_TxRxWakeUpCallback(void) +{ + k_work_schedule(&hal_radio_timer_work, K_NO_WAIT); + k_work_schedule(&ble_stack_work, K_NO_WAIT); +} + +void HAL_RADIO_TIMER_CpuWakeUpCallback(void) +{ + k_work_schedule(&hal_radio_timer_work, K_NO_WAIT); + k_work_schedule(&ble_stack_work, K_NO_WAIT); +} + +void HAL_RADIO_TxRxCallback(uint32_t flags) +{ + BLE_STACK_RadioHandler(flags); + k_work_schedule(&ble_stack_work, K_NO_WAIT); + k_work_schedule(&hal_radio_timer_work, K_NO_WAIT); +} + +ISR_DIRECT_DECLARE(RADIO_TIMER_TXRX_WKUP_IRQHandler) +{ + HAL_RADIO_TIMER_TXRX_WKUP_IRQHandler(); + ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ + return 1; +} + +ISR_DIRECT_DECLARE(RADIO_TXRX_IRQHandler) +{ + HAL_RADIO_TXRX_IRQHandler(); + ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ + return 1; +} + +ISR_DIRECT_DECLARE(RADIO_TXRX_SEQ_IRQHandler) +{ + HAL_RADIO_TXRX_SEQ_IRQHandler(); + ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ + return 1; +} + +ISR_DIRECT_DECLARE(RADIO_TIMER_CPU_WKUP_IRQHandler) +{ + HAL_RADIO_TIMER_TimeoutCallback(); + HAL_RADIO_TIMER_CpuWakeUpCallback(); + ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ + return 1; +} + +ISR_DIRECT_DECLARE(RADIO_TIMER_ERROR_IRQHandler) +{ + volatile uint32_t debug_cmd; + + BLUE->DEBUGCMDREG |= 1; + /* If the device is configured with CLK_SYS = 64MHz + * and BLE clock = 16MHz, a register read is necessary + * to ensure interrupt register is properly cleared + * due to AHB down converter latency + */ + debug_cmd = BLUE->DEBUGCMDREG; + LOG_ERR("Timer error"); + ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ + return 1; +} + +/* Function called from PKA_IRQHandler() context. */ +void PKAMGR_IRQCallback(void) +{ + k_work_schedule(&ble_stack_work, K_NO_WAIT); +} + +static int _PKA_IRQHandler(void *args) +{ + ARG_UNUSED(args); + + HAL_PKA_IRQHandler(&hpka); + ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ + return 1; +} + +static void ble_isr_installer(void) +{ + IRQ_DIRECT_CONNECT(RADIO_TIMER_TXRX_WKUP_IRQn, BLE_WKUP_PRIO, + RADIO_TIMER_TXRX_WKUP_IRQHandler, BLE_WKUP_FLAGS); + IRQ_DIRECT_CONNECT(RADIO_TXRX_IRQn, BLE_TX_RX_PRIO, RADIO_TXRX_IRQHandler, BLE_TX_RX_FLAGS); + IRQ_DIRECT_CONNECT(RADIO_TIMER_CPU_WKUP_IRQn, CPU_WKUP_PRIO, + RADIO_TIMER_CPU_WKUP_IRQHandler, CPU_WKUP_FLAGS); + IRQ_DIRECT_CONNECT(RADIO_TXRX_SEQ_IRQn, BLE_RXTX_SEQ_PRIO, RADIO_TXRX_SEQ_IRQHandler, + BLE_RXTX_SEQ_FLAGS); + IRQ_DIRECT_CONNECT(RADIO_TIMER_ERROR_IRQn, BLE_ERROR_PRIO, RADIO_TIMER_ERROR_IRQHandler, + BLE_ERROR_FLAGS); + IRQ_CONNECT(PKA_IRQn, PKA_PRIO, _PKA_IRQHandler, NULL, PKA_FLAGS); +} + +static struct net_buf *get_rx(uint8_t *msg) +{ + bool discardable = false; + k_timeout_t timeout = K_FOREVER; + struct net_buf *buf; + int len; + + switch (msg[PACKET_TYPE]) { + case BT_HCI_H4_EVT: + if (msg[EVT_HEADER_EVENT] == BT_HCI_EVT_LE_META_EVENT && + (msg[EVT_LE_META_SUBEVENT] == BT_HCI_EVT_LE_ADVERTISING_REPORT)) { + discardable = true; + timeout = K_NO_WAIT; + } + buf = bt_buf_get_evt(msg[EVT_HEADER_EVENT], discardable, timeout); + if (!buf) { + LOG_DBG("Discard adv report due to insufficient buf"); + return NULL; + } + + len = sizeof(struct bt_hci_evt_hdr) + msg[EVT_HEADER_SIZE]; + if (len > net_buf_tailroom(buf)) { + LOG_ERR("Event too long: %d", len); + net_buf_unref(buf); + return NULL; + } + net_buf_add_mem(buf, &msg[1], len); + break; + case BT_HCI_H4_ACL: + struct bt_hci_acl_hdr acl_hdr; + + buf = bt_buf_get_rx(BT_BUF_ACL_IN, timeout); + memcpy(&acl_hdr, &msg[1], sizeof(acl_hdr)); + len = sizeof(acl_hdr) + sys_le16_to_cpu(acl_hdr.len); + if (len > net_buf_tailroom(buf)) { + LOG_ERR("ACL too long: %d", len); + net_buf_unref(buf); + return NULL; + } + net_buf_add_mem(buf, &msg[1], len); + break; + case BT_HCI_H4_ISO: + struct bt_hci_iso_hdr iso_hdr; + + buf = bt_buf_get_rx(BT_BUF_ISO_IN, timeout); + if (buf) { + memcpy(&iso_hdr, &msg[1], sizeof(iso_hdr)); + len = sizeof(iso_hdr) + sys_le16_to_cpu(iso_hdr.len); + } else { + LOG_ERR("No available ISO buffers!"); + return NULL; + } + if (len > net_buf_tailroom(buf)) { + LOG_ERR("ISO too long: %d", len); + net_buf_unref(buf); + return NULL; + } + net_buf_add_mem(buf, &msg[1], len); + break; + default: + LOG_ERR("Unknown BT buf type %d", msg[0]); + return NULL; + } + + return buf; +} + +static int bt_hci_stm32wb0_send(const struct device *dev, struct net_buf *buf) +{ + int ret = 0; + uint8_t *hci_buffer = buf->data; + + ARG_UNUSED(dev); + + switch (bt_buf_get_type(buf)) { + case BT_BUF_ACL_OUT: { + uint16_t connection_handle; + uint16_t data_len; + uint8_t *pdu; + uint8_t pb_flag; + uint8_t bc_flag; + + connection_handle = ((hci_buffer[1] & 0x0F) << 8) + hci_buffer[0]; + data_len = (hci_buffer[3] << 8) + hci_buffer[2]; + pdu = hci_buffer + 4; + pb_flag = (hci_buffer[1] >> 4) & 0x3; + bc_flag = (hci_buffer[1] >> 6) & 0x3; + hci_tx_acl_data(connection_handle, pb_flag, bc_flag, data_len, pdu); + break; + } +#if defined(CONFIG_BT_ISO) + case BT_BUF_ISO_OUT: { + uint16_t connection_handle; + uint16_t iso_data_load_len; + uint8_t *iso_data_load; + uint8_t pb_flag; + uint8_t ts_flag; + + connection_handle = sys_get_le16(hci_buffer) & 0x0FFF; + iso_data_load_len = sys_get_le16(hci_buffer + 2) & 0x3FFF; + pb_flag = (hci_buffer[1] >> 4) & 0x3; + ts_flag = (hci_buffer[1] >> 6) & 0x1; + iso_data_load = &hci_buffer[4]; + hci_tx_iso_data(connection_handle, pb_flag, ts_flag, iso_data_load_len, + iso_data_load); + break; + } +#endif /* CONFIG_BT_ISO */ + case BT_BUF_CMD: + process_command(hci_buffer, buf->len, buffer_out_mem, sizeof(buffer_out_mem)); + send_event(buffer_out_mem, 0, 0); + break; + default: + LOG_ERR("Unsupported type"); + return -EINVAL; + } + net_buf_unref(buf); + + return ret; +} + +static int bt_hci_stm32wb0_open(const struct device *dev, bt_hci_recv_t recv) +{ + struct hci_data *data = dev->data; + RADIO_TIMER_InitTypeDef VTIMER_InitStruct = {MAX_HS_STARTUP_TIME, 0, 0}; + RADIO_HandleTypeDef hradio = {0}; + BLE_STACK_InitTypeDef BLE_STACK_InitParams = { + .BLEStartRamAddress = (uint8_t *)dyn_alloc_a, + .TotalBufferSize = BLE_DYN_ALLOC_SIZE, + .NumAttrRecords = CFG_BLE_NUM_GATT_ATTRIBUTES, + .MaxNumOfClientProcs = CFG_BLE_NUM_OF_CONCURRENT_GATT_CLIENT_PROC, + .NumOfRadioTasks = CFG_BLE_NUM_RADIO_TASKS, + .NumOfEATTChannels = CFG_BLE_NUM_EATT_CHANNELS, + .NumBlockCount = CFG_BLE_MBLOCKS_COUNT, + .ATT_MTU = CFG_BLE_ATT_MTU_MAX, + .MaxConnEventLength = CFG_BLE_CONN_EVENT_LENGTH_MAX, + .SleepClockAccuracy = CFG_BLE_SLEEP_CLOCK_ACCURACY, + .NumOfAdvDataSet = CFG_BLE_NUM_ADV_SETS, + .NumOfSubeventsPAwR = CFG_BLE_NUM_PAWR_SUBEVENTS, + .MaxPAwRSubeventDataCount = CFG_BLE_PAWR_SUBEVENT_DATA_COUNT_MAX, + .NumOfAuxScanSlots = CFG_BLE_NUM_AUX_SCAN_SLOTS, + .FilterAcceptListSizeLog2 = CFG_BLE_FILTER_ACCEPT_LIST_SIZE_LOG2, + .L2CAP_MPS = CFG_BLE_COC_MPS_MAX, + .L2CAP_NumChannels = CFG_BLE_COC_NBR_MAX, + .NumOfSyncSlots = CFG_BLE_NUM_SYNC_SLOTS, + .CTE_MaxNumAntennaIDs = CFG_BLE_NUM_CTE_ANTENNA_IDS_MAX, + .CTE_MaxNumIQSamples = CFG_BLE_NUM_CTE_IQ_SAMPLES_MAX, + .NumOfSyncBIG = CFG_BLE_NUM_SYNC_BIG_MAX, + .NumOfBrcBIG = CFG_BLE_NUM_BRC_BIG_MAX, + .NumOfSyncBIS = CFG_BLE_NUM_SYNC_BIS_MAX, + .NumOfBrcBIS = CFG_BLE_NUM_BRC_BIS_MAX, + .NumOfCIG = CFG_BLE_NUM_CIG_MAX, + .NumOfCIS = CFG_BLE_NUM_CIS_MAX, + .isr0_fifo_size = CFG_BLE_ISR0_FIFO_SIZE, + .isr1_fifo_size = CFG_BLE_ISR1_FIFO_SIZE, + .user_fifo_size = CFG_BLE_USER_FIFO_SIZE + }; + + ble_isr_installer(); + hradio.Instance = RADIO; + HAL_RADIO_Init(&hradio); + HAL_RADIO_TIMER_Init(&VTIMER_InitStruct); + + HW_RNG_Init(); + HW_AES_Init(); + hpka.Instance = PKA; + HAL_PKA_Init(&hpka); + HW_PKA_Init(); + if (BLE_STACK_Init(&BLE_STACK_InitParams)) { + LOG_ERR("BLE Init Failed...."); + return -EIO; + } + +#if CONFIG_BT_EXT_ADV + dm_init(CFG_BLE_ADV_NWK_BUFFER_SIZE, aci_adv_nwk_buffer); +#endif /* CONFIG_BT_EXT_ADV */ + + aci_adv_nwk_init(); + + data->recv = recv; + k_work_init_delayable(&hal_radio_timer_work, vtimer_process); + k_work_init_delayable(&ble_stack_work, blestack_process); + k_work_schedule(&ble_stack_work, K_NO_WAIT); + + return 0; +} + +static const struct bt_hci_driver_api drv = { + .open = bt_hci_stm32wb0_open, + .send = bt_hci_stm32wb0_send, +}; + +#define HCI_DEVICE_INIT(inst) \ + static struct hci_data hci_data_##inst = { \ + }; \ + DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &hci_data_##inst, NULL, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &drv) + +/* Only one instance supported */ +HCI_DEVICE_INIT(0)