drivers: bluetooth: hci: driver based on RPMsg transport
This commit contributes an RPMsg-based transport for BLE HCI. Signed-off-by: Kamil Piszczek <Kamil.Piszczek@nordicsemi.no>
This commit is contained in:
parent
50ebde1c8b
commit
83107e5ffd
4 changed files with 487 additions and 1 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
185
drivers/bluetooth/hci/rpmsg.c
Normal file
185
drivers/bluetooth/hci/rpmsg.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <init.h>
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_driver.h>
|
||||
|
||||
#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);
|
263
drivers/bluetooth/hci/rpmsg_nrf53.c
Normal file
263
drivers/bluetooth/hci/rpmsg_nrf53.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ipm.h>
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
#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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue