ipc: RPMsg service to register multiple endpoints
This patch implements a service that adds multiendpoint capabilities to RPMsg. Multiple endpoints are intended to be used when multiple modules need services from a remote processor. Each module may register one or more RPMsg endpoints. The implementation separates backend from the service, what allows to extend this module to support other topologies like Linux <-> Zephyr. Co-authored-by: Piotr Szkotak <piotr.szkotak@nordicsemi.no> Signed-off-by: Hubert Miś <hubert.mis@nordicsemi.no>
This commit is contained in:
parent
8fef65392d
commit
b0ec7a63ab
11 changed files with 726 additions and 0 deletions
|
@ -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
|
||||
|
|
78
include/ipc/rpmsg_service.h
Normal file
78
include/ipc/rpmsg_service.h
Normal file
|
@ -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 <openamp/open_amp.h>
|
||||
|
||||
#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_ */
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
3
subsys/ipc/CMakeLists.txt
Normal file
3
subsys/ipc/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_RPMSG_SERVICE rpmsg_service)
|
10
subsys/ipc/Kconfig
Normal file
10
subsys/ipc/Kconfig
Normal file
|
@ -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
|
4
subsys/ipc/rpmsg_service/CMakeLists.txt
Normal file
4
subsys/ipc/rpmsg_service/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_sources(rpmsg_backend.c)
|
||||
zephyr_sources(rpmsg_service.c)
|
122
subsys/ipc/rpmsg_service/Kconfig
Normal file
122
subsys/ipc/rpmsg_service/Kconfig
Normal file
|
@ -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
|
296
subsys/ipc/rpmsg_service/rpmsg_backend.c
Normal file
296
subsys/ipc/rpmsg_service/rpmsg_backend.c
Normal file
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "rpmsg_backend.h"
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <drivers/ipm.h>
|
||||
#include <device.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <metal/device.h>
|
||||
|
||||
#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 */
|
44
subsys/ipc/rpmsg_service/rpmsg_backend.h
Normal file
44
subsys/ipc/rpmsg_service/rpmsg_backend.h
Normal file
|
@ -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 <openamp/rpmsg_virtio.h>
|
||||
|
||||
#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 */
|
164
subsys/ipc/rpmsg_service/rpmsg_service.c
Normal file
164
subsys/ipc/rpmsg_service/rpmsg_service.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ipc/rpmsg_service.h>
|
||||
|
||||
#include "rpmsg_backend.h"
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <device.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
|
||||
#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);
|
Loading…
Add table
Add a link
Reference in a new issue