bluetooth: hci: rpmsg: Use RPMsg Service

This patch modifies Bluetooth HCI RPMsg drivers and samples to use
RPMsg Service instead of configuring OpenAMP directly in the driver
or the sample.

Co-authored-by: Piotr Szkotak <piotr.szkotak@nordicsemi.no>
Signed-off-by: Hubert Miś <hubert.mis@nordicsemi.no>
This commit is contained in:
Hubert Miś 2021-01-18 12:41:36 +01:00 committed by Carles Cufí
commit 5548917e69
5 changed files with 68 additions and 471 deletions

View file

@ -112,26 +112,16 @@ config BT_RPMSG_NRF53
bool "nRF53 configuration of RPMsg" bool "nRF53 configuration of RPMsg"
default y if SOC_NRF5340_CPUAPP default y if SOC_NRF5340_CPUAPP
depends on BT_RPMSG depends on BT_RPMSG
select IPM select RPMSG_SERVICE
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 help
Enable RPMsg configuration for nRF53. Two channels of the IPM driver 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. are used in the HCI driver: channel 0 for TX and channel 1 for RX.
if BT_RPMSG_NRF53 if BT_RPMSG_NRF53
config BT_RPMSG_NRF53_RX_STACK_SIZE choice RPMSG_SERVICE_MODE
int "RPMsg stack size for RX thread" default RPMSG_SERVICE_MODE_MASTER
default 1024 endchoice
config BT_RPMSG_NRF53_RX_PRIO
int "RPMsg RX thread priority"
default 8
endif # BT_RPMSG_NRF53 endif # BT_RPMSG_NRF53

View file

