diff --git a/CODEOWNERS b/CODEOWNERS index 8b2c82ed0548be..9700dd85182f2b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -139,6 +139,10 @@ /drivers/ieee802154/ @jukkar @tbursztyka /drivers/interrupt_controller/ @andrewboie /drivers/ipm/ipm_mhu* @karl-zh +/drivers/ipm/Kconfig.nrfx @masz-nordic @ioannisg +/drivers/ipm/Kconfig.nrfx_ipc_channel @masz-nordic @ioannisg +/drivers/ipm/ipm_nrfx_ipc.c @masz-nordic @ioannisg +/drivers/ipm/ipm_nrfx_ipc.h @masz-nordic @ioannisg /drivers/ipm/ipm_stm32_ipcc.c @arnop2 /drivers/*/vexriscv_litex.c @mateusz-holenko @kgugala @pgielda /drivers/led/ @Mani-Sadhasivam diff --git a/boards/arm/nrf5340_dk_nrf5340/Kconfig b/boards/arm/nrf5340_dk_nrf5340/Kconfig index 81e78956cea383..d02f1848e74c6b 100644 --- a/boards/arm/nrf5340_dk_nrf5340/Kconfig +++ b/boards/arm/nrf5340_dk_nrf5340/Kconfig @@ -20,6 +20,16 @@ config BOARD_ENABLE_DCDC_HV select SOC_DCDC_NRF53X_HV default y +choice BT_HCI_BUS_TYPE + default BT_RPMSG if BT +endchoice + +config HEAP_MEM_POOL_SIZE + default 4096 if BT_RPMSG + +config BT_HAS_HCI_VS + default y if BT + endif # BOARD_NRF5340_DK_NRF5340_CPUAPP || BOARD_NRF5340_DK_NRF5340_CPUAPPNS if BOARD_NRF5340_DK_NRF5340_CPUNET @@ -29,4 +39,7 @@ if BOARD_NRF5340_DK_NRF5340_CPUNET config BT_CTLR default y if BT +config BT_ECC + default y if BT + endif # BOARD_NRF5340_DK_NRF5340_CPUNET diff --git a/boards/arm/nrf5340_dk_nrf5340/nrf5340_dk_nrf5340_shared_sram_planning_conf.dts b/boards/arm/nrf5340_dk_nrf5340/nrf5340_dk_nrf5340_shared_sram_planning_conf.dts index 228a14b95016dc..7ba8481b90929c 100644 --- a/boards/arm/nrf5340_dk_nrf5340/nrf5340_dk_nrf5340_shared_sram_planning_conf.dts +++ b/boards/arm/nrf5340_dk_nrf5340/nrf5340_dk_nrf5340_shared_sram_planning_conf.dts @@ -12,6 +12,11 @@ */ / { + chosen { + /* shared memory reserved for the inter-processor communication */ + zephyr,ipc_shm = &sram0_shared; + }; + /* SRAM allocated to shared memory */ sram0_shared: memory@20010000 { device_type = "memory"; diff --git a/drivers/bluetooth/hci/CMakeLists.txt b/drivers/bluetooth/hci/CMakeLists.txt index 7465b2d5e47102..e8ac3e6558ecab 100644 --- a/drivers/bluetooth/hci/CMakeLists.txt +++ b/drivers/bluetooth/hci/CMakeLists.txt @@ -3,5 +3,7 @@ zephyr_sources_ifdef(CONFIG_BT_H4 h4.c) zephyr_sources_ifdef(CONFIG_BT_H5 h5.c) zephyr_sources_ifdef(CONFIG_BT_SPI spi.c) -zephyr_sources_ifdef(CONFIG_BT_STM32_IPM ipm_stm32wb.c) +zephyr_sources_ifdef(CONFIG_BT_RPMSG rpmsg.c) +zephyr_sources_ifdef(CONFIG_BT_RPMSG_NRF53 rpmsg_nrf53.c) +zephyr_sources_ifdef(CONFIG_BT_STM32_IPM ipm_stm32wb.c) zephyr_sources_ifdef(CONFIG_BT_USERCHAN userchan.c) diff --git a/drivers/bluetooth/hci/Kconfig b/drivers/bluetooth/hci/Kconfig index 605afa38cc38e4..694a532e017b31 100644 --- a/drivers/bluetooth/hci/Kconfig +++ b/drivers/bluetooth/hci/Kconfig @@ -30,6 +30,12 @@ config BT_H5 Bluetooth three-wire (H:5) UART driver. Implementation of HCI Three-Wire UART Transport Layer. +config BT_RPMSG + bool "HCI using RPMsg" + help + Bluetooth HCI driver for communication with another CPU + using RPMsg framework. + config BT_SPI bool "SPI HCI" depends on SPI @@ -92,3 +98,33 @@ config BT_SPI_BLUENRG Stack. Current driver supports: ST BLUENRG-MS. endif # BT_SPI + +if BT_RPMSG + +config BT_RPMSG_NRF53 + bool "nRF53 configuration of RPMsg" + default y if BOARD_NRF5340_DK_NRF5340_CPUAPP + select IPM + select IPM_NRFX + select IPM_MSG_CH_1_ENABLE + select IPM_MSG_CH_0_ENABLE + select IPM_MSG_CH_0_TX + select IPM_MSG_CH_1_RX + select OPENAMP + help + Enable RPMsg configuration for nRF53. Two channels of the IPM driver + are used in the HCI driver: channel 0 for TX and channel 1 for RX. + +if BT_RPMSG_NRF53 + +config BT_RPMSG_NRF53_RX_STACK_SIZE + int "RPMsg stack size for RX thread" + default 1024 + +config BT_RPMSG_NRF53_RX_PRIO + int "RPMsg RX thread priority" + default 8 + +endif # BT_RPMSG_NRF53 + +endif # BT_RPMSG diff --git a/drivers/bluetooth/hci/h5.c b/drivers/bluetooth/hci/h5.c index cf35f8cb50a50f..3a6b7b0c7670d9 100644 --- a/drivers/bluetooth/hci/h5.c +++ b/drivers/bluetooth/hci/h5.c @@ -122,7 +122,7 @@ static const u8_t conf_rsp[] = { 0x04, 0x7b }; /* H5 signal buffers pool */ #define MAX_SIG_LEN 3 #define SIGNAL_COUNT 2 -#define SIG_BUF_SIZE (CONFIG_BT_HCI_RESERVE + MAX_SIG_LEN) +#define SIG_BUF_SIZE (BT_BUF_RESERVE + MAX_SIG_LEN) NET_BUF_POOL_DEFINE(h5_pool, SIGNAL_COUNT, SIG_BUF_SIZE, 0, NULL); static struct device *h5_dev; diff --git a/drivers/bluetooth/hci/rpmsg.c b/drivers/bluetooth/hci/rpmsg.c new file mode 100644 index 00000000000000..42a50606be5c1b --- /dev/null +++ b/drivers/bluetooth/hci/rpmsg.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) +#define LOG_MODULE_NAME bt_hci_driver +#include "common/log.h" + +#define RPMSG_CMD 0x01 +#define RPMSG_ACL 0x02 +#define RPMSG_SCO 0x03 +#define RPMSG_EVT 0x04 + +int bt_rpmsg_platform_init(void); +int bt_rpmsg_platform_send(struct net_buf *buf); + +static struct net_buf *bt_rpmsg_evt_recv(u8_t *data, size_t remaining, + bool *prio) +{ + struct bt_hci_evt_hdr hdr; + struct net_buf *buf; + + if (remaining < sizeof(hdr)) { + BT_ERR("Not enough data for event header"); + return NULL; + } + + memcpy((void *)&hdr, data, sizeof(hdr)); + data += sizeof(hdr); + remaining -= sizeof(hdr); + + if (remaining != hdr.len) { + BT_ERR("Event payload length is not correct"); + return NULL; + } + BT_DBG("len %u", hdr.len); + + buf = bt_buf_get_evt(hdr.evt, false, K_NO_WAIT); + if (!buf) { + BT_ERR("No available event buffers!"); + return buf; + } + + net_buf_add_mem(buf, &hdr, sizeof(hdr)); + *prio = bt_hci_evt_is_prio(hdr.evt); + + net_buf_add_mem(buf, data, remaining); + + return buf; +} + +static struct net_buf *bt_rpmsg_acl_recv(u8_t *data, size_t remaining) +{ + struct bt_hci_acl_hdr hdr; + struct net_buf *buf; + + if (remaining < sizeof(hdr)) { + BT_ERR("Not enough data for ACL header"); + return NULL; + } + + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT); + if (buf) { + memcpy((void *)&hdr, data, sizeof(hdr)); + data += sizeof(hdr); + remaining -= sizeof(hdr); + + net_buf_add_mem(buf, &hdr, sizeof(hdr)); + } else { + BT_ERR("No available ACL buffers!"); + return NULL; + } + + if (remaining != sys_le16_to_cpu(hdr.len)) { + BT_ERR("ACL payload length is not correct"); + net_buf_unref(buf); + return NULL; + } + + BT_DBG("len %u", remaining); + net_buf_add_mem(buf, data, remaining); + + return buf; +} + +void bt_rpmsg_rx(u8_t *data, size_t len) +{ + u8_t pkt_indicator; + bool prio = false; + struct net_buf *buf = NULL; + size_t remaining = len; + + BT_HEXDUMP_DBG(data, len, "RPMsg data:"); + + pkt_indicator = *data++; + remaining -= sizeof(pkt_indicator); + + switch (pkt_indicator) { + case RPMSG_EVT: + buf = bt_rpmsg_evt_recv(data, remaining, &prio); + break; + + case RPMSG_ACL: + buf = bt_rpmsg_acl_recv(data, remaining); + break; + + default: + BT_ERR("Unknown HCI type %u", pkt_indicator); + return; + } + + if (buf) { + BT_DBG("Calling bt_recv(%p)", buf); + if (prio) { + bt_recv_prio(buf); + } else { + bt_recv(buf); + } + + BT_HEXDUMP_DBG(buf->data, buf->len, "RX buf payload:"); + } +} + +static int bt_rpmsg_send(struct net_buf *buf) +{ + int err; + u8_t pkt_indicator; + + BT_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_OUT: + pkt_indicator = RPMSG_ACL; + break; + case BT_BUF_CMD: + pkt_indicator = RPMSG_CMD; + break; + default: + BT_ERR("Unknown type %u", bt_buf_get_type(buf)); + goto done; + } + net_buf_push_u8(buf, pkt_indicator); + + BT_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:"); + err = bt_rpmsg_platform_send(buf); + if (err < 0) { + BT_ERR("Failed to send (err %d)", err); + } + +done: + net_buf_unref(buf); + return 0; +} + +static int bt_rpmsg_open(void) +{ + BT_DBG(""); + + return bt_rpmsg_platform_init(); +} + +static const struct bt_hci_driver drv = { + .name = "RPMsg", + .open = bt_rpmsg_open, + .send = bt_rpmsg_send, + .bus = BT_HCI_DRIVER_BUS_IPM, +}; + +static int bt_rpmsg_init(struct device *unused) +{ + ARG_UNUSED(unused); + + return bt_hci_driver_register(&drv); +} + +SYS_INIT(bt_rpmsg_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/drivers/bluetooth/hci/rpmsg_nrf53.c b/drivers/bluetooth/hci/rpmsg_nrf53.c new file mode 100644 index 00000000000000..90743347471bfe --- /dev/null +++ b/drivers/bluetooth/hci/rpmsg_nrf53.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) +#define LOG_MODULE_NAME bt_hci_driver_nrf53 +#include "common/log.h" + +void bt_rpmsg_rx(u8_t *data, size_t len); + +static K_SEM_DEFINE(ready_sem, 0, 1); +static K_SEM_DEFINE(rx_sem, 0, 1); + +static K_THREAD_STACK_DEFINE(bt_rpmsg_rx_thread_stack, + CONFIG_BT_RPMSG_NRF53_RX_STACK_SIZE); +static struct k_thread bt_rpmsg_rx_thread_data; + +static struct device *ipm_tx_handle; +static struct device *ipm_rx_handle; + +/* Configuration defines */ + +#define SHM_START_ADDR (DT_IPC_SHM_BASE_ADDRESS + 0x400) +#define SHM_SIZE 0x7c00 +#define SHM_DEVICE_NAME "sram0.shm" + +#define VRING_COUNT 2 +#define VRING_TX_ADDRESS (SHM_START_ADDR + SHM_SIZE - 0x400) +#define VRING_RX_ADDRESS (VRING_TX_ADDRESS - 0x400) +#define VRING_ALIGNMENT 4 +#define VRING_SIZE 16 + +#define VDEV_STATUS_ADDR DT_IPC_SHM_BASE_ADDRESS + +/* End of configuration defines */ + +static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR }; +static struct metal_device shm_device = { + .name = SHM_DEVICE_NAME, + .bus = NULL, + .num_regions = 1, + .regions = { + { + .virt = (void *) SHM_START_ADDR, + .physmap = shm_physmap, + .size = SHM_SIZE, + .page_shift = 0xffffffff, + .page_mask = 0xffffffff, + .mem_flags = 0, + .ops = { NULL }, + }, + }, + .node = { NULL }, + .irq_num = 0, + .irq_info = NULL +}; + +static struct virtqueue *vq[2]; +static struct rpmsg_endpoint ep; + +static unsigned char virtio_get_status(struct virtio_device *vdev) +{ + return VIRTIO_CONFIG_STATUS_DRIVER_OK; +} + +static void virtio_set_status(struct virtio_device *vdev, unsigned char status) +{ + sys_write8(status, VDEV_STATUS_ADDR); +} + +static u32_t virtio_get_features(struct virtio_device *vdev) +{ + return BIT(VIRTIO_RPMSG_F_NS); +} + +static void virtio_set_features(struct virtio_device *vdev, u32_t features) +{ + /* No need for implementation */ +} + +static void virtio_notify(struct virtqueue *vq) +{ + int status; + + status = ipm_send(ipm_tx_handle, 0, 0, NULL, 0); + if (status != 0) { + BT_ERR("ipm_send failed to notify: %d", status); + } +} + +const struct virtio_dispatch dispatch = { + .get_status = virtio_get_status, + .set_status = virtio_set_status, + .get_features = virtio_get_features, + .set_features = virtio_set_features, + .notify = virtio_notify, +}; + +static void ipm_callback(void *context, u32_t id, volatile void *data) +{ + BT_DBG("Got callback of id %u", id); + k_sem_give(&rx_sem); +} + +static int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + u32_t src, void *priv) +{ + BT_DBG("Received message of %u bytes.", len); + BT_HEXDUMP_DBG((uint8_t *)data, len, "Data:"); + + bt_rpmsg_rx(data, len); + + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ep) +{ + rpmsg_destroy_ept(ep); +} + +static void ns_bind_cb(struct rpmsg_device *rdev, const char *name, u32_t dest) +{ + (void)rpmsg_create_ept(&ep, + rdev, + name, + RPMSG_ADDR_ANY, + dest, + endpoint_cb, + rpmsg_service_unbind); + + k_sem_give(&ready_sem); +} + +static void bt_rpmsg_rx_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + while (1) { + int status = k_sem_take(&rx_sem, K_FOREVER); + + if (status == 0) { + virtqueue_notification(vq[0]); + } + } +} + +int bt_rpmsg_platform_init(void) +{ + int err; + struct metal_init_params metal_params = METAL_INIT_DEFAULTS; + + static struct virtio_vring_info rvrings[2]; + static struct rpmsg_virtio_shm_pool shpool; + static struct virtio_device vdev; + static struct rpmsg_virtio_device rvdev; + static struct metal_io_region *io; + static struct metal_device *device; + + /* Setup thread for RX data processing. */ + k_thread_create(&bt_rpmsg_rx_thread_data, bt_rpmsg_rx_thread_stack, + K_THREAD_STACK_SIZEOF(bt_rpmsg_rx_thread_stack), + bt_rpmsg_rx_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_RPMSG_NRF53_RX_PRIO), + 0, K_NO_WAIT); + + /* Libmetal setup */ + err = metal_init(&metal_params); + if (err) { + BT_ERR("metal_init: failed - error code %d", err); + return err; + } + + err = metal_register_generic_device(&shm_device); + if (err) { + BT_ERR("Couldn't register shared memory device: %d", err); + return err; + } + + err = metal_device_open("generic", SHM_DEVICE_NAME, &device); + if (err) { + BT_ERR("metal_device_open failed: %d", err); + return err; + } + + io = metal_device_io_region(device, 0); + if (!io) { + BT_ERR("metal_device_io_region failed to get region"); + return -ENODEV; + } + + /* IPM setup */ + ipm_tx_handle = device_get_binding("IPM_0"); + if (!ipm_tx_handle) { + BT_ERR("Could not get TX IPM device handle"); + return -ENODEV; + } + + ipm_rx_handle = device_get_binding("IPM_1"); + if (!ipm_rx_handle) { + BT_ERR("Could not get RX IPM device handle"); + return -ENODEV; + } + + ipm_register_callback(ipm_rx_handle, ipm_callback, NULL); + + /* Virtqueue setup */ + vq[0] = virtqueue_allocate(VRING_SIZE); + if (!vq[0]) { + BT_ERR("virtqueue_allocate failed to alloc vq[0]"); + return -ENOMEM; + } + + vq[1] = virtqueue_allocate(VRING_SIZE); + if (!vq[1]) { + BT_ERR("virtqueue_allocate failed to alloc vq[1]"); + return -ENOMEM; + } + + rvrings[0].io = io; + rvrings[0].info.vaddr = (void *)VRING_TX_ADDRESS; + rvrings[0].info.num_descs = VRING_SIZE; + rvrings[0].info.align = VRING_ALIGNMENT; + rvrings[0].vq = vq[0]; + + rvrings[1].io = io; + rvrings[1].info.vaddr = (void *)VRING_RX_ADDRESS; + rvrings[1].info.num_descs = VRING_SIZE; + rvrings[1].info.align = VRING_ALIGNMENT; + rvrings[1].vq = vq[1]; + + vdev.role = RPMSG_MASTER; + vdev.vrings_num = VRING_COUNT; + vdev.func = &dispatch; + vdev.vrings_info = &rvrings[0]; + + rpmsg_virtio_init_shm_pool(&shpool, (void *)SHM_START_ADDR, SHM_SIZE); + err = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool); + if (err) { + BT_ERR("rpmsg_init_vdev failed %d", err); + return err; + } + + /* Wait til nameservice ep is setup */ + k_sem_take(&ready_sem, K_FOREVER); + + return 0; +} + +int bt_rpmsg_platform_send(struct net_buf *buf) +{ + return rpmsg_send(&ep, buf->data, buf->len); +} diff --git a/drivers/ipm/CMakeLists.txt b/drivers/ipm/CMakeLists.txt index 86f5c8aeae8454..b753bb7a6169ab 100644 --- a/drivers/ipm/CMakeLists.txt +++ b/drivers/ipm/CMakeLists.txt @@ -6,5 +6,6 @@ zephyr_library_sources_ifdef(CONFIG_IPM_MCUX ipm_mcux.c) zephyr_library_sources_ifdef(CONFIG_IPM_IMX ipm_imx.c) zephyr_library_sources_ifdef(CONFIG_IPM_MHU ipm_mhu.c) zephyr_library_sources_ifdef(CONFIG_IPM_STM32_IPCC ipm_stm32_ipcc.c) +zephyr_library_sources_ifdef(CONFIG_IPM_NRFX ipm_nrfx_ipc.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c) diff --git a/drivers/ipm/Kconfig b/drivers/ipm/Kconfig index aae6755040cab6..1af8251a61a768 100644 --- a/drivers/ipm/Kconfig +++ b/drivers/ipm/Kconfig @@ -67,6 +67,23 @@ config IPM_MHU help Driver for SSE 200 MHU (Message Handling Unit) +config IPM_NRFX + bool "IPM NRF driver" + depends on IPM && HAS_HW_NRF_IPC + select NRFX_IPC + help + Driver for Nordic nRF messaging unit, based + on nRF IPC peripheral HW. + +config IPM_NRF_SINGLE_INSTANCE + bool "Single instance of IPM device" + help + Enable this option if the IPM device should have + a single instance, instead of one per IPC + message channel. + +source "drivers/ipm/Kconfig.nrfx" + config IPM_STM32_IPCC bool "STM32 IPCC controller" depends on IPM diff --git a/drivers/ipm/Kconfig.nrfx b/drivers/ipm/Kconfig.nrfx new file mode 100644 index 00000000000000..72ff2f92dcdc5d --- /dev/null +++ b/drivers/ipm/Kconfig.nrfx @@ -0,0 +1,41 @@ +# nRF IPM driver configuration + +# Copyright (c) 2019 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if IPM_NRFX + +nrfx_ipc_num = 0 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 1 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 2 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 3 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 4 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 5 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 6 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 7 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 8 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 9 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 10 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 11 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 12 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 13 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 14 +rsource "Kconfig.nrfx_ipc_channel" +nrfx_ipc_num = 15 +rsource "Kconfig.nrfx_ipc_channel" + +endif # IPM_NRFX diff --git a/drivers/ipm/Kconfig.nrfx_ipc_channel b/drivers/ipm/Kconfig.nrfx_ipc_channel new file mode 100644 index 00000000000000..7153116cbc9c2e --- /dev/null +++ b/drivers/ipm/Kconfig.nrfx_ipc_channel @@ -0,0 +1,20 @@ +# nRF IPM driver channel configuration + +# Copyright (c) 2019 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +menu "IPM Message Channel [$(nrfx_ipc_num)] configuration" + +config IPM_MSG_CH_$(nrfx_ipc_num)_ENABLE + bool "Enable IPM Message Channel $(nrfx_ipc_num)" + +config IPM_MSG_CH_$(nrfx_ipc_num)_RX + bool "IPM Message RX Channel" + depends on IPM_MSG_CH_$(nrfx_ipc_num)_ENABLE + +config IPM_MSG_CH_$(nrfx_ipc_num)_TX + bool "IPM Message TX Channel" + depends on IPM_MSG_CH_$(nrfx_ipc_num)_ENABLE + default ! IPM_MSG_CH_$(nrfx_ipc_num)_RX + +endmenu diff --git a/drivers/ipm/ipm_nrfx_ipc.c b/drivers/ipm/ipm_nrfx_ipc.c new file mode 100644 index 00000000000000..46ab6411d7d04f --- /dev/null +++ b/drivers/ipm/ipm_nrfx_ipc.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2019, Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "ipm_nrfx_ipc.h" + +#define LOG_LEVEL CONFIG_IPM_LOG_LEVEL +#include +LOG_MODULE_REGISTER(ipm_nrfx_ipc); + +struct ipm_nrf_data { + ipm_callback_t callback; + void *callback_ctx; +}; + +static struct ipm_nrf_data nrfx_ipm_data; + +static void gipm_init(void); +static void gipm_send(u32_t id); + +#if IS_ENABLED(CONFIG_IPM_NRF_SINGLE_INSTANCE) + +static void nrfx_ipc_handler(u32_t event_mask, void *p_context) +{ + if (nrfx_ipm_data.callback) { + while (event_mask) { + u8_t event_idx = __CLZ(__RBIT(event_mask)); + + event_mask &= ~BIT(event_idx); + nrfx_ipm_data.callback(nrfx_ipm_data.callback_ctx, + event_idx, + NULL); + } + } +} + +static int ipm_nrf_send(struct device *dev, int wait, u32_t id, + const void *data, int size) +{ + if (id > NRFX_IPC_ID_MAX_VALUE) { + return -EINVAL; + } + + if (size > 0) { + LOG_WRN("nRF driver does not support sending data over IPM"); + } + + gipm_send(id); + return 0; +} + +static int ipm_nrf_max_data_size_get(struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +static u32_t ipm_nrf_max_id_val_get(struct device *dev) +{ + ARG_UNUSED(dev); + + return NRFX_IPC_ID_MAX_VALUE; +} + +static void ipm_nrf_register_callback(struct device *dev, + ipm_callback_t cb, + void *context) +{ + nrfx_ipm_data.callback = cb; + nrfx_ipm_data.callback_ctx = context; +} + +static int ipm_nrf_set_enabled(struct device *dev, int enable) +{ + /* Enable configured channels */ + if (enable) { + irq_enable(DT_INST_0_NORDIC_NRF_IPC_IRQ_0); + nrfx_ipc_receive_event_group_enable((uint32_t)IPC_EVENT_BITS); + } else { + irq_disable(DT_INST_0_NORDIC_NRF_IPC_IRQ_0); + nrfx_ipc_receive_event_group_disable((uint32_t)IPC_EVENT_BITS); + } + return 0; +} + +static int ipm_nrf_init(struct device *dev) +{ + gipm_init(); + return 0; +} + +static const struct ipm_driver_api ipm_nrf_driver_api = { + .send = ipm_nrf_send, + .register_callback = ipm_nrf_register_callback, + .max_data_size_get = ipm_nrf_max_data_size_get, + .max_id_val_get = ipm_nrf_max_id_val_get, + .set_enabled = ipm_nrf_set_enabled +}; + +DEVICE_AND_API_INIT(ipm_nrf, DT_INST_0_NORDIC_NRF_IPC_LABEL, + ipm_nrf_init, NULL, NULL, + PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &ipm_nrf_driver_api); + +#else + +struct vipm_nrf_data { + ipm_callback_t callback[NRFX_IPC_ID_MAX_VALUE]; + void *callback_ctx[NRFX_IPC_ID_MAX_VALUE]; + bool ipm_init; + struct device *ipm_device; +}; + +static struct vipm_nrf_data nrfx_vipm_data; + +static void vipm_dispatcher(u32_t event_mask, void *p_context) +{ + while (event_mask) { + u8_t event_idx = __CLZ(__RBIT(event_mask)); + + event_mask &= ~BIT(event_idx); + if (nrfx_vipm_data.callback[event_idx] != NULL) { + nrfx_vipm_data.callback[event_idx] + (nrfx_vipm_data.callback_ctx[event_idx], + 0, + NULL); + } + } +} + +static int vipm_nrf_max_data_size_get(struct device *dev) +{ + return ipm_max_data_size_get(dev); +} + +static u32_t vipm_nrf_max_id_val_get(struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +static int vipm_nrf_init(struct device *dev) +{ + if (!nrfx_vipm_data.ipm_init) { + gipm_init(); + nrfx_vipm_data.ipm_init = true; + } + return 0; +} + +#define VIPM_DEVICE_1(_idx) \ +static int vipm_nrf_##_idx##_send(struct device *dev, int wait, \ + u32_t id, const void *data, int size) \ +{ \ + if (!IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_TX)) { \ + LOG_ERR("IPM_" #_idx " is RX message channel"); \ + return -EINVAL; \ + } \ + \ + if (id > NRFX_IPC_ID_MAX_VALUE) { \ + return -EINVAL; \ + } \ + \ + if (id != 0) { \ + LOG_WRN("Passing message ID to IPM with" \ + "predefined message ID"); \ + } \ + \ + if (size > 0) { \ + LOG_WRN("nRF driver does not support" \ + "sending data over IPM"); \ + } \ + \ + gipm_send(_idx); \ + return 0; \ +} \ + \ +static void vipm_nrf_##_idx##_register_callback(struct device *dev, \ + ipm_callback_t cb, \ + void *context) \ +{ \ + if (IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_RX)) { \ + nrfx_vipm_data.callback[_idx] = cb; \ + nrfx_vipm_data.callback_ctx[_idx] = context; \ + } else { \ + LOG_WRN("Trying to register a callback" \ + "for TX channel IPM_" #_idx); \ + } \ +} \ + \ +static int vipm_nrf_##_idx##_set_enabled(struct device *dev, int enable)\ +{ \ + if (!IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_RX)) { \ + LOG_ERR("IPM_" #_idx " is TX message channel"); \ + return -EINVAL; \ + } else if (enable) { \ + irq_enable(DT_INST_0_NORDIC_NRF_IPC_IRQ_0); \ + nrfx_ipc_receive_event_enable(_idx); \ + } else if (!enable) { \ + nrfx_ipc_receive_event_disable(_idx); \ + } \ + return 0; \ +} \ + \ +static const struct ipm_driver_api vipm_nrf_##_idx##_driver_api = { \ + .send = vipm_nrf_##_idx##_send, \ + .register_callback = vipm_nrf_##_idx##_register_callback, \ + .max_data_size_get = vipm_nrf_max_data_size_get, \ + .max_id_val_get = vipm_nrf_max_id_val_get, \ + .set_enabled = vipm_nrf_##_idx##_set_enabled \ +}; \ + \ +DEVICE_AND_API_INIT(vipm_nrf_##_idx, "IPM_"#_idx, \ + vipm_nrf_init, NULL, NULL, \ + PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &vipm_nrf_##_idx##_driver_api) + +#define VIPM_DEVICE(_idx, _) \ + COND_CODE_1(IS_ENABLED(CONFIG_IPM_MSG_CH_##_idx##_ENABLE), \ + (VIPM_DEVICE_1(_idx);), ()) + +UTIL_LISTIFY(NRFX_IPC_ID_MAX_VALUE, VIPM_DEVICE, _); + +#endif + +static void gipm_init(void) +{ + /* Init IPC */ +#if IS_ENABLED(CONFIG_IPM_NRF_SINGLE_INSTANCE) + nrfx_ipc_init(0, nrfx_ipc_handler, (void *)&nrfx_ipm_data); +#else + nrfx_ipc_init(0, vipm_dispatcher, (void *)&nrfx_ipm_data); +#endif + IRQ_CONNECT(DT_INST_0_NORDIC_NRF_IPC_IRQ_0, + DT_INST_0_NORDIC_NRF_IPC_IRQ_0_PRIORITY, + nrfx_isr, nrfx_ipc_irq_handler, 0); + + /* Set up signals and channels */ + nrfx_ipc_config_load(&ipc_cfg); +} + +static void gipm_send(u32_t id) +{ + nrfx_ipc_signal(id); +} diff --git a/drivers/ipm/ipm_nrfx_ipc.h b/drivers/ipm/ipm_nrfx_ipc.h new file mode 100644 index 00000000000000..8cc49c40515bcd --- /dev/null +++ b/drivers/ipm/ipm_nrfx_ipc.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019, Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#define NRFX_IPC_ID_MAX_VALUE IPC_CONF_NUM + +/* + * Group IPC signals, events and channels into message channels. + * Message channels are one-way connections between cores. + * + * For example Message Channel 0 is configured as TX on core 0 + * and as RX on core 1: + * + * [C0] [C1] + * SIGNAL0 -> CHANNEL0 -> EVENT0 + * + * Message Channel 1 is configured as RX on core 0 and as TX + * on core 1: + * [C0] [C1] + * EVENT1 <- CHANNEL1 <- SIGNAL1 + */ + +#define IPC_EVENT_BIT(idx) \ + ((IS_ENABLED(CONFIG_IPM_MSG_CH_##idx##_RX)) << idx) + +#define IPC_EVENT_BITS \ + ( \ + IPC_EVENT_BIT(0) | \ + IPC_EVENT_BIT(1) | \ + IPC_EVENT_BIT(2) | \ + IPC_EVENT_BIT(3) | \ + IPC_EVENT_BIT(4) | \ + IPC_EVENT_BIT(5) | \ + IPC_EVENT_BIT(6) | \ + IPC_EVENT_BIT(7) | \ + IPC_EVENT_BIT(8) | \ + IPC_EVENT_BIT(9) | \ + IPC_EVENT_BIT(10) | \ + IPC_EVENT_BIT(11) | \ + IPC_EVENT_BIT(12) | \ + IPC_EVENT_BIT(13) | \ + IPC_EVENT_BIT(14) | \ + IPC_EVENT_BIT(15) \ + ) + +static const nrfx_ipc_config_t ipc_cfg = { + .send_task_config = { + [0] = BIT(0), + [1] = BIT(1), + [2] = BIT(2), + [3] = BIT(3), + [4] = BIT(4), + [5] = BIT(5), + [6] = BIT(6), + [7] = BIT(7), + [8] = BIT(8), + [9] = BIT(9), + [10] = BIT(10), + [11] = BIT(11), + [12] = BIT(12), + [13] = BIT(13), + [14] = BIT(14), + [15] = BIT(15), + }, + .receive_event_config = { + [0] = BIT(0), + [1] = BIT(1), + [2] = BIT(2), + [3] = BIT(3), + [4] = BIT(4), + [5] = BIT(5), + [6] = BIT(6), + [7] = BIT(7), + [8] = BIT(8), + [9] = BIT(9), + [10] = BIT(10), + [11] = BIT(11), + [12] = BIT(12), + [13] = BIT(13), + [14] = BIT(14), + [15] = BIT(15), + }, + .receive_events_enabled = IPC_EVENT_BITS, +}; diff --git a/dts/arm/nordic/nrf5340_cpuapp.dtsi b/dts/arm/nordic/nrf5340_cpuapp.dtsi index 73e5c2c04c699d..72b1df9c344e61 100644 --- a/dts/arm/nordic/nrf5340_cpuapp.dtsi +++ b/dts/arm/nordic/nrf5340_cpuapp.dtsi @@ -46,6 +46,7 @@ gpiote-0 = &gpiote; i2c-0 = &i2c0; i2c-1 = &i2c1; + ipc-0 = &ipc; pdm-0 = &pdm0; spi-0 = &spi0; spi-1 = &spi1; diff --git a/dts/arm/nordic/nrf5340_cpunet.dtsi b/dts/arm/nordic/nrf5340_cpunet.dtsi index 5b35525a50ff4e..e1df760ad076e8 100644 --- a/dts/arm/nordic/nrf5340_cpunet.dtsi +++ b/dts/arm/nordic/nrf5340_cpunet.dtsi @@ -37,6 +37,7 @@ gpio-1 = &gpio1; gpiote-0 = &gpiote; i2c-0 = &i2c0; + ipc-0 = &ipc; spi-0 = &spi0; wdt-0 = &wdt; timer-0 = &timer0; diff --git a/include/bluetooth/buf.h b/include/bluetooth/buf.h index 0cc138a472187f..0d25f6de3c1336 100644 --- a/include/bluetooth/buf.h +++ b/include/bluetooth/buf.h @@ -37,8 +37,14 @@ enum bt_buf_type { /** Minimum amount of user data size for buffers passed to the stack. */ #define BT_BUF_USER_DATA_MIN 8 +#if defined(CONFIG_BT_HCI_RAW) +#define BT_BUF_RESERVE MAX(CONFIG_BT_HCI_RESERVE, CONFIG_BT_HCI_RAW_RESERVE) +#else +#define BT_BUF_RESERVE CONFIG_BT_HCI_RESERVE +#endif + /** Data size neeed for HCI RX buffers */ -#define BT_BUF_RX_SIZE (CONFIG_BT_HCI_RESERVE + CONFIG_BT_RX_BUF_LEN) +#define BT_BUF_RX_SIZE (BT_BUF_RESERVE + CONFIG_BT_RX_BUF_LEN) /** Allocate a buffer for incoming data * diff --git a/include/bluetooth/l2cap.h b/include/bluetooth/l2cap.h index 2696ddab439a65..e55202e64214c7 100644 --- a/include/bluetooth/l2cap.h +++ b/include/bluetooth/l2cap.h @@ -38,7 +38,7 @@ extern "C" { * * @return Needed buffer size to match the requested L2CAP MTU. */ -#define BT_L2CAP_BUF_SIZE(mtu) (CONFIG_BT_HCI_RESERVE + \ +#define BT_L2CAP_BUF_SIZE(mtu) (BT_BUF_RESERVE + \ BT_HCI_ACL_HDR_SIZE + BT_L2CAP_HDR_SIZE + \ (mtu)) @@ -250,7 +250,7 @@ struct bt_l2cap_chan_ops { /** @def BT_L2CAP_CHAN_SEND_RESERVE * @brief Headroom needed for outgoing buffers */ -#define BT_L2CAP_CHAN_SEND_RESERVE (CONFIG_BT_HCI_RESERVE + 4 + 4) +#define BT_L2CAP_CHAN_SEND_RESERVE (BT_BUF_RESERVE + 4 + 4) /** @brief L2CAP Server structure. */ struct bt_l2cap_server { diff --git a/samples/bluetooth/hci_rpmsg/CMakeLists.txt b/samples/bluetooth/hci_rpmsg/CMakeLists.txt new file mode 100644 index 00000000000000..bad6906aaa8fd5 --- /dev/null +++ b/samples/bluetooth/hci_rpmsg/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(hci_rpmsg) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/bluetooth/hci_rpmsg/README.rst b/samples/bluetooth/hci_rpmsg/README.rst new file mode 100644 index 00000000000000..87dbfc36a73c66 --- /dev/null +++ b/samples/bluetooth/hci_rpmsg/README.rst @@ -0,0 +1,40 @@ +.. _bluetooth-hci-rpmsg-sample: + +Bluetooth: HCI RPMsg +#################### + +Overview +******** + +This sample exposes :ref:`bluetooth_controller` support +to another device or CPU using RPMsg transport which is +a part of `OpenAMP `__. + +Requirements +************ + +* A board with :ref:`ipm_interface` driver and Bluetooth LE support + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/hci_rpmsg` +in the Zephyr tree. + +To use this application, you need a board with a Bluetooth controller +and IPM drivers. +You can then build this application and flash it onto your board in +the usual way. See :ref:`boards` for board-specific building and +programming information. + +To test this sample, you need a separate device/CPU that acts as Bluetooth +HCI RPMsg peer. +This sample is compatible with the HCI RPMsg driver provided by +Zephyr's Bluetooth :ref:`bt_hci_drivers` core. See the +:option:`CONFIG_BT_RPMSG` configuration option for more information. + +You might need to adjust the Kconfig configuration of this sample to make it +compatible with the peer application. For example, :option:`CONFIG_BT_MAX_CONN` +must be equal to the maximum number of connections supported by the peer application. + +Refer to :ref:`bluetooth-samples` for general information about Bluetooth samples. diff --git a/samples/bluetooth/hci_rpmsg/prj.conf b/samples/bluetooth/hci_rpmsg/prj.conf new file mode 100644 index 00000000000000..d3445b936685b7 --- /dev/null +++ b/samples/bluetooth/hci_rpmsg/prj.conf @@ -0,0 +1,20 @@ +CONFIG_LOG=y +CONFIG_OPENAMP=y + +CONFIG_IPM=y +CONFIG_IPM_NRFX=y + +CONFIG_IPM_MSG_CH_1_ENABLE=y +CONFIG_IPM_MSG_CH_1_TX=y +CONFIG_IPM_MSG_CH_0_ENABLE=y +CONFIG_IPM_MSG_CH_0_RX=y + +CONFIG_HEAP_MEM_POOL_SIZE=8192 + +CONFIG_MAIN_STACK_SIZE=512 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=512 +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_MAX_CONN=16 +CONFIG_BT_CTLR_ASSERT_HANDLER=y +CONFIG_BT_HCI_RAW_RESERVE=1 diff --git a/samples/bluetooth/hci_rpmsg/sample.yaml b/samples/bluetooth/hci_rpmsg/sample.yaml new file mode 100644 index 00000000000000..abf80172eebae3 --- /dev/null +++ b/samples/bluetooth/hci_rpmsg/sample.yaml @@ -0,0 +1,9 @@ +sample: + description: Allows Zephyr to provide Bluetooth connectivity + via RPMsg. + name: Bluetooth HCI RPMsg +tests: + sample.bluetooth.hci_rpmsg: + harness: bluetooth + platform_whitelist: nrf5340_dk_nrf5340_cpunet + tags: bluetooth diff --git a/samples/bluetooth/hci_rpmsg/src/main.c b/samples/bluetooth/hci_rpmsg/src/main.c new file mode 100644 index 00000000000000..f81222730b83ca --- /dev/null +++ b/samples/bluetooth/hci_rpmsg/src/main.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_INFO +#define LOG_MODULE_NAME hci_rpmsg +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +/* Configuration defines */ + +#define SHM_START_ADDR (DT_IPC_SHM_BASE_ADDRESS + 0x400) +#define SHM_SIZE 0x7c00 +#define SHM_DEVICE_NAME "sram0.shm" + +#define VRING_COUNT 2 +#define VRING_TX_ADDRESS (SHM_START_ADDR + SHM_SIZE - 0x400) +#define VRING_RX_ADDRESS (VRING_TX_ADDRESS - 0x400) +#define VRING_ALIGNMENT 4 +#define VRING_SIZE 16 + +#define VDEV_STATUS_ADDR DT_IPC_SHM_BASE_ADDRESS + +/* End of configuration defines */ + +static struct device *ipm_tx_handle; +static struct device *ipm_rx_handle; + +static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR }; +static struct metal_device shm_device = { + .name = SHM_DEVICE_NAME, + .bus = NULL, + .num_regions = 1, + .regions = { + { + .virt = (void *) SHM_START_ADDR, + .physmap = shm_physmap, + .size = SHM_SIZE, + .page_shift = 0xffffffff, + .page_mask = 0xffffffff, + .mem_flags = 0, + .ops = { NULL }, + }, + }, + .node = { NULL }, + .irq_num = 0, + .irq_info = NULL +}; + +static struct virtqueue *vq[2]; +static struct rpmsg_endpoint ep; + +static struct k_work ipm_work; + +static unsigned char virtio_get_status(struct virtio_device *vdev) +{ + return sys_read8(VDEV_STATUS_ADDR); +} + +static u32_t virtio_get_features(struct virtio_device *vdev) +{ + return BIT(VIRTIO_RPMSG_F_NS); +} + +static void virtio_set_status(struct virtio_device *vdev, unsigned char status) +{ + sys_write8(status, VDEV_STATUS_ADDR); +} + +static void virtio_notify(struct virtqueue *vq) +{ + int status; + + status = ipm_send(ipm_tx_handle, 0, 0, NULL, 0); + if (status != 0) { + LOG_ERR("ipm_send failed to notify: %d", status); + } +} + +const struct virtio_dispatch dispatch = { + .get_status = virtio_get_status, + .set_status = virtio_set_status, + .get_features = virtio_get_features, + .notify = virtio_notify, +}; + +static void ipm_callback_process(struct k_work *work) +{ + virtqueue_notification(vq[1]); +} + +static void ipm_callback(void *context, u32_t id, volatile void *data) +{ + LOG_INF("Got callback of id %u", id); + k_work_submit(&ipm_work); +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ep) +{ + rpmsg_destroy_ept(ep); +} + +static K_THREAD_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); +static struct k_thread tx_thread_data; + +/* HCI command buffers */ +#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_TX_BUFFER_SIZE) +#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 K_FIFO_DEFINE(tx_queue); + +#define HCI_RPMSG_CMD 0x01 +#define HCI_RPMSG_ACL 0x02 +#define HCI_RPMSG_SCO 0x03 +#define HCI_RPMSG_EVT 0x04 + +static struct net_buf *hci_rpmsg_cmd_recv(u8_t *data, size_t remaining) +{ + struct bt_hci_cmd_hdr hdr; + struct net_buf *buf; + + if (remaining < sizeof(hdr)) { + LOG_ERR("Not enought data for command header"); + return NULL; + } + + buf = net_buf_alloc(&cmd_tx_pool, K_NO_WAIT); + if (buf) { + bt_buf_set_type(buf, BT_BUF_CMD); + + memcpy((void *)&hdr, data, sizeof(hdr)); + data += sizeof(hdr); + remaining -= sizeof(hdr); + + net_buf_add_mem(buf, &hdr, sizeof(hdr)); + } else { + LOG_ERR("No available command buffers!"); + return NULL; + } + + if (remaining != hdr.param_len) { + LOG_ERR("Command payload length is not correct"); + net_buf_unref(buf); + return NULL; + } + + LOG_DBG("len %u", hdr.param_len); + net_buf_add_mem(buf, data, remaining); + + return buf; +} + +static struct net_buf *hci_rpmsg_acl_recv(u8_t *data, size_t remaining) +{ + struct bt_hci_acl_hdr hdr; + struct net_buf *buf; + + if (remaining < sizeof(hdr)) { + LOG_ERR("Not enought data for ACL header"); + return NULL; + } + + buf = net_buf_alloc(&acl_tx_pool, K_NO_WAIT); + if (buf) { + bt_buf_set_type(buf, BT_BUF_ACL_OUT); + + memcpy((void *)&hdr, data, sizeof(hdr)); + data += sizeof(hdr); + remaining -= sizeof(hdr); + + net_buf_add_mem(buf, &hdr, sizeof(hdr)); + } else { + LOG_ERR("No available ACL buffers!"); + return NULL; + } + + if (remaining != sys_le16_to_cpu(hdr.len)) { + LOG_ERR("ACL payload length is not correct"); + net_buf_unref(buf); + return NULL; + } + + LOG_DBG("len %u", remaining); + net_buf_add_mem(buf, data, remaining); + + return buf; +} + +static void hci_rpmsg_rx(u8_t *data, size_t len) +{ + u8_t pkt_indicator; + struct net_buf *buf = NULL; + size_t remaining = len; + + LOG_HEXDUMP_DBG(data, len, "RPMSG data:"); + + pkt_indicator = *data++; + remaining -= sizeof(pkt_indicator); + + switch (pkt_indicator) { + case HCI_RPMSG_CMD: + buf = hci_rpmsg_cmd_recv(data, remaining); + break; + + case HCI_RPMSG_ACL: + buf = hci_rpmsg_acl_recv(data, remaining); + break; + + default: + LOG_ERR("Unknown HCI type %u", pkt_indicator); + return; + } + + if (buf) { + net_buf_put(&tx_queue, buf); + + LOG_HEXDUMP_DBG(buf->data, buf->len, "Final net buffer:"); + } +} + +static void tx_thread(void *p1, void *p2, void *p3) +{ + while (1) { + struct net_buf *buf; + int err; + + /* Wait until a buffer is available */ + buf = net_buf_get(&tx_queue, K_FOREVER); + /* Pass buffer to the stack */ + err = bt_send(buf); + if (err) { + LOG_ERR("Unable to send (err %d)", err); + net_buf_unref(buf); + } + + /* Give other threads a chance to run if tx_queue keeps getting + * new data all the time. + */ + k_yield(); + } +} + +static int hci_rpmsg_send(struct net_buf *buf) +{ + u8_t pkt_indicator; + + LOG_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), + buf->len); + + LOG_HEXDUMP_DBG(buf->data, buf->len, "Controller buffer:"); + + switch (bt_buf_get_type(buf)) { + case BT_BUF_ACL_IN: + pkt_indicator = HCI_RPMSG_ACL; + break; + case BT_BUF_EVT: + pkt_indicator = HCI_RPMSG_EVT; + break; + default: + LOG_ERR("Unknown type %u", bt_buf_get_type(buf)); + net_buf_unref(buf); + return -EINVAL; + } + net_buf_push_u8(buf, pkt_indicator); + + LOG_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:"); + rpmsg_send(&ep, buf->data, buf->len); + + net_buf_unref(buf); + + return 0; +} + +#if defined(CONFIG_BT_CTLR_ASSERT_HANDLER) +void bt_ctlr_assert_handle(char *file, u32_t line) +{ + LOG_ERR("Controller assert in: %s at %d", file, line); +} +#endif /* CONFIG_BT_CTLR_ASSERT_HANDLER */ + +int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, u32_t src, + void *priv) +{ + LOG_INF("Received message of %u bytes.", len); + hci_rpmsg_rx((u8_t *) data, len); + + return RPMSG_SUCCESS; +} + +static int hci_rpmsg_init(void) +{ + int err; + struct metal_init_params metal_params = METAL_INIT_DEFAULTS; + + static struct virtio_vring_info rvrings[2]; + static struct virtio_device vdev; + static struct rpmsg_device *rdev; + static struct rpmsg_virtio_device rvdev; + static struct metal_io_region *io; + static struct metal_device *device; + + /* Setup IPM workqueue item */ + k_work_init(&ipm_work, ipm_callback_process); + + /* Libmetal setup */ + err = metal_init(&metal_params); + if (err) { + LOG_ERR("metal_init: failed - error code %d", err); + return err; + } + + err = metal_register_generic_device(&shm_device); + if (err) { + LOG_ERR("Couldn't register shared memory device: %d", err); + return err; + } + + err = metal_device_open("generic", SHM_DEVICE_NAME, &device); + if (err) { + LOG_ERR("metal_device_open failed: %d", err); + return err; + } + + io = metal_device_io_region(device, 0); + if (!io) { + LOG_ERR("metal_device_io_region failed to get region"); + return -ENODEV; + } + + /* IPM setup */ + ipm_tx_handle = device_get_binding("IPM_1"); + if (!ipm_tx_handle) { + LOG_ERR("Could not get TX IPM device handle"); + return -ENODEV; + } + + ipm_rx_handle = device_get_binding("IPM_0"); + if (!ipm_rx_handle) { + LOG_ERR("Could not get RX IPM device handle"); + return -ENODEV; + } + + ipm_register_callback(ipm_rx_handle, ipm_callback, NULL); + + vq[0] = virtqueue_allocate(VRING_SIZE); + if (!vq[0]) { + LOG_ERR("virtqueue_allocate failed to alloc vq[0]"); + return -ENOMEM; + } + + vq[1] = virtqueue_allocate(VRING_SIZE); + if (!vq[1]) { + LOG_ERR("virtqueue_allocate failed to alloc vq[1]"); + return -ENOMEM; + } + + rvrings[0].io = io; + rvrings[0].info.vaddr = (void *)VRING_TX_ADDRESS; + rvrings[0].info.num_descs = VRING_SIZE; + rvrings[0].info.align = VRING_ALIGNMENT; + rvrings[0].vq = vq[0]; + + rvrings[1].io = io; + rvrings[1].info.vaddr = (void *)VRING_RX_ADDRESS; + rvrings[1].info.num_descs = VRING_SIZE; + rvrings[1].info.align = VRING_ALIGNMENT; + rvrings[1].vq = vq[1]; + + vdev.role = RPMSG_REMOTE; + vdev.vrings_num = VRING_COUNT; + vdev.func = &dispatch; + vdev.vrings_info = &rvrings[0]; + + /* setup rvdev */ + err = rpmsg_init_vdev(&rvdev, &vdev, NULL, io, NULL); + if (err) { + LOG_ERR("rpmsg_init_vdev failed %d", err); + return err; + } + + rdev = rpmsg_virtio_get_rpmsg_device(&rvdev); + + err = rpmsg_create_ept(&ep, rdev, "bt_hci", RPMSG_ADDR_ANY, + RPMSG_ADDR_ANY, endpoint_cb, + rpmsg_service_unbind); + if (err) { + LOG_ERR("rpmsg_create_ept failed %d", err); + return err; + } + + return err; +} + +void main(void) +{ + int err; + + /* incoming events and data from the controller */ + static K_FIFO_DEFINE(rx_queue); + + /* initialize RPMSG */ + err = hci_rpmsg_init(); + if (err != 0) { + return; + } + + LOG_DBG("Start"); + + /* Enable the raw interface, this will in turn open the HCI driver */ + bt_enable_raw(&rx_queue); + + /* Spawn the TX thread and start feeding commands and data to the + * controller + */ + k_thread_create(&tx_thread_data, tx_thread_stack, + K_THREAD_STACK_SIZEOF(tx_thread_stack), tx_thread, + NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); + + while (1) { + struct net_buf *buf; + + buf = net_buf_get(&rx_queue, K_FOREVER); + err = hci_rpmsg_send(buf); + if (err) { + LOG_ERR("Failed to send (err %d)", err); + } + } +} diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 6a5d2dac4e65aa..915f67f156bb98 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -119,6 +119,7 @@ def main(): write_addr_size(edt, "zephyr,sram", "SRAM") write_addr_size(edt, "zephyr,ccm", "CCM") write_addr_size(edt, "zephyr,dtcm", "DTCM") + write_addr_size(edt, "zephyr,ipc_shm", "IPC_SHM") write_flash(edt.chosen_node("zephyr,flash")) write_code_partition(edt.chosen_node("zephyr,code-partition")) diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index b0d8248bc7393f..6fe922b12da860 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -48,6 +48,14 @@ config BT_HCI_RAW This option allows to access Bluetooth controller from the application with the RAW HCI protocol. +config BT_HCI_RAW_RESERVE + int "Buffer headroom needed for HCI transport" + depends on BT_HCI_RAW + default 0 + help + This option is used by the HCI raw transport implementation to + declare how much headroom it needs for any HCI transport headers. + config BT_PERIPHERAL bool "Peripheral Role support" select BT_BROADCASTER diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index e9c0c6010cef5a..a835914bacace5 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -2326,7 +2326,7 @@ struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool, return NULL; } - reserve += sizeof(struct bt_hci_acl_hdr) + CONFIG_BT_HCI_RESERVE; + reserve += sizeof(struct bt_hci_acl_hdr) + BT_BUF_RESERVE; net_buf_reserve(buf, reserve); return buf; diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 7f3045b84441b2..e42f81a948c93b 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -269,7 +269,7 @@ struct net_buf *bt_hci_cmd_create(u16_t opcode, u8_t param_len) BT_DBG("buf %p", buf); - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); cmd(buf)->type = BT_BUF_CMD; cmd(buf)->opcode = opcode; @@ -6023,7 +6023,7 @@ struct net_buf *bt_buf_get_rx(enum bt_buf_type type, s32_t timeout) #endif if (buf) { - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); bt_buf_set_type(buf, type); } @@ -6045,7 +6045,7 @@ struct net_buf *bt_buf_get_cmd_complete(s32_t timeout) if (buf) { bt_buf_set_type(buf, BT_BUF_EVT); buf->len = 0U; - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); return buf; } @@ -6063,7 +6063,7 @@ struct net_buf *bt_buf_get_evt(u8_t evt, bool discardable, s32_t timeout) buf = net_buf_alloc(&num_complete_pool, timeout); if (buf) { - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); bt_buf_set_type(buf, BT_BUF_EVT); } @@ -6080,7 +6080,7 @@ struct net_buf *bt_buf_get_evt(u8_t evt, bool discardable, s32_t timeout) buf = net_buf_alloc(&discardable_pool, timeout); if (buf) { - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); bt_buf_set_type(buf, BT_BUF_EVT); } diff --git a/subsys/bluetooth/host/hci_raw.c b/subsys/bluetooth/host/hci_raw.c index f6a1d9650a7f76..9a9385b7916e45 100644 --- a/subsys/bluetooth/host/hci_raw.c +++ b/subsys/bluetooth/host/hci_raw.c @@ -54,7 +54,7 @@ struct net_buf *bt_buf_get_rx(enum bt_buf_type type, s32_t timeout) buf = net_buf_alloc(&hci_rx_pool, timeout); if (buf) { - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); bt_buf_set_type(buf, type); } diff --git a/subsys/bluetooth/host/rfcomm_internal.h b/subsys/bluetooth/host/rfcomm_internal.h index 3b176b77fccf27..dc4bf0a521a671 100644 --- a/subsys/bluetooth/host/rfcomm_internal.h +++ b/subsys/bluetooth/host/rfcomm_internal.h @@ -132,7 +132,7 @@ struct bt_rfcomm_rpn { * Length in rfcomm header can be two bytes depending on user data length. * One byte in the tail should be reserved for FCS. */ -#define BT_RFCOMM_BUF_SIZE(mtu) (CONFIG_BT_HCI_RESERVE + \ +#define BT_RFCOMM_BUF_SIZE(mtu) (BT_BUF_RESERVE + \ BT_HCI_ACL_HDR_SIZE + BT_L2CAP_HDR_SIZE + \ sizeof(struct bt_rfcomm_hdr) + 1 + (mtu) + \ BT_RFCOMM_FCS_SIZE) diff --git a/subsys/usb/class/bluetooth.c b/subsys/usb/class/bluetooth.c index 3fccf09a177d6e..bb1c0b449d30f6 100644 --- a/subsys/usb/class/bluetooth.c +++ b/subsys/usb/class/bluetooth.c @@ -200,7 +200,7 @@ static void acl_read_cb(u8_t ep, int size, void *priv) buf = net_buf_alloc(&acl_tx_pool, K_FOREVER); __ASSERT_NO_MSG(buf); - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); /* Start a new read transfer */ usb_transfer(bluetooth_ep_data[HCI_OUT_EP_IDX].ep_addr, buf->data, @@ -269,7 +269,7 @@ static int bluetooth_class_handler(struct usb_setup_packet *setup, return -ENOMEM; } - net_buf_reserve(buf, CONFIG_BT_HCI_RESERVE); + net_buf_reserve(buf, BT_BUF_RESERVE); bt_buf_set_type(buf, BT_BUF_CMD); net_buf_add_mem(buf, *data, *len);