From cf6694589e8b2dc40e04a24c09bdc5266b89f980 Mon Sep 17 00:00:00 2001 From: Ali Hozhabri Date: Fri, 5 Jan 2024 17:59:33 +0100 Subject: [PATCH 1/5] dts: bindings: bluetooth: Include zephyr,bt-hci-spi.yaml Include zephyr,bt-hci-spi.yaml in both ST HCI SPI yaml files. Signed-off-by: Ali Hozhabri --- dts/bindings/bluetooth/st,hci-spi-v1.yaml | 2 ++ dts/bindings/bluetooth/st,hci-spi-v2.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dts/bindings/bluetooth/st,hci-spi-v1.yaml b/dts/bindings/bluetooth/st,hci-spi-v1.yaml index d61b936c5b64..3784c88ded04 100644 --- a/dts/bindings/bluetooth/st,hci-spi-v1.yaml +++ b/dts/bindings/bluetooth/st,hci-spi-v1.yaml @@ -4,3 +4,5 @@ description: STMicroelectronics SPI protocol V1 compatible with BlueNRG-MS devices compatible: "st,hci-spi-v1" + +include: zephyr,bt-hci-spi.yaml diff --git a/dts/bindings/bluetooth/st,hci-spi-v2.yaml b/dts/bindings/bluetooth/st,hci-spi-v2.yaml index 58e91e496e21..36b25eae7689 100644 --- a/dts/bindings/bluetooth/st,hci-spi-v2.yaml +++ b/dts/bindings/bluetooth/st,hci-spi-v2.yaml @@ -4,3 +4,5 @@ description: STMicroelectronics SPI protocol V2 compatible with BlueNRG-1 and successor devices compatible: "st,hci-spi-v2" + +include: zephyr,bt-hci-spi.yaml From 42d11c743d56a543df49caacc7bfc9688025624d Mon Sep 17 00:00:00 2001 From: Ali Hozhabri Date: Fri, 5 Jan 2024 18:08:16 +0100 Subject: [PATCH 2/5] drivers: bluetooth: hci: Add Bluetooth driver for ST HCI SPI protocol Copy ST specific SPI protocol code from spi.c to a vendor specific file. Signed-off-by: Ali Hozhabri --- drivers/bluetooth/hci/hci_spi_st.c | 594 +++++++++++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 drivers/bluetooth/hci/hci_spi_st.c diff --git a/drivers/bluetooth/hci/hci_spi_st.c b/drivers/bluetooth/hci/hci_spi_st.c new file mode 100644 index 000000000000..605e36e56a62 --- /dev/null +++ b/drivers/bluetooth/hci/hci_spi_st.c @@ -0,0 +1,594 @@ +/* hci_spi_st.c - STMicroelectronics HCI SPI Bluetooth driver */ + +/* + * Copyright (c) 2017 Linaro Ltd. + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_DT_HAS_ST_HCI_SPI_V1_ENABLED) +#define DT_DRV_COMPAT st_hci_spi_v1 + +#elif defined(CONFIG_DT_HAS_ST_HCI_SPI_V2_ENABLED) +#define DT_DRV_COMPAT st_hci_spi_v2 + +#endif /* CONFIG_DT_HAS_ST_HCI_SPI_V1_ENABLED */ + +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_LEVEL CONFIG_BT_HCI_DRIVER_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_driver); + +#define HCI_CMD 0x01 +#define HCI_ACL 0x02 +#define HCI_SCO 0x03 +#define HCI_EVT 0x04 + +/* Special Values */ +#define SPI_WRITE 0x0A +#define SPI_READ 0x0B +#define READY_NOW 0x02 + +#define EVT_BLUE_INITIALIZED 0x01 + +/* Offsets */ +#define STATUS_HEADER_READY 0 +#define STATUS_HEADER_TOREAD 3 +#define STATUS_HEADER_TOWRITE 1 + +#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 + +#define CMD_OGF 1 +#define CMD_OCF 2 + +#define SPI_MAX_MSG_LEN 255 + +/* Single byte header denoting the buffer type */ +#define H4_HDR_SIZE 1 + +/* Maximum L2CAP MTU that can fit in a single packet */ +#define MAX_MTU (SPI_MAX_MSG_LEN - H4_HDR_SIZE - BT_L2CAP_HDR_SIZE - BT_HCI_ACL_HDR_SIZE) + +#if CONFIG_BT_L2CAP_TX_MTU > MAX_MTU +#warning CONFIG_BT_L2CAP_TX_MTU is too large and can result in packets that cannot \ + be transmitted across this HCI link +#endif /* CONFIG_BT_L2CAP_TX_MTU > MAX_MTU */ + +static uint8_t rxmsg[SPI_MAX_MSG_LEN]; +static uint8_t txmsg[SPI_MAX_MSG_LEN]; + +static const struct gpio_dt_spec irq_gpio = GPIO_DT_SPEC_INST_GET(0, irq_gpios); +static const struct gpio_dt_spec rst_gpio = GPIO_DT_SPEC_INST_GET(0, reset_gpios); + +static struct gpio_callback gpio_cb; + +static K_SEM_DEFINE(sem_initialised, 0, 1); +static K_SEM_DEFINE(sem_request, 0, 1); +static K_SEM_DEFINE(sem_busy, 1, 1); + +static K_KERNEL_STACK_DEFINE(spi_rx_stack, CONFIG_BT_DRV_RX_STACK_SIZE); +static struct k_thread spi_rx_thread_data; + +#if defined(CONFIG_BT_BLUENRG_ACI) +#define BLUENRG_ACI_WRITE_CONFIG_DATA BT_OP(BT_OGF_VS, 0x000C) +#define BLUENRG_CONFIG_PUBADDR_OFFSET 0x00 +#define BLUENRG_CONFIG_PUBADDR_LEN 0x06 +#define BLUENRG_CONFIG_LL_ONLY_OFFSET 0x2C +#define BLUENRG_CONFIG_LL_ONLY_LEN 0x01 + +static int bt_spi_send_aci_config(uint8_t offset, const uint8_t *value, size_t value_len); +#endif /* CONFIG_BT_BLUENRG_ACI */ + +static const struct spi_dt_spec bus = SPI_DT_SPEC_INST_GET( + 0, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0); + +static struct spi_buf spi_tx_buf; +static struct spi_buf spi_rx_buf; +static const struct spi_buf_set spi_tx = { + .buffers = &spi_tx_buf, + .count = 1 +}; +static const struct spi_buf_set spi_rx = { + .buffers = &spi_rx_buf, + .count = 1 +}; + +static inline int bt_spi_transceive(void *tx, uint32_t tx_len, + void *rx, uint32_t rx_len) +{ + spi_tx_buf.buf = tx; + spi_tx_buf.len = (size_t)tx_len; + spi_rx_buf.buf = rx; + spi_rx_buf.len = (size_t)rx_len; + return spi_transceive_dt(&bus, &spi_tx, &spi_rx); +} + +static inline uint16_t bt_spi_get_cmd(uint8_t *msg) +{ + return (msg[CMD_OCF] << 8) | msg[CMD_OGF]; +} + +static inline uint16_t bt_spi_get_evt(uint8_t *msg) +{ + return (msg[EVT_VENDOR_CODE_MSB] << 8) | msg[EVT_VENDOR_CODE_LSB]; +} + +static void bt_spi_isr(const struct device *unused1, + struct gpio_callback *unused2, + uint32_t unused3) +{ + LOG_DBG(""); + + k_sem_give(&sem_request); +} + +static bool bt_spi_handle_vendor_evt(uint8_t *msg) +{ + bool handled = false; + + switch (bt_spi_get_evt(msg)) { + case EVT_BLUE_INITIALIZED: { + k_sem_give(&sem_initialised); +#if defined(CONFIG_BT_BLUENRG_ACI) + /* force BlueNRG to be on controller mode */ + uint8_t data = 1; + + bt_spi_send_aci_config(BLUENRG_CONFIG_LL_ONLY_OFFSET, &data, 1); +#endif + handled = true; + } + default: + break; + } + return handled; +} + +#define IS_IRQ_HIGH gpio_pin_get_dt(&irq_gpio) + +#if DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) + +/* Define a limit when reading IRQ high */ +#define IRQ_HIGH_MAX_READ 15 + +/* On BlueNRG-MS, host is expected to read */ +/* as long as IRQ pin is high */ +#define READ_CONDITION IS_IRQ_HIGH + +static void assert_cs(void) +{ + gpio_pin_set_dt(&bus.config.cs.gpio, 0); + gpio_pin_set_dt(&bus.config.cs.gpio, 1); +} + +static void release_cs(bool data_transaction) +{ + ARG_UNUSED(data_transaction); + gpio_pin_set_dt(&bus.config.cs.gpio, 0); +} + +static int bt_spi_get_header(uint8_t op, uint16_t *size) +{ + uint8_t header_master[5] = {op, 0, 0, 0, 0}; + uint8_t header_slave[5]; + uint8_t size_offset, attempts; + int ret; + + if (op == SPI_READ) { + if (!IS_IRQ_HIGH) { + *size = 0; + return 0; + } + size_offset = STATUS_HEADER_TOREAD; + } else if (op == SPI_WRITE) { + size_offset = STATUS_HEADER_TOWRITE; + } else { + return -EINVAL; + } + attempts = IRQ_HIGH_MAX_READ; + do { + if (op == SPI_READ) { + /* Keep checking that IRQ is still high, if we need to read */ + if (!IS_IRQ_HIGH) { + *size = 0; + return 0; + } + } + assert_cs(); + ret = bt_spi_transceive(header_master, 5, header_slave, 5); + if (ret) { + /* SPI transaction failed */ + break; + } + + *size = (header_slave[STATUS_HEADER_READY] == READY_NOW) ? + header_slave[size_offset] : 0; + attempts--; + } while ((*size == 0) && attempts); + + return ret; +} + +#elif DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2) + +#define READ_CONDITION false + +static void assert_cs(uint16_t delay) +{ + gpio_pin_set_dt(&bus.config.cs.gpio, 0); + if (delay) { + k_sleep(K_USEC(delay)); + } + gpio_pin_set_dt(&bus.config.cs.gpio, 1); + gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_DISABLE); +} + +static void release_cs(bool data_transaction) +{ + /* Consume possible event signals */ + while (k_sem_take(&sem_request, K_NO_WAIT) == 0) { + } + if (data_transaction) { + /* Wait for IRQ to become low only when data phase has been performed */ + while (IS_IRQ_HIGH) { + } + } + gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + gpio_pin_set_dt(&bus.config.cs.gpio, 0); +} + +static int bt_spi_get_header(uint8_t op, uint16_t *size) +{ + uint8_t header_master[5] = {op, 0, 0, 0, 0}; + uint8_t header_slave[5]; + uint16_t cs_delay; + uint8_t size_offset; + int ret; + + if (op == SPI_READ) { + if (!IS_IRQ_HIGH) { + *size = 0; + return 0; + } + cs_delay = 0; + size_offset = STATUS_HEADER_TOREAD; + } else if (op == SPI_WRITE) { + /* To make sure we have a minimum delay from previous release cs */ + cs_delay = 100; + size_offset = STATUS_HEADER_TOWRITE; + } else { + return -EINVAL; + } + + assert_cs(cs_delay); + /* Wait up to a maximum time of 100 ms */ + if (!WAIT_FOR(IS_IRQ_HIGH, 100000, k_usleep(100))) { + LOG_ERR("IRQ pin did not raise"); + return -EIO; + } + + ret = bt_spi_transceive(header_master, 5, header_slave, 5); + *size = header_slave[size_offset] | (header_slave[size_offset + 1] << 8); + return ret; +} +#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) */ + +#if defined(CONFIG_BT_BLUENRG_ACI) +static int bt_spi_send_aci_config(uint8_t offset, const uint8_t *value, size_t value_len) +{ + struct net_buf *buf; + uint8_t *cmd_data; + size_t data_len = 2 + value_len; + + buf = bt_hci_cmd_create(BLUENRG_ACI_WRITE_CONFIG_DATA, data_len); + if (!buf) { + return -ENOBUFS; + } + + cmd_data = net_buf_add(buf, data_len); + cmd_data[0] = offset; + cmd_data[1] = value_len; + memcpy(&cmd_data[2], value, value_len); + + return bt_hci_cmd_send(BLUENRG_ACI_WRITE_CONFIG_DATA, buf); +} + +static int bt_spi_bluenrg_setup(const struct bt_hci_setup_params *params) +{ + int ret; + const bt_addr_t *addr = ¶ms->public_addr; + + if (!bt_addr_eq(addr, BT_ADDR_NONE) && !bt_addr_eq(addr, BT_ADDR_ANY)) { + ret = bt_spi_send_aci_config( + BLUENRG_CONFIG_PUBADDR_OFFSET, + addr->val, sizeof(addr->val)); + + if (ret != 0) { + LOG_ERR("Failed to set BlueNRG public address (%d)", ret); + return ret; + } + } + + return 0; +} +#endif /* CONFIG_BT_BLUENRG_ACI */ + +static struct net_buf *bt_spi_rx_buf_construct(uint8_t *msg) +{ + bool discardable = false; + k_timeout_t timeout = K_FOREVER; + struct bt_hci_acl_hdr acl_hdr; + struct net_buf *buf; + int len; + + switch (msg[PACKET_TYPE]) { + case HCI_EVT: + switch (msg[EVT_HEADER_EVENT]) { + case BT_HCI_EVT_VENDOR: + /* Run event through interface handler */ + if (bt_spi_handle_vendor_evt(msg)) { + return NULL; + } + /* Event has not yet been handled */ + __fallthrough; + default: + 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 HCI_ACL: + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER); + 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; + default: + LOG_ERR("Unknown BT buf type %d", msg[0]); + return NULL; + } + + return buf; +} + +static void bt_spi_rx_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct net_buf *buf; + uint16_t size = 0U; + int ret; + + (void)memset(&txmsg, 0xFF, SPI_MAX_MSG_LEN); + while (true) { + + /* Wait for interrupt pin to be active */ + k_sem_take(&sem_request, K_FOREVER); + + LOG_DBG(""); + + do { + /* Wait for SPI bus to be available */ + k_sem_take(&sem_busy, K_FOREVER); + ret = bt_spi_get_header(SPI_READ, &size); + + /* Read data */ + if (ret == 0 && size != 0) { + ret = bt_spi_transceive(&txmsg, size, &rxmsg, size); + } + + release_cs(size > 0); + + k_sem_give(&sem_busy); + + if (ret || size == 0) { + if (ret) { + LOG_ERR("Error %d", ret); + } + continue; + } + + LOG_HEXDUMP_DBG(rxmsg, size, "SPI RX"); + + /* Construct net_buf from SPI data */ + buf = bt_spi_rx_buf_construct(rxmsg); + if (buf) { + /* Handle the received HCI data */ + bt_recv(buf); + } + } while (READ_CONDITION); + } +} + +static int bt_spi_send(struct net_buf *buf) +{ + uint16_t size; + uint8_t rx_first[1]; + int ret; + + LOG_DBG(""); + + /* Buffer needs an additional byte for type */ + if (buf->len >= SPI_MAX_MSG_LEN) { + LOG_ERR("Message too long (%d)", buf->len); + return -EINVAL; + } + + switch (bt_buf_get_type(buf)) { + case BT_BUF_ACL_OUT: + net_buf_push_u8(buf, HCI_ACL); + break; + case BT_BUF_CMD: + net_buf_push_u8(buf, HCI_CMD); + break; + default: + LOG_ERR("Unsupported type"); + return -EINVAL; + } + + /* Wait for SPI bus to be available */ + k_sem_take(&sem_busy, K_FOREVER); + + ret = bt_spi_get_header(SPI_WRITE, &size); + size = MIN(buf->len, size); + + if (size < buf->len) { + LOG_WRN("Unable to write full data, skipping"); + size = 0; + ret = -ECANCELED; + } + + if (!ret) { + /* Transmit the message */ + ret = bt_spi_transceive(buf->data, size, + rx_first, 1); + } + + release_cs(size > 0); + + k_sem_give(&sem_busy); + + if (ret) { + LOG_ERR("Error %d", ret); + return ret; + } + + LOG_HEXDUMP_DBG(buf->data, buf->len, "SPI TX"); + +#if (DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) || DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2)) + /* + * Since a RESET has been requested, the chip will now restart. + * Unfortunately the BlueNRG will reply with "reset received" but + * since it does not send back a NOP, we have no way to tell when the + * RESET has actually taken place. Instead, we use the vendor command + * EVT_BLUE_INITIALIZED as an indication that it is safe to proceed. + */ + if (bt_spi_get_cmd(buf->data) == BT_HCI_OP_RESET) { + k_sem_take(&sem_initialised, K_FOREVER); + } +#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) || DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2) */ + net_buf_unref(buf); + + return ret; +} + +static int bt_spi_open(void) +{ + int err; + + /* Configure RST pin and hold BLE in Reset */ + err = gpio_pin_configure_dt(&rst_gpio, GPIO_OUTPUT_ACTIVE); + if (err) { + return err; + } + + /* Configure IRQ pin and the IRQ call-back/handler */ + err = gpio_pin_configure_dt(&irq_gpio, GPIO_INPUT); + if (err) { + return err; + } + + gpio_init_callback(&gpio_cb, bt_spi_isr, BIT(irq_gpio.pin)); + err = gpio_add_callback(irq_gpio.port, &gpio_cb); + if (err) { + return err; + } + + /* Enable the interrupt line */ + err = gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (err) { + return err; + } + + /* Take BLE out of reset */ + k_sleep(K_MSEC(DT_INST_PROP_OR(0, reset_assert_duration_ms, 0))); + gpio_pin_set_dt(&rst_gpio, 0); + + /* Start RX thread */ + k_thread_create(&spi_rx_thread_data, spi_rx_stack, + K_KERNEL_STACK_SIZEOF(spi_rx_stack), + bt_spi_rx_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_DRIVER_RX_HIGH_PRIO), + 0, K_NO_WAIT); + + /* Device will let us know when it's ready */ + k_sem_take(&sem_initialised, K_FOREVER); + + return 0; +} + +static const struct bt_hci_driver drv = { + .name = DEVICE_DT_NAME(DT_DRV_INST(0)), + .bus = BT_HCI_DRIVER_BUS_SPI, +#if defined(CONFIG_BT_BLUENRG_ACI) + .quirks = BT_QUIRK_NO_RESET, + .setup = bt_spi_bluenrg_setup, +#endif /* CONFIG_BT_BLUENRG_ACI */ + .open = bt_spi_open, + .send = bt_spi_send, +}; + +static int bt_spi_init(void) +{ + + if (!spi_is_ready_dt(&bus)) { + LOG_ERR("SPI device not ready"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&irq_gpio)) { + LOG_ERR("IRQ GPIO device not ready"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&rst_gpio)) { + LOG_ERR("Reset GPIO device not ready"); + return -ENODEV; + } + + bt_hci_driver_register(&drv); + + + LOG_DBG("BT SPI initialized"); + + return 0; +} + +SYS_INIT(bt_spi_init, POST_KERNEL, CONFIG_BT_SPI_INIT_PRIORITY); From cb49540c922c2712c6a7d98b0b0b2a99a968231f Mon Sep 17 00:00:00 2001 From: Ali Hozhabri Date: Fri, 5 Jan 2024 18:11:05 +0100 Subject: [PATCH 3/5] drivers: bluetooth: hci: Remove ST vendor code from spi.c Remove ST vendor code from spi.c. Update CMakeLists to select ST vendor file for ST BlueNRG devices. Signed-off-by: Ali Hozhabri --- drivers/bluetooth/hci/CMakeLists.txt | 8 +- drivers/bluetooth/hci/spi.c | 283 ++++----------------------- 2 files changed, 40 insertions(+), 251 deletions(-) diff --git a/drivers/bluetooth/hci/CMakeLists.txt b/drivers/bluetooth/hci/CMakeLists.txt index e20706c494fe..adda6ea0e2ab 100644 --- a/drivers/bluetooth/hci/CMakeLists.txt +++ b/drivers/bluetooth/hci/CMakeLists.txt @@ -19,7 +19,13 @@ zephyr_library_sources_ifdef(CONFIG_BT_ESP32 hci_esp32.c) zephyr_library_sources_ifdef(CONFIG_BT_H4 h4.c) zephyr_library_sources_ifdef(CONFIG_BT_H5 h5.c) zephyr_library_sources_ifdef(CONFIG_BT_HCI_IPC ipc.c) -zephyr_library_sources_ifdef(CONFIG_BT_SPI spi.c) +if(CONFIG_BT_SPI) + if ((CONFIG_DT_HAS_ST_HCI_SPI_V1_ENABLED) OR (CONFIG_DT_HAS_ST_HCI_SPI_V2_ENABLED)) + zephyr_library_sources(hci_spi_st.c) + else() + zephyr_library_sources(spi.c) + endif() +endif() zephyr_library_sources_ifdef(CONFIG_BT_STM32_IPM ipm_stm32wb.c) zephyr_library_sources_ifdef(CONFIG_BT_STM32WBA hci_stm32wba.c) zephyr_library_sources_ifdef(CONFIG_BT_USERCHAN userchan.c) diff --git a/drivers/bluetooth/hci/spi.c b/drivers/bluetooth/hci/spi.c index afad8fcf2ba1..5c2695435e4b 100644 --- a/drivers/bluetooth/hci/spi.c +++ b/drivers/bluetooth/hci/spi.c @@ -86,16 +86,6 @@ static K_SEM_DEFINE(sem_busy, 1, 1); static K_KERNEL_STACK_DEFINE(spi_rx_stack, CONFIG_BT_DRV_RX_STACK_SIZE); static struct k_thread spi_rx_thread_data; -#if defined(CONFIG_BT_BLUENRG_ACI) -#define BLUENRG_ACI_WRITE_CONFIG_DATA BT_OP(BT_OGF_VS, 0x000C) -#define BLUENRG_CONFIG_PUBADDR_OFFSET 0x00 -#define BLUENRG_CONFIG_PUBADDR_LEN 0x06 -#define BLUENRG_CONFIG_LL_ONLY_OFFSET 0x2C -#define BLUENRG_CONFIG_LL_ONLY_LEN 0x01 - -static int bt_spi_send_aci_config(uint8_t offset, const uint8_t *value, size_t value_len); -#endif /* CONFIG_BT_BLUENRG_ACI */ - static const struct spi_dt_spec bus = SPI_DT_SPEC_INST_GET( 0, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0); @@ -146,12 +136,6 @@ static bool bt_spi_handle_vendor_evt(uint8_t *msg) switch (bt_spi_get_evt(msg)) { case EVT_BLUE_INITIALIZED: { k_sem_give(&sem_initialised); -#if defined(CONFIG_BT_BLUENRG_ACI) - /* force BlueNRG to be on controller mode */ - uint8_t data = 1; - - bt_spi_send_aci_config(BLUENRG_CONFIG_LL_ONLY_OFFSET, &data, 1); -#endif handled = true; } default: @@ -160,144 +144,6 @@ static bool bt_spi_handle_vendor_evt(uint8_t *msg) return handled; } -#define IS_IRQ_HIGH gpio_pin_get_dt(&irq_gpio) - -#if DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) -/* Define a limit when reading IRQ high */ -/* It can be required to be increased for */ -/* some particular cases. */ -#define IRQ_HIGH_MAX_READ 3 -/* On BlueNRG-MS, host is expected to read */ -/* as long as IRQ pin is high */ -#define READ_CONDITION IS_IRQ_HIGH -/* We cannot retry write data without reading again the header */ -#define WRITE_DATA_CONDITION(...) true - -static void assert_cs(void) -{ - gpio_pin_set_dt(&bus.config.cs.gpio, 0); - gpio_pin_set_dt(&bus.config.cs.gpio, 1); -} - -static void release_cs(bool data_transaction) -{ - ARG_UNUSED(data_transaction); - gpio_pin_set_dt(&bus.config.cs.gpio, 0); -} - -static int bt_spi_get_header(uint8_t op, uint16_t *size) -{ - uint8_t header_master[5] = {op, 0, 0, 0, 0}; - uint8_t header_slave[5]; - uint8_t size_offset, attempts; - int ret; - - if (op == SPI_READ) { - if (!IS_IRQ_HIGH) { - *size = 0; - return 0; - } - size_offset = STATUS_HEADER_TOREAD; - } else if (op == SPI_WRITE) { - size_offset = STATUS_HEADER_TOWRITE; - } else { - return -EINVAL; - } - attempts = IRQ_HIGH_MAX_READ; - do { - if (op == SPI_READ) { - /* Keep checking that IRQ is still high, if we need to read */ - if (!IS_IRQ_HIGH) { - *size = 0; - return 0; - } - } - assert_cs(); - ret = bt_spi_transceive(header_master, 5, header_slave, 5); - if (ret) { - /* SPI transaction failed */ - break; - } - - *size = (header_slave[STATUS_HEADER_READY] == READY_NOW) ? - header_slave[size_offset] : 0; - attempts--; - } while ((*size == 0) && attempts); - - return ret; -} - -#elif DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2) - -#define READ_CONDITION false -/* We cannot retry writing data without reading the header again */ -#define WRITE_DATA_CONDITION(...) true - -static void assert_cs(uint16_t delay) -{ - gpio_pin_set_dt(&bus.config.cs.gpio, 0); - if (delay) { - k_sleep(K_USEC(delay)); - } - gpio_pin_set_dt(&bus.config.cs.gpio, 1); - gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_DISABLE); -} - -static void release_cs(bool data_transaction) -{ - /* Consume possible event signals */ - while (k_sem_take(&sem_request, K_NO_WAIT) == 0) { - } - if (data_transaction) { - /* Wait for IRQ to become low only when data phase has been performed */ - while (IS_IRQ_HIGH) { - } - } - gpio_pin_interrupt_configure_dt(&irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); - gpio_pin_set_dt(&bus.config.cs.gpio, 0); -} - -static int bt_spi_get_header(uint8_t op, uint16_t *size) -{ - uint8_t header_master[5] = {op, 0, 0, 0, 0}; - uint8_t header_slave[5]; - uint16_t cs_delay; - uint8_t size_offset; - int ret; - - if (op == SPI_READ) { - if (!IS_IRQ_HIGH) { - *size = 0; - return 0; - } - cs_delay = 0; - size_offset = STATUS_HEADER_TOREAD; - } else if (op == SPI_WRITE) { - /* To make sure we have a minimum delay from previous release cs */ - cs_delay = 100; - size_offset = STATUS_HEADER_TOWRITE; - } else { - return -EINVAL; - } - - assert_cs(cs_delay); - /* Wait up to a maximum time of 100 ms */ - if (!WAIT_FOR(IS_IRQ_HIGH, 100000, k_usleep(100))) { - LOG_ERR("IRQ pin did not raise"); - return -EIO; - } - - ret = bt_spi_transceive(header_master, 5, header_slave, 5); - *size = header_slave[size_offset] | (header_slave[size_offset + 1] << 8); - return ret; -} -/* Other Boards */ -#else - -#define release_cs(...) -#define READ_CONDITION false -#define WRITE_DATA_CONDITION(ret, rx_first) (rx_first != 0U || ret) - static int bt_spi_get_header(uint8_t op, uint16_t *size) { uint8_t header_master[5] = {op, 0, 0, 0, 0}; @@ -334,47 +180,6 @@ static int bt_spi_get_header(uint8_t op, uint16_t *size) return ret; } -#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) */ - -#if defined(CONFIG_BT_BLUENRG_ACI) -static int bt_spi_send_aci_config(uint8_t offset, const uint8_t *value, size_t value_len) -{ - struct net_buf *buf; - uint8_t *cmd_data; - size_t data_len = 2 + value_len; - - buf = bt_hci_cmd_create(BLUENRG_ACI_WRITE_CONFIG_DATA, data_len); - if (!buf) { - return -ENOBUFS; - } - - cmd_data = net_buf_add(buf, data_len); - cmd_data[0] = offset; - cmd_data[1] = value_len; - memcpy(&cmd_data[2], value, value_len); - - return bt_hci_cmd_send(BLUENRG_ACI_WRITE_CONFIG_DATA, buf); -} - -static int bt_spi_bluenrg_setup(const struct bt_hci_setup_params *params) -{ - int ret; - const bt_addr_t *addr = ¶ms->public_addr; - - if (!bt_addr_eq(addr, BT_ADDR_NONE) && !bt_addr_eq(addr, BT_ADDR_ANY)) { - ret = bt_spi_send_aci_config( - BLUENRG_CONFIG_PUBADDR_OFFSET, - addr->val, sizeof(addr->val)); - - if (ret != 0) { - LOG_ERR("Failed to set BlueNRG public address (%d)", ret); - return ret; - } - } - - return 0; -} -#endif /* CONFIG_BT_BLUENRG_ACI */ static struct net_buf *bt_spi_rx_buf_construct(uint8_t *msg) { @@ -453,48 +258,44 @@ static void bt_spi_rx_thread(void *p1, void *p2, void *p3) LOG_DBG(""); - do { - /* Wait for SPI bus to be available */ - k_sem_take(&sem_busy, K_FOREVER); - ret = bt_spi_get_header(SPI_READ, &size); - - /* Delay here is rounded up to next tick */ - k_sleep(K_USEC(DATA_DELAY_US)); - /* Read data */ - if (ret == 0 && size != 0) { - do { - ret = bt_spi_transceive(&txmsg, size, - &rxmsg, size); - if (rxmsg[0] == 0U) { - /* Consider increasing controller-data-delay-us - * if this message is extremely common. - */ - LOG_DBG("Controller not ready for SPI transaction " - "of %d bytes", size); - } - } while (rxmsg[0] == 0U && ret == 0); - } + /* Wait for SPI bus to be available */ + k_sem_take(&sem_busy, K_FOREVER); + ret = bt_spi_get_header(SPI_READ, &size); - release_cs(size > 0); + /* Delay here is rounded up to next tick */ + k_sleep(K_USEC(DATA_DELAY_US)); + /* Read data */ + if (ret == 0 && size != 0) { + do { + ret = bt_spi_transceive(&txmsg, size, + &rxmsg, size); + if (rxmsg[0] == 0U) { + /* Consider increasing controller-data-delay-us + * if this message is extremely common. + */ + LOG_DBG("Controller not ready for SPI transaction " + "of %d bytes", size); + } + } while (rxmsg[0] == 0U && ret == 0); + } - k_sem_give(&sem_busy); + k_sem_give(&sem_busy); - if (ret || size == 0) { - if (ret) { - LOG_ERR("Error %d", ret); - } - continue; + if (ret || size == 0) { + if (ret) { + LOG_ERR("Error %d", ret); } + continue; + } - LOG_HEXDUMP_DBG(rxmsg, size, "SPI RX"); + LOG_HEXDUMP_DBG(rxmsg, size, "SPI RX"); - /* Construct net_buf from SPI data */ - buf = bt_spi_rx_buf_construct(rxmsg); - if (buf) { - /* Handle the received HCI data */ - bt_recv(buf); - } - } while (READ_CONDITION); + /* Construct net_buf from SPI data */ + buf = bt_spi_rx_buf_construct(rxmsg); + if (buf) { + /* Handle the received HCI data */ + bt_recv(buf); + } } } @@ -544,7 +345,7 @@ static int bt_spi_send(struct net_buf *buf) while (true) { ret = bt_spi_transceive(buf->data, size, rx_first, 1); - if (WRITE_DATA_CONDITION(ret, rx_first[0])) { + if (rx_first[0] != 0U || ret) { break; } /* Consider increasing controller-data-delay-us @@ -554,8 +355,6 @@ static int bt_spi_send(struct net_buf *buf) } } - release_cs(size > 0); - k_sem_give(&sem_busy); if (ret) { @@ -565,18 +364,6 @@ static int bt_spi_send(struct net_buf *buf) LOG_HEXDUMP_DBG(buf->data, buf->len, "SPI TX"); -#if (DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) || DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2)) - /* - * Since a RESET has been requested, the chip will now restart. - * Unfortunately the BlueNRG will reply with "reset received" but - * since it does not send back a NOP, we have no way to tell when the - * RESET has actually taken place. Instead, we use the vendor command - * EVT_BLUE_INITIALIZED as an indication that it is safe to proceed. - */ - if (bt_spi_get_cmd(buf->data) == BT_HCI_OP_RESET) { - k_sem_take(&sem_initialised, K_FOREVER); - } -#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v1) || DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2) */ out: net_buf_unref(buf); @@ -631,10 +418,6 @@ static int bt_spi_open(void) static const struct bt_hci_driver drv = { .name = DEVICE_DT_NAME(DT_DRV_INST(0)), .bus = BT_HCI_DRIVER_BUS_SPI, -#if defined(CONFIG_BT_BLUENRG_ACI) - .quirks = BT_QUIRK_NO_RESET, - .setup = bt_spi_bluenrg_setup, -#endif /* CONFIG_BT_BLUENRG_ACI */ .open = bt_spi_open, .send = bt_spi_send, }; From 8cc2887a5206feacdd4119c97dab9ea839496303 Mon Sep 17 00:00:00 2001 From: Ali Hozhabri Date: Fri, 5 Jan 2024 18:19:20 +0100 Subject: [PATCH 4/5] boards: arm: Update dts files for ST BlueNRG-based boards Update dts files to use ST vendor specific HCI SPI Bluetooth driver. Remove unnecessary GPIO bias for output pins. Signed-off-by: Ali Hozhabri --- boards/arm/b_l4s5i_iot01a/b_l4s5i_iot01a.dts | 9 ++++----- boards/arm/disco_l475_iot1/disco_l475_iot1.dts | 9 ++++----- boards/arm/sensortile_box/sensortile_box.dts | 9 ++++----- boards/arm/sensortile_box_pro/sensortile_box_pro.dts | 2 +- boards/arm/stm32l562e_dk/stm32l562e_dk_common.dtsi | 9 ++++----- boards/shields/x_nucleo_idb05a1/x_nucleo_idb05a1.overlay | 9 ++++----- 6 files changed, 21 insertions(+), 26 deletions(-) diff --git a/boards/arm/b_l4s5i_iot01a/b_l4s5i_iot01a.dts b/boards/arm/b_l4s5i_iot01a/b_l4s5i_iot01a.dts index fe6322cc26b0..c51161814327 100644 --- a/boards/arm/b_l4s5i_iot01a/b_l4s5i_iot01a.dts +++ b/boards/arm/b_l4s5i_iot01a/b_l4s5i_iot01a.dts @@ -148,16 +148,15 @@ pinctrl-names = "default"; status = "okay"; - cs-gpios = <&gpiod 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + cs-gpios = <&gpiod 13 GPIO_ACTIVE_LOW>, <&gpioe 0 GPIO_ACTIVE_LOW>; spbtle-rf@0 { - compatible = "zephyr,bt-hci-spi", "st,hci-spi-v1"; + compatible = "st,hci-spi-v1"; reg = <0>; - reset-gpios = <&gpioa 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + reset-gpios = <&gpioa 8 GPIO_ACTIVE_LOW>; irq-gpios = <&gpioe 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; - spi-max-frequency = <2000000>; - controller-data-delay-us = <0>; /* No need for extra delay for BlueNRG-MS */ + spi-max-frequency = ; spi-hold-cs; }; diff --git a/boards/arm/disco_l475_iot1/disco_l475_iot1.dts b/boards/arm/disco_l475_iot1/disco_l475_iot1.dts index 85e0dcea3c2d..9ae6399e62ea 100644 --- a/boards/arm/disco_l475_iot1/disco_l475_iot1.dts +++ b/boards/arm/disco_l475_iot1/disco_l475_iot1.dts @@ -187,16 +187,15 @@ pinctrl-0 = <&spi3_sck_pc10 &spi3_miso_pc11 &spi3_mosi_pc12>; pinctrl-names = "default"; - cs-gpios = <&gpiod 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + cs-gpios = <&gpiod 13 GPIO_ACTIVE_LOW>, <&gpioe 0 GPIO_ACTIVE_LOW>; spbtle-rf@0 { - compatible = "zephyr,bt-hci-spi", "st,hci-spi-v1"; + compatible = "st,hci-spi-v1"; reg = <0>; - reset-gpios = <&gpioa 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + reset-gpios = <&gpioa 8 GPIO_ACTIVE_LOW>; irq-gpios = <&gpioe 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; - spi-max-frequency = <2000000>; - controller-data-delay-us = <0>; /* No need for extra delay for BlueNRG-MS */ + spi-max-frequency = ; spi-hold-cs; }; diff --git a/boards/arm/sensortile_box/sensortile_box.dts b/boards/arm/sensortile_box/sensortile_box.dts index b86220208405..4c5a9654b3d6 100644 --- a/boards/arm/sensortile_box/sensortile_box.dts +++ b/boards/arm/sensortile_box/sensortile_box.dts @@ -165,16 +165,15 @@ pinctrl-0 = <&spi2_sck_pd1 &spi2_miso_pd3 &spi2_mosi_pc3>; pinctrl-names = "default"; status = "okay"; - cs-gpios = <&gpiod 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + cs-gpios = <&gpiod 0 GPIO_ACTIVE_LOW>; spbtle_1s_sensortile_box: spbtle-1s@0 { - compatible = "zephyr,bt-hci-spi", "st,hci-spi-v2"; + compatible = "st,hci-spi-v2"; reg = <0>; - reset-gpios = <&gpioa 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + reset-gpios = <&gpioa 8 GPIO_ACTIVE_LOW>; irq-gpios = <&gpiod 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; - spi-max-frequency = <1000000>; + spi-max-frequency = ; spi-cpha; spi-hold-cs; - controller-data-delay-us = <0>; reset-assert-duration-ms = <6>; }; }; diff --git a/boards/arm/sensortile_box_pro/sensortile_box_pro.dts b/boards/arm/sensortile_box_pro/sensortile_box_pro.dts index 0a5be03e4a97..8c159eb831fe 100644 --- a/boards/arm/sensortile_box_pro/sensortile_box_pro.dts +++ b/boards/arm/sensortile_box_pro/sensortile_box_pro.dts @@ -164,7 +164,7 @@ stm32_lp_tick_source: &lptim1 { status = "okay"; bluenrg-lp@0 { - compatible = "zephyr,bt-hci-spi", "st,hci-spi-v2"; + compatible = "st,hci-spi-v2"; reg = <0>; irq-gpios = <&gpiod 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; reset-gpios = <&gpiod 4 GPIO_ACTIVE_LOW>; diff --git a/boards/arm/stm32l562e_dk/stm32l562e_dk_common.dtsi b/boards/arm/stm32l562e_dk/stm32l562e_dk_common.dtsi index 97263620f2e1..7c91d04cbb57 100644 --- a/boards/arm/stm32l562e_dk/stm32l562e_dk_common.dtsi +++ b/boards/arm/stm32l562e_dk/stm32l562e_dk_common.dtsi @@ -111,16 +111,15 @@ stm32_lp_tick_source: &lptim1 { &spi1 { pinctrl-0 = <&spi1_sck_pg2 &spi1_miso_pg3 &spi1_mosi_pg4>; pinctrl-names = "default"; - cs-gpios = <&gpiog 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + cs-gpios = <&gpiog 5 GPIO_ACTIVE_LOW>; status = "okay"; spbtle-rf@0 { - compatible = "zephyr,bt-hci-spi", "st,hci-spi-v1"; + compatible = "st,hci-spi-v1"; reg = <0>; irq-gpios = <&gpiog 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; - reset-gpios = <&gpiog 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; - spi-max-frequency = <2000000>; - controller-data-delay-us = <0>; /* No need for extra delay for BlueNRG-MS */ + reset-gpios = <&gpiog 8 GPIO_ACTIVE_LOW>; + spi-max-frequency = ; spi-hold-cs; }; }; diff --git a/boards/shields/x_nucleo_idb05a1/x_nucleo_idb05a1.overlay b/boards/shields/x_nucleo_idb05a1/x_nucleo_idb05a1.overlay index dbb23935f037..e6dff3c9c9f3 100644 --- a/boards/shields/x_nucleo_idb05a1/x_nucleo_idb05a1.overlay +++ b/boards/shields/x_nucleo_idb05a1/x_nucleo_idb05a1.overlay @@ -5,15 +5,14 @@ */ &arduino_spi { - cs-gpios = <&arduino_header 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* A1 */ + cs-gpios = <&arduino_header 1 GPIO_ACTIVE_LOW>; /* A1 */ spbtle_rf_x_nucleo_idb05a1: spbtle-rf@0 { - compatible = "zephyr,bt-hci-spi", "st,hci-spi-v1"; + compatible = "st,hci-spi-v1"; reg = <0>; - reset-gpios = <&arduino_header 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* D7 */ + reset-gpios = <&arduino_header 13 GPIO_ACTIVE_LOW>; /* D7 */ irq-gpios = <&arduino_header 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; /* A0 */ - spi-max-frequency = <2000000>; - controller-data-delay-us = <0>; /* No need for extra delay for BlueNRG-MS */ + spi-max-frequency = ; spi-hold-cs; }; }; From 07e7cddbd5fbcb4e63fc3a88b6ac967747c111df Mon Sep 17 00:00:00 2001 From: Ali Hozhabri Date: Mon, 8 Jan 2024 14:38:20 +0100 Subject: [PATCH 5/5] drivers: bluetooth: hci: Add support for ST Proprietary extended event Add support for handling ST Proprietary extended event. Signed-off-by: Ali Hozhabri --- drivers/bluetooth/hci/hci_spi_st.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/bluetooth/hci/hci_spi_st.c b/drivers/bluetooth/hci/hci_spi_st.c index 605e36e56a62..bd1e4d447ef3 100644 --- a/drivers/bluetooth/hci/hci_spi_st.c +++ b/drivers/bluetooth/hci/hci_spi_st.c @@ -32,6 +32,8 @@ LOG_MODULE_REGISTER(bt_driver); #define HCI_ACL 0x02 #define HCI_SCO 0x03 #define HCI_EVT 0x04 +/* ST Proprietary extended event */ +#define HCI_EXT_EVT 0x82 /* Special Values */ #define SPI_WRITE 0x0A @@ -108,6 +110,11 @@ static const struct spi_buf_set spi_rx = { .count = 1 }; +struct bt_hci_ext_evt_hdr { + uint8_t evt; + uint16_t len; +} __packed; + static inline int bt_spi_transceive(void *tx, uint32_t tx_len, void *rx, uint32_t rx_len) { @@ -336,6 +343,19 @@ static struct net_buf *bt_spi_rx_buf_construct(uint8_t *msg) int len; switch (msg[PACKET_TYPE]) { +#if DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2) + case HCI_EXT_EVT: + struct bt_hci_ext_evt_hdr *evt = (struct bt_hci_ext_evt_hdr *) (msg + 1); + struct bt_hci_evt_hdr *evt2 = (struct bt_hci_evt_hdr *) (msg + 1); + + if (evt->len > 0xff) { + return NULL; + } + /* Use memmove instead of memcpy due to buffer overlapping */ + memmove(msg + (1 + sizeof(*evt2)), msg + (1 + sizeof(*evt)), evt2->len); + /* Manage event as regular HCI_EVT */ + __fallthrough; +#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_hci_spi_v2) */ case HCI_EVT: switch (msg[EVT_HEADER_EVENT]) { case BT_HCI_EVT_VENDOR: