| /* |
| * Copyright (c) 2017 Linaro Limited |
| * Copyright (c) 2016 Nordic Semiconductor ASA |
| * Copyright (c) 2015-2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| |
| #include <zephyr.h> |
| #include <misc/byteorder.h> |
| #include <logging/sys_log.h> |
| #include <misc/stack.h> |
| |
| #include <device.h> |
| #include <init.h> |
| #include <gpio.h> |
| #include <spi.h> |
| |
| #include <net/buf.h> |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/l2cap.h> |
| #include <bluetooth/hci.h> |
| #include <bluetooth/buf.h> |
| #include <bluetooth/hci_raw.h> |
| |
| #include "common/log.h" |
| |
| #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 SANITY_CHECK 0x02 |
| |
| /* Offsets */ |
| #define STATUS_HEADER_READY 0 |
| #define STATUS_HEADER_TOREAD 3 |
| |
| #define PACKET_TYPE 0 |
| #define EVT_BLUE_INITIALIZED 0x01 |
| |
| #define SPI_HCI_DEV_NAME CONFIG_BT_CTLR_TO_HOST_SPI_DEV_NAME |
| #define GPIO_IRQ_DEV_NAME CONFIG_BT_CTLR_TO_HOST_SPI_IRQ_DEV_NAME |
| #define GPIO_IRQ_PIN CONFIG_BT_CTLR_TO_HOST_SPI_IRQ_PIN |
| |
| /* Needs to be aligned with the SPI master buffer size */ |
| #define SPI_MAX_MSG_LEN 255 |
| |
| static u8_t rxmsg[SPI_MAX_MSG_LEN]; |
| static u8_t txmsg[SPI_MAX_MSG_LEN]; |
| |
| /* HCI buffer pools */ |
| #define CMD_BUF_SIZE BT_BUF_RX_SIZE |
| |
| NET_BUF_POOL_DEFINE(cmd_tx_pool, CONFIG_BT_HCI_CMD_COUNT, CMD_BUF_SIZE, |
| BT_BUF_USER_DATA_MIN, NULL); |
| |
| #if defined(CONFIG_BT_CTLR) |
| #define BT_L2CAP_MTU (CONFIG_BT_CTLR_TX_BUFFER_SIZE - \ |
| BT_L2CAP_HDR_SIZE) |
| #else |
| #define BT_L2CAP_MTU 65 /* 64-byte public key + opcode */ |
| #endif /* CONFIG_BT_CTLR */ |
| |
| /* Data size needed for ACL buffers */ |
| #define BT_BUF_ACL_SIZE BT_L2CAP_BUF_SIZE(BT_L2CAP_MTU) |
| |
| #if defined(CONFIG_BT_CTLR_TX_BUFFERS) |
| #define TX_BUF_COUNT CONFIG_BT_CTLR_TX_BUFFERS |
| #else |
| #define TX_BUF_COUNT 6 |
| #endif |
| |
| NET_BUF_POOL_DEFINE(acl_tx_pool, TX_BUF_COUNT, BT_BUF_ACL_SIZE, |
| BT_BUF_USER_DATA_MIN, NULL); |
| |
| static struct device *spi_hci_dev; |
| static struct device *gpio_dev; |
| static BT_STACK_NOINIT(bt_tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); |
| static struct k_thread bt_tx_thread_data; |
| |
| static K_SEM_DEFINE(sem_spi_rx, 0, 1); |
| static K_SEM_DEFINE(sem_spi_tx, 0, 1); |
| |
| static inline int spi_send(struct net_buf *buf) |
| { |
| u8_t header_master[5] = { 0 }; |
| u8_t header_slave[5] = { READY_NOW, SANITY_CHECK, |
| 0x00, 0x00, 0x00 }; |
| int ret; |
| |
| SYS_LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), |
| buf->len); |
| |
| switch (bt_buf_get_type(buf)) { |
| case BT_BUF_ACL_IN: |
| net_buf_push_u8(buf, HCI_ACL); |
| break; |
| case BT_BUF_EVT: |
| net_buf_push_u8(buf, HCI_EVT); |
| break; |
| default: |
| SYS_LOG_ERR("Unknown type %u", bt_buf_get_type(buf)); |
| net_buf_unref(buf); |
| return -EINVAL; |
| } |
| |
| if (buf->len > SPI_MAX_MSG_LEN) { |
| SYS_LOG_ERR("TX message too long"); |
| net_buf_unref(buf); |
| return -EINVAL; |
| } |
| header_slave[STATUS_HEADER_TOREAD] = buf->len; |
| |
| gpio_pin_write(gpio_dev, GPIO_IRQ_PIN, 1); |
| |
| /* Coordinate transfer lock with the spi rx thread */ |
| k_sem_take(&sem_spi_tx, K_FOREVER); |
| do { |
| ret = spi_transceive(spi_hci_dev, header_slave, 5, |
| header_master, 5); |
| if (ret < 0) { |
| SYS_LOG_ERR("SPI transceive error: %d", ret); |
| } |
| } while (header_master[STATUS_HEADER_READY] != SPI_READ); |
| |
| ret = spi_transceive(spi_hci_dev, buf->data, buf->len, |
| &rxmsg, buf->len); |
| if (ret < 0) { |
| SYS_LOG_ERR("SPI transceive error: %d", ret); |
| } |
| net_buf_unref(buf); |
| |
| gpio_pin_write(gpio_dev, GPIO_IRQ_PIN, 0); |
| k_sem_give(&sem_spi_rx); |
| |
| return 0; |
| } |
| |
| static void bt_tx_thread(void *p1, void *p2, void *p3) |
| { |
| u8_t header_master[5]; |
| u8_t header_slave[5] = { READY_NOW, SANITY_CHECK, |
| 0x00, 0x00, 0x00 }; |
| struct net_buf *buf = NULL; |
| struct bt_hci_cmd_hdr cmd_hdr; |
| struct bt_hci_acl_hdr acl_hdr; |
| int ret; |
| |
| ARG_UNUSED(p1); |
| ARG_UNUSED(p2); |
| ARG_UNUSED(p3); |
| |
| memset(&txmsg, 0xFF, SPI_MAX_MSG_LEN); |
| |
| while (1) { |
| do { |
| ret = spi_transceive(spi_hci_dev, header_slave, 5, |
| header_master, 5); |
| if (ret < 0) { |
| SYS_LOG_ERR("SPI transceive error: %d", ret); |
| } |
| } while ((header_master[STATUS_HEADER_READY] != SPI_READ) && |
| (header_master[STATUS_HEADER_READY] != SPI_WRITE)); |
| |
| if (header_master[STATUS_HEADER_READY] == SPI_READ) { |
| /* Unblock the spi tx thread and wait for it */ |
| k_sem_give(&sem_spi_tx); |
| k_sem_take(&sem_spi_rx, K_FOREVER); |
| continue; |
| } |
| |
| /* Receiving data from the SPI Host */ |
| ret = spi_transceive(spi_hci_dev, &txmsg, SPI_MAX_MSG_LEN, |
| &rxmsg, SPI_MAX_MSG_LEN); |
| if (ret < 0) { |
| SYS_LOG_ERR("SPI transceive error: %d", ret); |
| continue; |
| } |
| |
| switch (rxmsg[PACKET_TYPE]) { |
| case HCI_CMD: |
| memcpy(&cmd_hdr, &rxmsg[1], sizeof(cmd_hdr)); |
| |
| buf = net_buf_alloc(&cmd_tx_pool, K_NO_WAIT); |
| if (buf) { |
| bt_buf_set_type(buf, BT_BUF_CMD); |
| net_buf_add_mem(buf, &cmd_hdr, |
| sizeof(cmd_hdr)); |
| net_buf_add_mem(buf, &rxmsg[4], |
| cmd_hdr.param_len); |
| } else { |
| SYS_LOG_ERR("No available command buffers!"); |
| continue; |
| } |
| break; |
| case HCI_ACL: |
| memcpy(&acl_hdr, &rxmsg[1], sizeof(acl_hdr)); |
| |
| buf = net_buf_alloc(&acl_tx_pool, K_NO_WAIT); |
| if (buf) { |
| bt_buf_set_type(buf, BT_BUF_ACL_OUT); |
| net_buf_add_mem(buf, &acl_hdr, |
| sizeof(acl_hdr)); |
| net_buf_add_mem(buf, &rxmsg[5], |
| sys_le16_to_cpu(acl_hdr.len)); |
| } else { |
| SYS_LOG_ERR("No available ACL buffers!"); |
| continue; |
| } |
| break; |
| default: |
| SYS_LOG_ERR("Unknown BT HCI buf type"); |
| continue; |
| } |
| |
| SYS_LOG_DBG("buf %p type %u len %u", |
| buf, bt_buf_get_type(buf), buf->len); |
| |
| ret = bt_send(buf); |
| if (ret) { |
| SYS_LOG_ERR("Unable to send (ret %d)", ret); |
| net_buf_unref(buf); |
| } |
| |
| STACK_ANALYZE("tx_stack", bt_tx_thread_stack); |
| |
| /* Make sure other threads get a chance to run */ |
| k_yield(); |
| } |
| } |
| |
| static int hci_spi_init(struct device *unused) |
| { |
| static struct spi_config btspi_config = { |
| .config = SPI_WORD(8), |
| }; |
| |
| ARG_UNUSED(unused); |
| |
| SYS_LOG_DBG(""); |
| |
| spi_hci_dev = device_get_binding(SPI_HCI_DEV_NAME); |
| if (!spi_hci_dev) { |
| return -EINVAL; |
| } |
| |
| if (spi_configure(spi_hci_dev, &btspi_config) < 0) { |
| return -EINVAL; |
| } |
| |
| gpio_dev = device_get_binding(GPIO_IRQ_DEV_NAME); |
| if (!gpio_dev) { |
| return -EINVAL; |
| } |
| gpio_pin_configure(gpio_dev, GPIO_IRQ_PIN, |
| GPIO_DIR_OUT | GPIO_PUD_PULL_DOWN); |
| |
| return 0; |
| } |
| |
| DEVICE_INIT(hci_spi, "hci_spi", &hci_spi_init, NULL, NULL, |
| APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |
| |
| void main(void) |
| { |
| static K_FIFO_DEFINE(rx_queue); |
| struct bt_hci_evt_hdr *evt_hdr; |
| struct net_buf *buf; |
| k_tid_t tx_id; |
| int err; |
| |
| SYS_LOG_DBG("Start"); |
| |
| err = bt_enable_raw(&rx_queue); |
| if (err) { |
| SYS_LOG_ERR("bt_enable_raw: %d; aborting", err); |
| return; |
| } |
| |
| /* Spawn the TX thread, which feeds cmds and data to the controller */ |
| tx_id = k_thread_create(&bt_tx_thread_data, bt_tx_thread_stack, |
| K_THREAD_STACK_SIZEOF(bt_tx_thread_stack), |
| bt_tx_thread, NULL, NULL, NULL, K_PRIO_COOP(7), |
| 0, K_NO_WAIT); |
| |
| /* Send a vendor event to announce that the slave is initialized */ |
| buf = net_buf_alloc(&cmd_tx_pool, K_FOREVER); |
| bt_buf_set_type(buf, BT_BUF_EVT); |
| evt_hdr = net_buf_add(buf, sizeof(*evt_hdr)); |
| evt_hdr->evt = BT_HCI_EVT_VENDOR; |
| evt_hdr->len = 2; |
| net_buf_add_le16(buf, EVT_BLUE_INITIALIZED); |
| err = spi_send(buf); |
| if (err) { |
| SYS_LOG_ERR("can't send initialization event; aborting"); |
| k_thread_abort(tx_id); |
| return; |
| } |
| |
| while (1) { |
| buf = net_buf_get(&rx_queue, K_FOREVER); |
| err = spi_send(buf); |
| if (err) { |
| SYS_LOG_ERR("Failed to send"); |
| } |
| } |
| } |