@ -23,6 +23,7 @@
int bt_rpmsg_platform_init(void); int bt_rpmsg_platform_init(void);
int bt_rpmsg_platform_send(struct net_buf *buf); int bt_rpmsg_platform_send(struct net_buf *buf);
int bt_rpmsg_platform_endpoint_is_bound(void);
static bool is_hci_event_discardable(const uint8_t *evt_data) static bool is_hci_event_discardable(const uint8_t *evt_data)
{ {
@ -234,7 +235,10 @@ static int bt_rpmsg_open(void)
{ {
BT_DBG(""); BT_DBG("");
return bt_rpmsg_platform_init(); while (!bt_rpmsg_platform_endpoint_is_bound()) {
k_sleep(K_MSEC(1));
}
return 0;
} }
static const struct bt_hci_driver drv = { static const struct bt_hci_driver drv = {
@ -251,7 +255,20 @@ static int bt_rpmsg_init(const struct device *unused)
{ {
ARG_UNUSED(unused); ARG_UNUSED(unused);
return bt_hci_driver_register(&drv); int err;
err = bt_rpmsg_platform_init();
if (err < 0) {
BT_ERR("Failed to initialize BT RPMSG (err %d)", err);
return err;
}
err = bt_hci_driver_register(&drv);
if (err < 0) {
BT_ERR("Failed to register BT HIC driver (err %d)", err);
}
return err;
} }
SYS_INIT(bt_rpmsg_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); SYS_INIT(bt_rpmsg_init, POST_KERNEL, CONFIG_RPMSG_SERVICE_EP_REG_PRIORITY);

View file

@ -6,10 +6,7 @@
#include <drivers/ipm.h> #include <drivers/ipm.h>
#include <openamp/open_amp.h> #include <ipc/rpmsg_service.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 BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_hci_driver_nrf53 #define LOG_MODULE_NAME bt_hci_driver_nrf53
@ -20,107 +17,10 @@ void bt_rpmsg_rx(uint8_t *data, size_t len);
static K_SEM_DEFINE(ready_sem, 0, 1); static K_SEM_DEFINE(ready_sem, 0, 1);
static K_SEM_DEFINE(rx_sem, 0, 1); static K_SEM_DEFINE(rx_sem, 0, 1);
static K_KERNEL_STACK_DEFINE(bt_rpmsg_rx_thread_stack,
CONFIG_BT_RPMSG_NRF53_RX_STACK_SIZE);
static struct k_thread bt_rpmsg_rx_thread_data;
static const struct device *ipm_tx_handle;
static const struct device *ipm_rx_handle;
/* Configuration defines */
#define SHM_NODE DT_CHOSEN(zephyr_ipc_shm)
#define SHM_BASE_ADDRESS DT_REG_ADDR(SHM_NODE)
#define SHM_START_ADDR (SHM_BASE_ADDRESS + 0x400)
#define SHM_SIZE 0x7c00
#define SHM_DEVICE_NAME "sram0.shm"
BUILD_ASSERT((SHM_START_ADDR + SHM_SIZE - SHM_BASE_ADDRESS)
<= DT_REG_SIZE(SHM_NODE),
"Allocated size exceeds available shared memory reserved for IPC");
#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 SHM_BASE_ADDRESS
BUILD_ASSERT(CONFIG_HEAP_MEM_POOL_SIZE >= 1024, BUILD_ASSERT(CONFIG_HEAP_MEM_POOL_SIZE >= 1024,
"Not enough heap memory for RPMsg queue allocation"); "Not enough heap memory for RPMsg queue allocation");
/* End of configuration defines */ static int endpoint_id;
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 uint32_t virtio_get_features(struct virtio_device *vdev)
{
return BIT(VIRTIO_RPMSG_F_NS);
}
static void virtio_set_features(struct virtio_device *vdev, uint32_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(const struct device *dev, void *context,
uint32_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, static int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len,
uint32_t src, void *priv) uint32_t src, void *priv)
@ -133,146 +33,28 @@ static int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len,
return RPMSG_SUCCESS; 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, uint32_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 bt_rpmsg_platform_init(void)
{ {
int err; int err;
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
static struct virtio_vring_info rvrings[2]; err = rpmsg_service_register_endpoint("nrf_bt_hci", endpoint_cb);
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. */ if (err < 0) {
k_thread_create(&bt_rpmsg_rx_thread_data, bt_rpmsg_rx_thread_stack, LOG_ERR("Registering endpoint failed with %d", err);
K_KERNEL_STACK_SIZEOF(bt_rpmsg_rx_thread_stack), return RPMSG_ERR_INIT;
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); endpoint_id = err;
if (err) {
BT_ERR("Couldn't register shared memory device: %d", err);
return err;
}
err = metal_device_open("generic", SHM_DEVICE_NAME, &device); return RPMSG_SUCCESS;
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 */
err = k_sem_take(&ready_sem, K_SECONDS(3));
if (err) {
BT_ERR("No contact with network core EP (err %d)", err);
return err;
}
return 0;
} }
int bt_rpmsg_platform_send(struct net_buf *buf) int bt_rpmsg_platform_send(struct net_buf *buf)
{ {
return rpmsg_send(&ep, buf->data, buf->len); return rpmsg_service_send(endpoint_id, buf->data, buf->len);
}
int bt_rpmsg_platform_endpoint_is_bound(void)
{
return rpmsg_service_endpoint_is_bound(endpoint_id);
} }

View file

@ -1,13 +1,7 @@
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_OPENAMP=y
CONFIG_IPM=y CONFIG_RPMSG_SERVICE=y
CONFIG_IPM_NRFX=y CONFIG_RPMSG_SERVICE_MODE_REMOTE=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_HEAP_MEM_POOL_SIZE=8192
@ -18,3 +12,7 @@ CONFIG_BT_HCI_RAW=y
CONFIG_BT_MAX_CONN=16 CONFIG_BT_MAX_CONN=16
CONFIG_BT_CTLR_ASSERT_HANDLER=y CONFIG_BT_CTLR_ASSERT_HANDLER=y
CONFIG_BT_HCI_RAW_RESERVE=1 CONFIG_BT_HCI_RAW_RESERVE=1
CONFIG_ASSERT=y
CONFIG_DEBUG_INFO=y
CONFIG_EXCEPTION_STACK_TRACE=y

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019 Nordic Semiconductor ASA * Copyright (c) 2019-2021 Nordic Semiconductor ASA
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -21,6 +21,8 @@
#include <metal/device.h> #include <metal/device.h>
#include <metal/alloc.h> #include <metal/alloc.h>
#include <ipc/rpmsg_service.h>
#include <net/buf.h> #include <net/buf.h>
#include <bluetooth/bluetooth.h> #include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h> #include <bluetooth/l2cap.h>
@ -32,108 +34,7 @@
#define LOG_MODULE_NAME hci_rpmsg #define LOG_MODULE_NAME hci_rpmsg
LOG_MODULE_REGISTER(LOG_MODULE_NAME); LOG_MODULE_REGISTER(LOG_MODULE_NAME);
/* Configuration defines */ static int endpoint_id;
#if !DT_HAS_CHOSEN(zephyr_ipc_shm)
#error "Sample requires definition of shared memory for rpmsg"
#endif
#define SHM_NODE DT_CHOSEN(zephyr_ipc_shm)
#define SHM_BASE_ADDRESS DT_REG_ADDR(SHM_NODE)
#define SHM_START_ADDR (SHM_BASE_ADDRESS + 0x400)
#define SHM_SIZE 0x7c00
#define SHM_DEVICE_NAME "sram0.shm"
BUILD_ASSERT((SHM_START_ADDR + SHM_SIZE - SHM_BASE_ADDRESS)
<= DT_REG_SIZE(SHM_NODE),
"Allocated size exceeds available shared memory reserved for IPC");
#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 SHM_BASE_ADDRESS
/* End of configuration defines */
static const struct device *ipm_tx_handle;
static const 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 uint32_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(const struct device *dev, void *context,
uint32_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 K_THREAD_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE);
static struct k_thread tx_thread_data; static struct k_thread tx_thread_data;
@ -323,7 +224,7 @@ static int hci_rpmsg_send(struct net_buf *buf)
net_buf_push_u8(buf, pkt_indicator); net_buf_push_u8(buf, pkt_indicator);
LOG_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:"); LOG_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:");
rpmsg_send(&ep, buf->data, buf->len); rpmsg_service_send(endpoint_id, buf->data, buf->len);
net_buf_unref(buf); net_buf_unref(buf);
@ -346,110 +247,6 @@ int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src
return RPMSG_SUCCESS; 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) void main(void)
{ {
int err; int err;
@ -457,12 +254,6 @@ void main(void)
/* incoming events and data from the controller */ /* incoming events and data from the controller */
static K_FIFO_DEFINE(rx_queue); static K_FIFO_DEFINE(rx_queue);
/* initialize RPMSG */
err = hci_rpmsg_init();
if (err != 0) {
return;
}
LOG_DBG("Start"); LOG_DBG("Start");
/* Enable the raw interface, this will in turn open the HCI driver */ /* Enable the raw interface, this will in turn open the HCI driver */
@ -486,3 +277,22 @@ void main(void)
} }
} }
} }
/* Make sure we register endpoint before RPMsg Service is initialized. */
int register_endpoint(const struct device *arg)
{
int status;
status = rpmsg_service_register_endpoint("nrf_bt_hci", endpoint_cb);
if (status < 0) {
LOG_ERR("Registering endpoint failed with %d", status);
return status;
}
endpoint_id = status;
return 0;
}
SYS_INIT(register_endpoint, POST_KERNEL, CONFIG_RPMSG_SERVICE_EP_REG_PRIORITY);