diff --git a/CODEOWNERS b/CODEOWNERS index 15918b4cb164e..007833e98975b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -552,6 +552,7 @@ /subsys/fs/fuse_fs_access.c @vanwinkeljan /subsys/fs/littlefs_fs.c @pabigot /subsys/fs/nvs/ @Laczen +/subsys/ipc/ @ioannisg /subsys/logging/ @nordic-krch /subsys/logging/log_backend_net.c @nordic-krch @jukkar /subsys/lorawan/ @Mani-Sadhasivam diff --git a/boards/arm/nrf5340dk_nrf5340/Kconfig b/boards/arm/nrf5340dk_nrf5340/Kconfig index f2cf86257b97f..f7ab2d18a4482 100644 --- a/boards/arm/nrf5340dk_nrf5340/Kconfig +++ b/boards/arm/nrf5340dk_nrf5340/Kconfig @@ -1,8 +1,44 @@ # nRF5340 (P)DK board configuration -# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA +# Copyright (c) 2019 - 2021 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 +config IPM_NRFX + default IPM + +config RPMSG_SERVICE_DUAL_IPM_SUPPORT + default RPMSG_SERVICE + +if RPMSG_SERVICE_DUAL_IPM_SUPPORT + +config IPM_MSG_CH_0_ENABLE + default y + +config IPM_MSG_CH_1_ENABLE + default y + +config RPMSG_SERVICE_IPM_TX_NAME + default "IPM_0" if RPMSG_SERVICE_MODE_MASTER + default "IPM_1" if RPMSG_SERVICE_MODE_REMOTE + +config RPMSG_SERVICE_IPM_RX_NAME + default "IPM_1" if RPMSG_SERVICE_MODE_MASTER + default "IPM_0" if RPMSG_SERVICE_MODE_REMOTE + +config IPM_MSG_CH_0_TX + default RPMSG_SERVICE_MODE_MASTER + +config IPM_MSG_CH_0_RX + default RPMSG_SERVICE_MODE_REMOTE + +config IPM_MSG_CH_1_TX + default RPMSG_SERVICE_MODE_REMOTE + +config IPM_MSG_CH_1_RX + default RPMSG_SERVICE_MODE_MASTER + +endif # RPMSG_SERVICE_DUAL_IPM_SUPPORT + if BOARD_NRF5340PDK_NRF5340_CPUAPP || BOARD_NRF5340PDK_NRF5340_CPUAPPNS || BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_NRF5340DK_NRF5340_CPUAPPNS config BOARD_ENABLE_DCDC_APP diff --git a/boards/arm/nrf5340dk_nrf5340/nrf5340_cpunet_reset.c b/boards/arm/nrf5340dk_nrf5340/nrf5340_cpunet_reset.c index 28da9938d154a..8cff522478a67 100644 --- a/boards/arm/nrf5340dk_nrf5340/nrf5340_cpunet_reset.c +++ b/boards/arm/nrf5340dk_nrf5340/nrf5340_cpunet_reset.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Nordic Semiconductor ASA. + * Copyright (c) 2019-2021 Nordic Semiconductor ASA. * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,13 +12,6 @@ LOG_MODULE_REGISTER(nrf5340pdk_nrf5340_cpuapp, CONFIG_LOG_DEFAULT_LEVEL); -/* Shared memory definitions */ -#if DT_HAS_CHOSEN(zephyr_ipc_shm) -#define SHM_NODE DT_CHOSEN(zephyr_ipc_shm) -#define SHM_BASE_ADDRESS DT_REG_ADDR(SHM_NODE) -#define SHM_SIZE DT_REG_SIZE(SHM_NODE) -#endif - #if !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) /* This should come from DTS, possibly an overlay. */ @@ -83,14 +76,6 @@ static int remoteproc_mgr_boot(const struct device *dev) * this case do the remainder of actions to properly configure and * boot the Network MCU. */ -#if defined(SHM_BASE_ADDRESS) && (SHM_BASE_ADDRESS != 0) - - /* Initialize inter-processor shared memory block to zero. It is - * assumed that the application image has access to the shared - * memory at this point (see #24147). - */ - memset((void *) SHM_BASE_ADDRESS, 0, SHM_SIZE); -#endif /* Release the Network MCU, 'Release force off signal' */ NRF_RESET->NETWORK.FORCEOFF = RESET_NETWORK_FORCEOFF_FORCEOFF_Release; diff --git a/drivers/bluetooth/hci/Kconfig b/drivers/bluetooth/hci/Kconfig index 198d07ac4ace3..2e7ec5e978105 100644 --- a/drivers/bluetooth/hci/Kconfig +++ b/drivers/bluetooth/hci/Kconfig @@ -112,26 +112,16 @@ config BT_RPMSG_NRF53 bool "nRF53 configuration of RPMsg" default y if SOC_NRF5340_CPUAPP depends on BT_RPMSG - 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 + select RPMSG_SERVICE 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 +choice RPMSG_SERVICE_MODE + default RPMSG_SERVICE_MODE_MASTER +endchoice endif # BT_RPMSG_NRF53 diff --git a/drivers/bluetooth/hci/rpmsg.c b/drivers/bluetooth/hci/rpmsg.c index 4706d2c3b52a1..d679af6cce1d2 100644 --- a/drivers/bluetooth/hci/rpmsg.c +++ b/drivers/bluetooth/hci/rpmsg.c @@ -23,6 +23,7 @@ int bt_rpmsg_platform_init(void); 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) { @@ -234,7 +235,10 @@ static int bt_rpmsg_open(void) { 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 = { @@ -251,7 +255,20 @@ static int bt_rpmsg_init(const struct device *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); diff --git a/drivers/bluetooth/hci/rpmsg_nrf53.c b/drivers/bluetooth/hci/rpmsg_nrf53.c index 62afda90f60d2..4f07c92f507d3 100644 --- a/drivers/bluetooth/hci/rpmsg_nrf53.c +++ b/drivers/bluetooth/hci/rpmsg_nrf53.c @@ -6,10 +6,7 @@ #include -#include -#include -#include -#include +#include #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) #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(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, "Not enough heap memory for RPMsg queue allocation"); -/* 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 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_id; static int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, 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; } -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 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; + err = rpmsg_service_register_endpoint("nrf_bt_hci", endpoint_cb); - /* Setup thread for RX data processing. */ - k_thread_create(&bt_rpmsg_rx_thread_data, bt_rpmsg_rx_thread_stack, - K_KERNEL_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; + if (err < 0) { + LOG_ERR("Registering endpoint failed with %d", err); + return RPMSG_ERR_INIT; } - err = metal_register_generic_device(&shm_device); - if (err) { - BT_ERR("Couldn't register shared memory device: %d", err); - return err; - } + endpoint_id = 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 */ - 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; + return RPMSG_SUCCESS; } 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); } diff --git a/include/ipc/rpmsg_service.h b/include/ipc/rpmsg_service.h new file mode 100644 index 0000000000000..8a010a1f4c41b --- /dev/null +++ b/include/ipc/rpmsg_service.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_RPMSG_SERVICE_RPMSG_SERVICE_H_ +#define ZEPHYR_INCLUDE_RPMSG_SERVICE_RPMSG_SERVICE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief RPMsg service API + * @defgroup rpmsg_service_api RPMsg service APIs + * @{ + */ + +/** + * @brief Register IPC endpoint + * + * Registers IPC endpoint to enable communication with a remote device. + * The endpoint is created when the slave device registers it. + * + * The same function registers endpoints for both master and slave devices. + * + * @param name String containing the name of the endpoint. Must be identical + * for master and slave + * @param cb Callback executed when data are available on given endpoint + * + * @retval >=0 id of registered endpoint on success; + * @retval -EINPROGRESS when requested to register an endpoint after endpoints + * creation procedure has started; + * @retval -ENOMEM when there is not enough slots to register the endpoint; + * @retval <0 an other negative errno code, reported by rpmsg. + */ +int rpmsg_service_register_endpoint(const char *name, rpmsg_ept_cb cb); + +/** + * @brief Send data using given IPC endpoint + * + * @param endpoint_id Id of registered endpoint, obtained by + * @ref rpmsg_service_register_endpoint + * @param data Pointer to the buffer to send through RPMsg service + * @param len Number of bytes to send. + * + * @retval >=0 number of sent bytes; + * @retval <0 an error code, reported by rpmsg. + */ +int rpmsg_service_send(int endpoint_id, const void *data, size_t len); + +/** + * @brief Check if endpoint is bound. + * + * Checks if remote endpoint has been created + * and the master has bound its endpoint to it. + * + * @param endpoint_id Id of registered endpoint, obtained by + * @ref rpmsg_service_register_endpoint + * + * @retval true endpoint is bound + * @retval false endpoint not bound + */ +bool rpmsg_service_endpoint_is_bound(int endpoint_id); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_RPMSG_SERVICE_RPMSG_SERVICE_H_ */ diff --git a/modules/hal_nordic/Kconfig b/modules/hal_nordic/Kconfig index 529d5eb1a9fc0..5b90002c5bab5 100644 --- a/modules/hal_nordic/Kconfig +++ b/modules/hal_nordic/Kconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Nordic Semiconductor ASA +# Copyright (c) 2016-2021 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 config ZEPHYR_HAL_NORDIC_MODULE @@ -107,13 +107,7 @@ config NRF_802154_SER_HOST bool "nRF IEEE 802.15.4 Driver serialization host" depends on !NRF_802154_RADIO_DRIVER depends on !HAS_HW_NRF_RADIO_IEEE802154 - select IPM - select IPM_NRFX - select IPM_MSG_CH_0_ENABLE - select IPM_MSG_CH_1_ENABLE - select IPM_MSG_CH_0_TX - select IPM_MSG_CH_1_RX - select OPENAMP + select RPMSG_SERVICE select IEEE802154_NRF5_EXT_IRQ_MGMT if IEEE802154_NRF5 help Enable serialization of nRF IEEE 802.15.4 Driver. This option is to be @@ -124,13 +118,7 @@ menuconfig NRF_802154_SER_RADIO bool "nRF IEEE 802.15.4 Driver serialization radio" depends on HAS_HW_NRF_RADIO_IEEE802154 depends on !IEEE802154_NRF5 - select IPM - select IPM_NRFX - select IPM_MSG_CH_0_ENABLE - select IPM_MSG_CH_1_ENABLE - select IPM_MSG_CH_0_RX - select IPM_MSG_CH_1_TX - select OPENAMP + select RPMSG_SERVICE select NRF_802154_RADIO_DRIVER help Enable serialization of nRF IEEE 802.15.4 Driver. This option is to be @@ -146,6 +134,10 @@ config NRF_802154_SER_RADIO_INIT_PRIO Set the initialization priority number. Do not mess with it unless you know what you are doing. +choice RPMSG_SERVICE_MODE + default RPMSG_SERVICE_MODE_REMOTE +endchoice + endif menu "nRF 802.15.4 serialization" @@ -181,6 +173,10 @@ config NRF_802154_RX_BUFFERS Number of buffers in nRF 802.15.4 driver serialization host's receive queue. If this value is modified, its remote counterpart must be set to the exact same value. +choice RPMSG_SERVICE_MODE + default RPMSG_SERVICE_MODE_MASTER +endchoice + endif endmenu # NRF_802154_SER_HOST || NRF_802154_SER_RADIO diff --git a/samples/bluetooth/hci_rpmsg/prj.conf b/samples/bluetooth/hci_rpmsg/prj.conf index d3445b936685b..46a1992edc763 100644 --- a/samples/bluetooth/hci_rpmsg/prj.conf +++ b/samples/bluetooth/hci_rpmsg/prj.conf @@ -1,13 +1,7 @@ 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_RPMSG_SERVICE=y +CONFIG_RPMSG_SERVICE_MODE_REMOTE=y CONFIG_HEAP_MEM_POOL_SIZE=8192 @@ -18,3 +12,7 @@ CONFIG_BT_HCI_RAW=y CONFIG_BT_MAX_CONN=16 CONFIG_BT_CTLR_ASSERT_HANDLER=y CONFIG_BT_HCI_RAW_RESERVE=1 + +CONFIG_ASSERT=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y diff --git a/samples/bluetooth/hci_rpmsg/src/main.c b/samples/bluetooth/hci_rpmsg/src/main.c index 68dfe7abe9f9e..d328dc28718c0 100644 --- a/samples/bluetooth/hci_rpmsg/src/main.c +++ b/samples/bluetooth/hci_rpmsg/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Nordic Semiconductor ASA + * Copyright (c) 2019-2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -32,108 +34,7 @@ #define LOG_MODULE_NAME hci_rpmsg LOG_MODULE_REGISTER(LOG_MODULE_NAME); -/* Configuration defines */ -#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 int endpoint_id; static K_THREAD_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); 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); 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); @@ -346,110 +247,6 @@ int endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src 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; @@ -457,12 +254,6 @@ void main(void) /* 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 */ @@ -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); diff --git a/samples/subsys/ipc/rpmsg_service/CMakeLists.txt b/samples/subsys/ipc/rpmsg_service/CMakeLists.txt new file mode 100644 index 0000000000000..f8e09d594e625 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.13.1) +# Copyright (c) 2019 Linaro Limited +# Copyright (c) 2018-2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +set(REMOTE_ZEPHYR_DIR ${CMAKE_CURRENT_BINARY_DIR}/rpmsg_service_remote-prefix/src/rpmsg_service_remote-build/zephyr) + +if("${BOARD}" STREQUAL "nrf5340dk_nrf5340_cpuapp") + set(BOARD_REMOTE "nrf5340dk_nrf5340_cpunet") +elseif("${BOARD}" STREQUAL "lpcxpresso54114_m4") + set(BOARD_REMOTE "lpcxpresso54114_m0") +elseif("${BOARD}" STREQUAL "mps2_an521") + set(QEMU_EXTRA_FLAGS "-device;loader,file=${REMOTE_ZEPHYR_DIR}/zephyr.elf") + set(BOARD_REMOTE "mps2_an521_nonsecure") +elseif("${BOARD}" STREQUAL "v2m_musca_b1") + set(BOARD_REMOTE "v2m_musca_b1_nonsecure") +else() + message(FATAL_ERROR "${BOARD} was not supported for this sample") +endif() + +message(INFO " ${BOARD} compile as Master in this sample") + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(rpmsg_service) + +enable_language(C ASM) + +target_sources(app PRIVATE src/main.c) + +include(ExternalProject) + +ExternalProject_Add( + rpmsg_service_remote + SOURCE_DIR ${APPLICATION_SOURCE_DIR}/remote + INSTALL_COMMAND "" # This particular build system has no install command + CMAKE_CACHE_ARGS -DBOARD:STRING=${BOARD_REMOTE} + CMAKE_CACHE_ARGS -DDTC_OVERLAY_FILE:STRING=${DTC_OVERLAY_FILE} + BUILD_BYPRODUCTS "${REMOTE_ZEPHYR_DIR}/${KERNEL_BIN_NAME}" + # NB: Do we need to pass on more CMake variables? + BUILD_ALWAYS True +) + +if(("${BOARD}" STREQUAL "lpcxpresso54114_m4")) + add_dependencies(core_m0_inc_target rpmsg_service_remote) +endif() + +target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/samples/subsys/ipc/rpmsg_service/README.rst b/samples/subsys/ipc/rpmsg_service/README.rst new file mode 100644 index 0000000000000..bb05b0b01ee12 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/README.rst @@ -0,0 +1,75 @@ +.. _RPMsg_Service_sample: + +RPMsg Service sample Application +################################ + +Overview +******** + +RPMsg Service is an abstraction created over OpenAMP that makes initialization +and endpoints creation process easier. +This application demonstrates how to use RPMsg Service in Zephyr. It is designed +to demonstrate how to integrate RPMsg Service with Zephyr both from a build +perspective and code. + +Building the application for nrf5340dk_nrf5340_cpuapp +***************************************************** + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/ipc/rpmsg_service + :board: nrf5340dk_nrf5340_cpuapp + :goals: debug + +Building the application for mps2_an521 +*************************************** + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/ipc/rpmsg_service + :board: mps2_an521 + :goals: debug + +Building the application for v2m_musca_b1 +***************************************** + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/ipc/rpmsg_service + :board: v2m_musca_b1 + :goals: debug + +Open a serial terminal (minicom, putty, etc.) and connect the board with the +following settings: + +- Speed: 115200 +- Data: 8 bits +- Parity: None +- Stop bits: 1 + +Reset the board and the following message will appear on the corresponding +serial port, one is master another is remote: + +.. code-block:: console + + **** Booting Zephyr OS build zephyr-v1.14.0-2064-g888fc98fddaa **** + Starting application thread! + + RPMsg Service [master] demo started + Master core received a message: 1 + Master core received a message: 3 + Master core received a message: 5 + ... + Master core received a message: 99 + RPMsg Service demo ended. + + +.. code-block:: console + + **** Booting Zephyr OS build zephyr-v1.14.0-2064-g888fc98fddaa **** + Starting application thread! + + RPMsg Service [remote] demo started + Remote core received a message: 0 + Remote core received a message: 2 + Remote core received a message: 4 + ... + Remote core received a message: 98 + RPMsg Service demo ended. diff --git a/samples/subsys/ipc/rpmsg_service/boards/mps2_an521.overlay b/samples/subsys/ipc/rpmsg_service/boards/mps2_an521.overlay new file mode 100644 index 0000000000000..aff3c460cbc09 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/boards/mps2_an521.overlay @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + /* + * shared memory reserved for the inter-processor communication + */ + zephyr,ipc_shm = &sramx; + zephyr,ipc = &mhu0; + }; + + sramx: memory@28180000 { + compatible = "mmio-sram"; + reg = <0x28180000 0x8000>; + }; +}; diff --git a/samples/subsys/ipc/rpmsg_service/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/subsys/ipc/rpmsg_service/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 0000000000000..0af6c0de76911 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1 @@ +CONFIG_BOARD_ENABLE_CPUNET=y diff --git a/samples/subsys/ipc/rpmsg_service/boards/v2m_musca_b1.overlay b/samples/subsys/ipc/rpmsg_service/boards/v2m_musca_b1.overlay new file mode 100644 index 0000000000000..a2473c4cc09b2 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/boards/v2m_musca_b1.overlay @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + /* + * shared memory reserved for the inter-processor communication + */ + zephyr,ipc_shm = &sramx; + zephyr,ipc = &mhu0; + }; + + sramx: memory@20060000 { + compatible = "mmio-sram"; + reg = <0x20060000 0x8000>; + }; +}; diff --git a/samples/subsys/ipc/rpmsg_service/prj.conf b/samples/subsys/ipc/rpmsg_service/prj.conf new file mode 100644 index 0000000000000..dc7dba36ca73b --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/prj.conf @@ -0,0 +1,8 @@ +CONFIG_PRINTK=y +CONFIG_IPM=y +CONFIG_TIMESLICE_SIZE=1 +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_RPMSG_SERVICE=y +CONFIG_RPMSG_SERVICE_MODE_MASTER=y +CONFIG_OPENAMP_SLAVE=n diff --git a/samples/subsys/ipc/rpmsg_service/remote/CMakeLists.txt b/samples/subsys/ipc/rpmsg_service/remote/CMakeLists.txt new file mode 100644 index 0000000000000..81c5ccf9d96e0 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/remote/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.13.1) +# Copyright (c) 2019 Linaro Limited +# Copyright (c) 2018-2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +if("${BOARD}" STREQUAL "nrf5340dk_nrf5340_cpunet" + OR "${BOARD}" STREQUAL "lpcxpresso54114_m0" + OR "${BOARD}" STREQUAL "mps2_an521_nonsecure" + OR "${BOARD}" STREQUAL "v2m_musca_b1_nonsecure") + message(INFO " ${BOARD} compile as slave in this sample") +else() + message(FATAL_ERROR "${BOARD} was not supported for this sample") +endif() + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(rpmsg_service_remote) + +target_sources(app PRIVATE src/main.c) +target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) diff --git a/samples/subsys/ipc/rpmsg_service/remote/boards/lpcxpresso54114_m0.conf b/samples/subsys/ipc/rpmsg_service/remote/boards/lpcxpresso54114_m0.conf new file mode 100644 index 0000000000000..2360fff8e4235 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/remote/boards/lpcxpresso54114_m0.conf @@ -0,0 +1 @@ +CONFIG_IPM_MCUX=y diff --git a/samples/subsys/ipc/rpmsg_service/remote/prj.conf b/samples/subsys/ipc/rpmsg_service/remote/prj.conf new file mode 100644 index 0000000000000..cf83e5b59a53d --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/remote/prj.conf @@ -0,0 +1,8 @@ +CONFIG_STDOUT_CONSOLE=n +CONFIG_PRINTK=n +CONFIG_IPM=y +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096 +CONFIG_RPMSG_SERVICE=y +CONFIG_RPMSG_SERVICE_MODE_REMOTE=y +CONFIG_OPENAMP_MASTER=n diff --git a/samples/subsys/ipc/rpmsg_service/remote/src/main.c b/samples/subsys/ipc/rpmsg_service/remote/src/main.c new file mode 100644 index 0000000000000..45e4c83886088 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/remote/src/main.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018, NXP + * Copyright (c) 2018-2019, Linaro Limited + * Copyright (c) 2018-2021, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define APP_TASK_STACK_SIZE (1024) +K_THREAD_STACK_DEFINE(thread_stack, APP_TASK_STACK_SIZE); +static struct k_thread thread_data; + +static volatile unsigned int received_data; + +static K_SEM_DEFINE(data_sem, 0, 1); +static K_SEM_DEFINE(data_rx_sem, 0, 1); + +int endpoint_cb(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv) +{ + received_data = *((unsigned int *) data); + + k_sem_give(&data_rx_sem); + + return RPMSG_SUCCESS; +} + +static int ep_id; + +static unsigned int receive_message(void) +{ + k_sem_take(&data_rx_sem, K_FOREVER); + return received_data; +} + +static int send_message(unsigned int message) +{ + return rpmsg_service_send(ep_id, &message, sizeof(message)); +} + +void app_task(void *arg1, void *arg2, void *arg3) +{ + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + int status = 0; + unsigned int message = 0U; + + printk("\r\nRPMsg Service [remote] demo started\r\n"); + + while (message < 99) { + message = receive_message(); + printk("Remote core received a message: %d\n", message); + + message++; + status = send_message(message); + if (status < 0) { + printk("send_message(%d) failed with status %d\n", + message, status); + break; + } + } + + printk("RPMsg Service demo ended.\n"); +} + +void main(void) +{ + printk("Starting application thread!\n"); + k_thread_create(&thread_data, thread_stack, APP_TASK_STACK_SIZE, + (k_thread_entry_t)app_task, + NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); +} + +/* Make sure we register endpoint before RPMsg Service is initialized. */ +int register_endpoint(const struct device *arg) +{ + int status; + + status = rpmsg_service_register_endpoint("demo", endpoint_cb); + + if (status < 0) { + printk("rpmsg_create_ept failed %d\n", status); + return status; + } + + ep_id = status; + + return 0; +} + +SYS_INIT(register_endpoint, POST_KERNEL, CONFIG_RPMSG_SERVICE_EP_REG_PRIORITY); diff --git a/samples/subsys/ipc/rpmsg_service/sample.yaml b/samples/subsys/ipc/rpmsg_service/sample.yaml new file mode 100644 index 0000000000000..1210167532d9a --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/sample.yaml @@ -0,0 +1,15 @@ +sample: + description: This app provides an example of how to integrate + RPMsg Service with Zephyr. + name: RPMsg Service example integration +tests: + sample.ipc.rpmsg_service: + platform_allow: nrf5340dk_nrf5340_cpuapp mps2_an521 v2m_musca_b1 + tags: ipm + harness: console + harness_config: + type: multi_line + regex: + - "Master core received a message: 1" + - "Master core received a message: 99" + - "RPMsg Service demo ended." diff --git a/samples/subsys/ipc/rpmsg_service/src/main.c b/samples/subsys/ipc/rpmsg_service/src/main.c new file mode 100644 index 0000000000000..7b56e56f107b4 --- /dev/null +++ b/samples/subsys/ipc/rpmsg_service/src/main.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018, NXP + * Copyright (c) 2018-2019, Linaro Limited + * Copyright (c) 2018-2021, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define APP_TASK_STACK_SIZE (1024) +K_THREAD_STACK_DEFINE(thread_stack, APP_TASK_STACK_SIZE); +static struct k_thread thread_data; + +static volatile unsigned int received_data; + +static K_SEM_DEFINE(data_rx_sem, 0, 1); + +int endpoint_cb(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv) +{ + received_data = *((unsigned int *) data); + + k_sem_give(&data_rx_sem); + + return RPMSG_SUCCESS; +} + +static int ep_id; +struct rpmsg_endpoint my_ept; +struct rpmsg_endpoint *ep = &my_ept; + +static unsigned int receive_message(void) +{ + k_sem_take(&data_rx_sem, K_FOREVER); + return received_data; +} + +static int send_message(unsigned int message) +{ + return rpmsg_service_send(ep_id, &message, sizeof(message)); +} + +void app_task(void *arg1, void *arg2, void *arg3) +{ + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + int status = 0; + unsigned int message = 0U; + + printk("\r\nRPMsg Service [master] demo started\r\n"); + + /* Since we are using name service, we need to wait for a response + * from NS setup and than we need to process it + */ + while (!rpmsg_service_endpoint_is_bound(ep_id)) { + k_sleep(K_MSEC(1)); + } + + while (message < 100) { + status = send_message(message); + if (status < 0) { + printk("send_message(%d) failed with status %d\n", + message, status); + break; + } + + message = receive_message(); + printk("Master core received a message: %d\n", message); + + message++; + } + + printk("RPMsg Service demo ended.\n"); +} + +void main(void) +{ + printk("Starting application thread!\n"); + k_thread_create(&thread_data, thread_stack, APP_TASK_STACK_SIZE, + (k_thread_entry_t)app_task, + NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); + +#if defined(CONFIG_SOC_MPS2_AN521) || \ + defined(CONFIG_SOC_V2M_MUSCA_A) || \ + defined(CONFIG_SOC_V2M_MUSCA_B1) + wakeup_cpu1(); + k_msleep(500); +#endif /* #if defined(CONFIG_SOC_MPS2_AN521) */ +} + +/* Make sure we register endpoint before RPMsg Service is initialized. */ +int register_endpoint(const struct device *arg) +{ + int status; + + status = rpmsg_service_register_endpoint("demo", endpoint_cb); + + if (status < 0) { + printk("rpmsg_create_ept failed %d\n", status); + return status; + } + + ep_id = status; + + return 0; +} + +SYS_INIT(register_endpoint, POST_KERNEL, CONFIG_RPMSG_SERVICE_EP_REG_PRIORITY); diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index d528abafbfce1..80d7a838b35be 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory_ifdef(CONFIG_CPLUSPLUS cpp) add_subdirectory_ifdef(CONFIG_DISK_ACCESS disk) add_subdirectory_ifdef(CONFIG_EMUL emul) add_subdirectory(fs) +add_subdirectory(ipc) add_subdirectory(mgmt) add_subdirectory_ifdef(CONFIG_MCUBOOT_IMG_MANAGER dfu) add_subdirectory_ifdef(CONFIG_NET_BUF net) diff --git a/subsys/Kconfig b/subsys/Kconfig index fb64e0add7aab..6c5a95d671f00 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -1,6 +1,7 @@ # Subsystem configuration options # Copyright (c) 2016-2017 Intel Corporation +# Copyright (c) 2021 Nordic Semiconductor # SPDX-License-Identifier: Apache-2.0 menu "Sub Systems and OS Services" @@ -23,6 +24,8 @@ source "subsys/fb/Kconfig" source "subsys/fs/Kconfig" +source "subsys/ipc/Kconfig" + source "subsys/jwt/Kconfig" source "subsys/logging/Kconfig" diff --git a/subsys/ipc/CMakeLists.txt b/subsys/ipc/CMakeLists.txt new file mode 100644 index 0000000000000..41bb76fc997b1 --- /dev/null +++ b/subsys/ipc/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_RPMSG_SERVICE rpmsg_service) diff --git a/subsys/ipc/Kconfig b/subsys/ipc/Kconfig new file mode 100644 index 0000000000000..c2a106d3ebdb6 --- /dev/null +++ b/subsys/ipc/Kconfig @@ -0,0 +1,10 @@ +# IPC subsystem configuration options + +# Copyright (c) 2021 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +menu "Inter Processor Communication" + +source "subsys/ipc/rpmsg_service/Kconfig" + +endmenu diff --git a/subsys/ipc/rpmsg_service/CMakeLists.txt b/subsys/ipc/rpmsg_service/CMakeLists.txt new file mode 100644 index 0000000000000..357a2bc6c3f8c --- /dev/null +++ b/subsys/ipc/rpmsg_service/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources(rpmsg_backend.c) +zephyr_sources(rpmsg_service.c) diff --git a/subsys/ipc/rpmsg_service/Kconfig b/subsys/ipc/rpmsg_service/Kconfig new file mode 100644 index 0000000000000..1c053f0bfe5d8 --- /dev/null +++ b/subsys/ipc/rpmsg_service/Kconfig @@ -0,0 +1,122 @@ +# Copyright (c) 2020-2021 Nordic Semiconductor (ASA) +# SPDX-License-Identifier: Apache-2.0 + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_IPC_SHM := zephyr,ipc_shm +DT_CHOSEN_Z_IPC := zephyr,ipc +DT_CHOSEN_Z_IPC_TX := zephyr,ipc_tx +DT_CHOSEN_Z_IPC_RX := zephyr,ipc_rx + +config RPMSG_SERVICE_SINGLE_IPM_SUPPORT + bool + default $(dt_chosen_enabled,$(DT_CHOSEN_Z_IPC)) + help + This option must be selected when single IPM is used for + both TX and RX communication + +config RPMSG_SERVICE_DUAL_IPM_SUPPORT + bool + default $(dt_chosen_enabled,$(DT_CHOSEN_Z_IPC_TX)) && \ + $(dt_chosen_enabled,$(DT_CHOSEN_Z_IPC_RX)) + help + This option must be selected when separate IPMs are used for + TX and RX communication + +menuconfig RPMSG_SERVICE + bool "RPMsg service for multiple users" + select IPM + select OPENAMP + help + Enables support for a service that can be shared by multiple + users to establish RPMsg endpoints for given channel. + +if RPMSG_SERVICE + +config RPMSG_SERVICE_SHM_BASE_ADDRESS + hex + default "$(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_IPC_SHM))" + help + This option specifies base address of the memory region to + be used for the OpenAMP IPC shared memory + +config RPMSG_SERVICE_SHM_SIZE + hex + default "$(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_IPC_SHM))" + help + This option specifies size of the memory region to be used + for the OpenAMP IPC shared memory + +if RPMSG_SERVICE_SINGLE_IPM_SUPPORT + +config RPMSG_SERVICE_IPM_NAME + string + default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC))" + help + This option specifies the IPM device name to be used + +endif # RPMSG_SERVICE_SINGLE_IPM_SUPPORT + +if RPMSG_SERVICE_DUAL_IPM_SUPPORT + +config RPMSG_SERVICE_IPM_TX_NAME + string + default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC_TX))" + help + This option specifies the IPM device name to be used for + TX communication + +config RPMSG_SERVICE_IPM_RX_NAME + string + default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC_RX))" + help + This option specifies the IPM device name to be used for + RX communication + +endif # RPMSG_SERVICE_DUAL_IPM_SUPPORT + +choice RPMSG_SERVICE_MODE + prompt "RPMsg Service mode" + +config RPMSG_SERVICE_MODE_MASTER + bool "RPMsg master" + select OPENAMP_MASTER + +config RPMSG_SERVICE_MODE_REMOTE + bool "RPMsg remote" + select OPENAMP_SLAVE + +endchoice + +config RPMSG_SERVICE_NUM_ENDPOINTS + int "Max number of registered endpoints" + default 2 + help + Maximal number of endpoints that can be registered for given + RPMsg service. + +config RPMSG_SERVICE_WORK_QUEUE_STACK_SIZE + int "Size of RX work queue stack" + default 2048 + help + Size of stack used by work queue RX thread. This work queue is + created in the RPMsg Service backend module to prevent notifying + service users about received data from the system work queue. + +config RPMSG_SERVICE_INIT_PRIORITY + int "Initialization priority of RPMsg service" + default 48 + help + The order of RPMsg Service initialization and endpoints registration + is important to avoid race conditions in RPMsg endpoints handshake. + + If in doubt, do not modify this value. + +config RPMSG_SERVICE_EP_REG_PRIORITY + int "Initialization priority of modules registering RPMsg endpoints" + default 47 + help + The endpoints must be registered before RPMsg Service is initialized. + + If in doubt, do not modify this value. + +endif # RPMSG_SERVICE diff --git a/subsys/ipc/rpmsg_service/rpmsg_backend.c b/subsys/ipc/rpmsg_service/rpmsg_backend.c new file mode 100644 index 0000000000000..bd5e7bf8a22bc --- /dev/null +++ b/subsys/ipc/rpmsg_service/rpmsg_backend.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2021, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "rpmsg_backend.h" + +#include +#include +#include +#include + +#include +#include + +#define LOG_LEVEL LOG_LEVEL_INFO +#define LOG_MODULE_NAME rpmsg_backend +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +/* Configuration defines */ +#if !DT_HAS_CHOSEN(zephyr_ipc_shm) +#error "Module requires definition of shared memory for rpmsg" +#endif + +#define MASTER IS_ENABLED(CONFIG_RPMSG_SERVICE_MODE_MASTER) + +#if MASTER +#define VIRTQUEUE_ID 0 +#define RPMSG_ROLE RPMSG_MASTER +#else +#define VIRTQUEUE_ID 1 +#define RPMSG_ROLE RPMSG_REMOTE +#endif + +/* Configuration defines */ + +#define VRING_COUNT 2 +#define VRING_RX_ADDRESS (VDEV_START_ADDR + SHM_SIZE - VDEV_STATUS_SIZE) +#define VRING_TX_ADDRESS (VDEV_START_ADDR + SHM_SIZE) +#define VRING_ALIGNMENT 4 +#define VRING_SIZE 16 + +#define IPM_WORK_QUEUE_STACK_SIZE CONFIG_RPMSG_SERVICE_WORK_QUEUE_STACK_SIZE + +#if IS_ENABLED(CONFIG_COOP_ENABLED) +#define IPM_WORK_QUEUE_PRIORITY -1 +#else +#define IPM_WORK_QUEUE_PRIORITY 0 +#endif + +K_THREAD_STACK_DEFINE(ipm_stack_area, IPM_WORK_QUEUE_STACK_SIZE); + +struct k_work_q ipm_work_q; + +/* End of configuration defines */ + +#if defined(CONFIG_RPMSG_SERVICE_DUAL_IPM_SUPPORT) +static const struct device *ipm_tx_handle; +static const struct device *ipm_rx_handle; +#elif defined(CONFIG_RPMSG_SERVICE_SINGLE_IPM_SUPPORT) +static const struct device *ipm_handle; +#endif + +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, + { + { + .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 virtio_vring_info rvrings[2] = { + [0] = { + .info.align = VRING_ALIGNMENT, + }, + [1] = { + .info.align = VRING_ALIGNMENT, + }, +}; +static struct virtqueue *vq[2]; + +static struct k_work ipm_work; + +static unsigned char virtio_get_status(struct virtio_device *vdev) +{ +#if MASTER + return VIRTIO_CONFIG_STATUS_DRIVER_OK; +#else + return sys_read8(VDEV_STATUS_ADDR); +#endif +} + +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) +{ +} + +static void virtio_notify(struct virtqueue *vq) +{ + int status; + +#if defined(CONFIG_RPMSG_SERVICE_DUAL_IPM_SUPPORT) + status = ipm_send(ipm_tx_handle, 0, 0, NULL, 0); +#elif defined(CONFIG_RPMSG_SERVICE_SINGLE_IPM_SUPPORT) + +#if defined(CONFIG_SOC_MPS2_AN521) || \ + defined(CONFIG_SOC_V2M_MUSCA_A) || \ + defined(CONFIG_SOC_V2M_MUSCA_B1) + uint32_t current_core = sse_200_platform_get_cpu_id(); + + status = ipm_send(ipm_handle, 0, current_core ? 0 : 1, 0, 1); +#else + uint32_t dummy_data = 0x55005500; /* Some data must be provided */ + + status = ipm_send(ipm_handle, 0, 0, &dummy_data, sizeof(dummy_data)); +#endif /* #if defined(CONFIG_SOC_MPS2_AN521) */ + +#endif + + 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, + .set_features = virtio_set_features, + .notify = virtio_notify, +}; + +static void ipm_callback_process(struct k_work *work) +{ + virtqueue_notification(vq[VIRTQUEUE_ID]); +} + +static void ipm_callback(const struct device *dev, + void *context, uint32_t id, + volatile void *data) +{ + (void)dev; + + LOG_DBG("Got callback of id %u", id); + /* TODO: Separate workqueue is needed only + * for serialization master (app core) + * + * Use sysworkq to optimize memory footprint + * for serialization slave (net core) + */ + k_work_submit_to_queue(&ipm_work_q, &ipm_work); +} + +int rpmsg_backend_init(struct metal_io_region **io, struct virtio_device *vdev) +{ + int32_t err; + struct metal_init_params metal_params = METAL_INIT_DEFAULTS; + struct metal_device *device; + + /* Start IPM workqueue */ + k_work_q_start(&ipm_work_q, ipm_stack_area, + K_THREAD_STACK_SIZEOF(ipm_stack_area), + IPM_WORK_QUEUE_PRIORITY); + k_thread_name_set(&ipm_work_q.thread, "ipm_work_q"); + + /* 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 err; + } + + /* IPM setup */ +#if defined(CONFIG_RPMSG_SERVICE_DUAL_IPM_SUPPORT) + ipm_tx_handle = device_get_binding(CONFIG_RPMSG_SERVICE_IPM_TX_NAME); + ipm_rx_handle = device_get_binding(CONFIG_RPMSG_SERVICE_IPM_RX_NAME); + + if (!ipm_tx_handle) { + LOG_ERR("Could not get TX IPM device handle"); + return -ENODEV; + } + + if (!ipm_rx_handle) { + LOG_ERR("Could not get RX IPM device handle"); + return -ENODEV; + } + + ipm_register_callback(ipm_rx_handle, ipm_callback, NULL); +#elif defined(CONFIG_RPMSG_SERVICE_SINGLE_IPM_SUPPORT) + ipm_handle = device_get_binding(CONFIG_RPMSG_SERVICE_IPM_NAME); + + if (ipm_handle == NULL) { + LOG_ERR("Could not get IPM device handle"); + return -ENODEV; + } + + ipm_register_callback(ipm_handle, ipm_callback, NULL); + + err = ipm_set_enabled(ipm_handle, 1); + if (err != 0) { + LOG_ERR("Could not enable IPM interrupts and callbacks"); + return err; + } +#endif + + /* Virtqueue setup */ + 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_ROLE; + vdev->vrings_num = VRING_COUNT; + vdev->func = &dispatch; + vdev->vrings_info = &rvrings[0]; + + return 0; +} + +#if MASTER +/* Make sure we clear out the status flag very early (before we bringup the + * secondary core) so the secondary core see's the proper status + */ +int init_status_flag(const struct device *arg) +{ + virtio_set_status(NULL, 0); + + return 0; +} + +SYS_INIT(init_status_flag, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#endif /* MASTER */ diff --git a/subsys/ipc/rpmsg_service/rpmsg_backend.h b/subsys/ipc/rpmsg_service/rpmsg_backend.h new file mode 100644 index 0000000000000..ab5df7c42979c --- /dev/null +++ b/subsys/ipc/rpmsg_service/rpmsg_backend.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SUBSYS_IPC_RPMSG_BACKEND_H +#define ZEPHYR_SUBSYS_IPC_RPMSG_BACKEND_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define VDEV_START_ADDR CONFIG_RPMSG_SERVICE_SHM_BASE_ADDRESS +#define VDEV_SIZE CONFIG_RPMSG_SERVICE_SHM_SIZE + +#define VDEV_STATUS_ADDR VDEV_START_ADDR +#define VDEV_STATUS_SIZE 0x400 + +#define SHM_START_ADDR (VDEV_START_ADDR + VDEV_STATUS_SIZE) +#define SHM_SIZE (VDEV_SIZE - VDEV_STATUS_SIZE) +#define SHM_DEVICE_NAME "sramx.shm" + +/* + * @brief Initialize RPMsg backend + * + * @param io Shared memory IO region. This is an output parameter providing + * a pointer to an actual shared memory IO region structure. + * Caller of this function shall pass an address at which the + * pointer to the shared memory IO region structure is stored. + * @param vdev Pointer to the virtio device initialized by this function. + * + * @retval 0 Initialization successful + * @retval <0 Initialization error reported by OpenAMP + */ +int rpmsg_backend_init(struct metal_io_region **io, struct virtio_device *vdev); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_SUBSYS_IPC_RPMSG_BACKEND_H */ diff --git a/subsys/ipc/rpmsg_service/rpmsg_service.c b/subsys/ipc/rpmsg_service/rpmsg_service.c new file mode 100644 index 0000000000000..44ce0a9b0a5a1 --- /dev/null +++ b/subsys/ipc/rpmsg_service/rpmsg_service.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020-2021, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "rpmsg_backend.h" + +#include +#include +#include + +#include + +#define LOG_LEVEL LOG_LEVEL_INFO +#define LOG_MODULE_NAME rpmsg_service +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#define MASTER IS_ENABLED(CONFIG_RPMSG_SERVICE_MODE_MASTER) + +static struct virtio_device vdev; +static struct rpmsg_virtio_device rvdev; +static struct metal_io_region *io; +static bool ep_crt_started; + +#if MASTER +static struct rpmsg_virtio_shm_pool shpool; +#endif + +static struct { + const char *name; + rpmsg_ept_cb cb; + struct rpmsg_endpoint ep; + volatile bool bound; +} endpoints[CONFIG_RPMSG_SERVICE_NUM_ENDPOINTS]; + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ep) +{ + rpmsg_destroy_ept(ep); +} + +#if MASTER + +static void ns_bind_cb(struct rpmsg_device *rdev, + const char *name, + uint32_t dest) +{ + int err; + + for (int i = 0; i < CONFIG_RPMSG_SERVICE_NUM_ENDPOINTS; ++i) { + if (strcmp(name, endpoints[i].name) == 0) { + err = rpmsg_create_ept(&endpoints[i].ep, + rdev, + name, + RPMSG_ADDR_ANY, + dest, + endpoints[i].cb, + rpmsg_service_unbind); + + if (err != 0) { + LOG_ERR("Creating remote endpoint %s" + " failed wirh error %d", name, err); + } else { + endpoints[i].bound = true; + } + + return; + } + } + + LOG_ERR("Remote endpoint %s not registered locally", name); +} + +#endif + +static int rpmsg_service_init(const struct device *dev) +{ + int32_t err; + + (void)dev; + + LOG_DBG("RPMsg service initialization start"); + + err = rpmsg_backend_init(&io, &vdev); + if (err) { + LOG_ERR("RPMsg backend init failed with error %d", err); + return err; + } + +#if MASTER + rpmsg_virtio_init_shm_pool(&shpool, (void *)SHM_START_ADDR, SHM_SIZE); + err = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool); +#else + err = rpmsg_init_vdev(&rvdev, &vdev, NULL, io, NULL); +#endif + + if (err) { + LOG_ERR("rpmsg_init_vdev failed %d", err); + return err; + } + + ep_crt_started = true; + +#if !MASTER + struct rpmsg_device *rdev; + + rdev = rpmsg_virtio_get_rpmsg_device(&rvdev); + + for (int i = 0; i < CONFIG_RPMSG_SERVICE_NUM_ENDPOINTS; ++i) { + if (endpoints[i].name) { + err = rpmsg_create_ept(&endpoints[i].ep, + rdev, + endpoints[i].name, + RPMSG_ADDR_ANY, + RPMSG_ADDR_ANY, + endpoints[i].cb, + rpmsg_service_unbind); + + if (err) { + LOG_ERR("rpmsg_create_ept failed %d", err); + return err; + } + } + } +#endif + + LOG_DBG("RPMsg service initialized"); + + return 0; +} + +int rpmsg_service_register_endpoint(const char *name, rpmsg_ept_cb cb) +{ + if (ep_crt_started) { + return -EINPROGRESS; + } + + for (int i = 0; i < CONFIG_RPMSG_SERVICE_NUM_ENDPOINTS; ++i) { + if (!endpoints[i].name) { + endpoints[i].name = name; + endpoints[i].cb = cb; + + return i; + } + } + + LOG_ERR("No free slots to register endpoint %s", name); + + return -ENOMEM; +} + +bool rpmsg_service_endpoint_is_bound(int endpoint_id) +{ + return endpoints[endpoint_id].bound; +} + +int rpmsg_service_send(int endpoint_id, const void *data, size_t len) +{ + return rpmsg_send(&endpoints[endpoint_id].ep, data, len); +} + +SYS_INIT(rpmsg_service_init, POST_KERNEL, CONFIG_RPMSG_SERVICE_INIT_PRIORITY); diff --git a/west.yml b/west.yml index ae79cd3bcc958..87993d380709d 100644 --- a/west.yml +++ b/west.yml @@ -57,7 +57,7 @@ manifest: revision: f1fa8241f8786198ba41155413243de36ed878a5 path: modules/hal/infineon - name: hal_nordic - revision: f163c53b88c33070cb28150ba20200e4aa5fdc49 + revision: 74e4ab0185a9a53d03bf382d4bfe5ec81e05b5e4 path: modules/hal/nordic - name: hal_openisa revision: 40d049f69c50b58ea20473bee14cf93f518bf262