ipc: Add multiple instances RPMsg
This patch implements a service that adds multiple instances capabilities to RPMsg. Each instance is allocated a separate piece of shared memory. Multiple instances provide independent message processing. Each instance has its own work_q. Signed-off-by: Marcin Jeliński <marcin.jelinski@nordicsemi.no>
This commit is contained in:
parent
3771b968f0
commit
54d2eb45a1
7 changed files with 732 additions and 0 deletions
193
include/ipc/rpmsg_multi_instance.h
Normal file
193
include/ipc/rpmsg_multi_instance.h
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_RPMSG_MULTIPLE_INSTANCE_H_
|
||||
#define ZEPHYR_INCLUDE_RPMSG_MULTIPLE_INSTANCE_H_
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief RPMsg multiple instance API
|
||||
* @defgroup rpmsg_multiple_instance_api RPMsg multiple instance APIs
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define VDEV_START_ADDR CONFIG_RPMSG_MULTI_INSTANCE_SHM_BASE_ADDRESS
|
||||
#define VDEV_SIZE CONFIG_RPMSG_MULTI_INSTANCE_SHM_SIZE
|
||||
|
||||
#define SHM_START_ADDR VDEV_START_ADDR
|
||||
#define SHM_SIZE VDEV_SIZE
|
||||
|
||||
#define VRING_ALIGNMENT (4) /**< Alignment of vring buffer. */
|
||||
#define VDEV_STATUS_SIZE (0x4) /**< Size of status region. */
|
||||
|
||||
/** @brief Event callback structure.
|
||||
*
|
||||
* It is registered during endpoint registration.
|
||||
* This structure is packed into the endpoint configuration.
|
||||
*/
|
||||
struct rpmsg_mi_cb {
|
||||
/** @brief Bind was successful.
|
||||
*
|
||||
* @param priv Private user data.
|
||||
*/
|
||||
void (*bound)(void *priv);
|
||||
|
||||
/** @brief New packet arrived.
|
||||
*
|
||||
* @param data Pointer to data buffer.
|
||||
* @param len Length of @a data.
|
||||
* @param priv Private user data.
|
||||
*/
|
||||
void (*received)(const void *data, size_t len, void *priv);
|
||||
};
|
||||
|
||||
/** @brief Endpoint instance. */
|
||||
struct rpmsg_mi_ept {
|
||||
|
||||
/** Name of endpoint. */
|
||||
const char *name;
|
||||
|
||||
/** RPMsg endpoint. */
|
||||
struct rpmsg_endpoint ep;
|
||||
|
||||
/** Event callback structure. */
|
||||
struct rpmsg_mi_cb *cb;
|
||||
|
||||
/** Private user data. */
|
||||
void *priv;
|
||||
|
||||
/** Endpoint was bound. */
|
||||
volatile bool bound;
|
||||
|
||||
/** Linked list node. */
|
||||
sys_snode_t node;
|
||||
};
|
||||
|
||||
/** @brief Endpoint configuration. */
|
||||
struct rpmsg_mi_ept_cfg {
|
||||
|
||||
/** Name of endpoint. */
|
||||
const char *name;
|
||||
|
||||
/** Event callback structure. */
|
||||
struct rpmsg_mi_cb *cb;
|
||||
|
||||
/** Private user data. */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/** @brief Struct describing the context of the RPMsg instanece. */
|
||||
struct rpmsg_mi_ctx {
|
||||
const char *name;
|
||||
struct k_work_q ipm_work_q;
|
||||
struct k_work ipm_work;
|
||||
|
||||
const struct device *ipm_tx_handle;
|
||||
const struct device *ipm_rx_handle;
|
||||
|
||||
uint32_t shm_status_reg_addr;
|
||||
struct metal_io_region *shm_io;
|
||||
struct metal_device shm_device;
|
||||
metal_phys_addr_t shm_physmap[1];
|
||||
|
||||
struct rpmsg_virtio_device rvdev;
|
||||
struct rpmsg_virtio_shm_pool shpool;
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
struct virtqueue *vq[2];
|
||||
struct virtio_vring_info rvrings[2];
|
||||
struct virtio_device vdev;
|
||||
|
||||
uint32_t vring_tx_addr;
|
||||
uint32_t vring_rx_addr;
|
||||
|
||||
sys_slist_t endpoints;
|
||||
};
|
||||
|
||||
/** @brief Configuration of the RPMsg instance. */
|
||||
struct rpsmg_mi_ctx_cfg {
|
||||
|
||||
/** Name of instance. */
|
||||
const char *name;
|
||||
|
||||
/** Stack area for k_work_q. */
|
||||
k_thread_stack_t *ipm_stack_area;
|
||||
|
||||
/** Size of stack area. */
|
||||
size_t ipm_stack_size;
|
||||
|
||||
/** Priority of work_q. */
|
||||
int ipm_work_q_prio;
|
||||
|
||||
/** Name of work_q thread. */
|
||||
const char *ipm_thread_name;
|
||||
|
||||
/** Name of the TX IPM channel. */
|
||||
const char *ipm_tx_name;
|
||||
|
||||
/** Name of the RX IPM channel. */
|
||||
const char *ipm_rx_name;
|
||||
};
|
||||
|
||||
/** @brief Initialization of RPMsg instance.
|
||||
*
|
||||
* Each instance has an automatically allocated area of shared memory.
|
||||
*
|
||||
* @param ctx Pointer to the RPMsg instance.
|
||||
* @param cfg Pointer to the configuration structure.
|
||||
* @retval 0 if the operation was successful.
|
||||
* -EINVAL when the incorrect parameters have been passed.
|
||||
* -EIO when the configuration is not correct.
|
||||
* -ENODEV failed to get TX or RX IPM handle.
|
||||
* -ENOMEM when there is not enough memory to register virqueue.
|
||||
* < 0 on other negative errno code, reported by rpmsg.
|
||||
*/
|
||||
int rpmsg_mi_ctx_init(struct rpmsg_mi_ctx *ctx, const struct rpsmg_mi_ctx_cfg *cfg);
|
||||
|
||||
/** @brief Register IPC endpoint.
|
||||
*
|
||||
* Registers IPC endpoint to enable communication with a remote device.
|
||||
*
|
||||
* @param ctx Pointer to the RPMsg instance.
|
||||
* @param ept Pointer to endpoint object.
|
||||
* @param cfg Pointer to the endpoint configuration.
|
||||
*
|
||||
* @retval -EINVAL One of the parameters is incorrect.
|
||||
* @retval other errno code reported by rpmsg.
|
||||
*/
|
||||
int rpmsg_mi_ept_register(struct rpmsg_mi_ctx *ctx,
|
||||
struct rpmsg_mi_ept *ept,
|
||||
struct rpmsg_mi_ept_cfg *cfg);
|
||||
|
||||
/** @brief Send data using given IPC endpoint.
|
||||
*
|
||||
* Note: It is not possible to send a message of zero length.
|
||||
*
|
||||
* @param ept Endpoint object.
|
||||
* @param data Pointer to the buffer to send through RPMsg.
|
||||
* @param len Number of bytes to send.
|
||||
*
|
||||
* @retval Number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
int rpmsg_mi_send(struct rpmsg_mi_ept *ept, const void *data, size_t len);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_RPMSG_MULTIPLE_INNSTANCE_H_ */
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_RPMSG_SERVICE rpmsg_service)
|
||||
add_subdirectory_ifdef(CONFIG_RPMSG_MULTI_INSTANCE rpmsg_multi_instance)
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
menu "Inter Processor Communication"
|
||||
|
||||
source "subsys/ipc/rpmsg_service/Kconfig"
|
||||
source "subsys/ipc/rpmsg_multi_instance/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
3
subsys/ipc/rpmsg_multi_instance/CMakeLists.txt
Normal file
3
subsys/ipc/rpmsg_multi_instance/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_sources(rpmsg_multi_instance.c)
|
82
subsys/ipc/rpmsg_multi_instance/Kconfig
Normal file
82
subsys/ipc/rpmsg_multi_instance/Kconfig
Normal file
|
@ -0,0 +1,82 @@
|
|||
# Copyright (c) 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
|
||||
|
||||
menuconfig RPMSG_MULTI_INSTANCE
|
||||
bool "RPMsg multiple instance"
|
||||
select IPM
|
||||
select OPENAMP
|
||||
help
|
||||
Enables support for RPMsg multiple instance.
|
||||
|
||||
if RPMSG_MULTI_INSTANCE
|
||||
|
||||
choice RPMSG_ROLE
|
||||
prompt "RPMSG device role"
|
||||
default RPMSG_MULTI_INSTANCE_REMOTE
|
||||
|
||||
config RPMSG_MULTI_INSTANCE_REMOTE
|
||||
bool "Remote"
|
||||
|
||||
config RPMSG_MULTI_INSTANCE_MASTER
|
||||
bool "Master"
|
||||
endchoice
|
||||
|
||||
config RPMSG_MULTI_INSTANCES_NO
|
||||
int "Number of RPMSG instances."
|
||||
default 2
|
||||
range 1 8
|
||||
help
|
||||
How many instances are to be used.
|
||||
|
||||
ipm_name_instance_num = 1
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 2
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 3
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 4
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 5
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 6
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 7
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
ipm_name_instance_num = 8
|
||||
rsource "Kconfig.ipm_name_instance"
|
||||
|
||||
config RPMSG_MULTI_INSTANCE_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_MULTI_INSTANCE_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.
|
||||
|
||||
config RPMSG_MULTI_INSTANCE_INIT_PRIORITY
|
||||
int "Initialization priority of RPMsg muliple instances"
|
||||
default 46
|
||||
help
|
||||
If in doubt, do not modify this value.
|
||||
|
||||
config IPM_MSG_ID
|
||||
int "IPM message identifier."
|
||||
default 0
|
||||
help
|
||||
Values are constrained by ipm_max_data_size_get since many boards
|
||||
only allow for a subset of bits in a 32-bit register to store the ID.
|
||||
|
||||
module = RPMSG_MULTI_INSTANCE
|
||||
module-str = RPMsg multi instance
|
||||
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif # RPMSG_MULTI_INSTANCE
|
14
subsys/ipc/rpmsg_multi_instance/Kconfig.ipm_name_instance
Normal file
14
subsys/ipc/rpmsg_multi_instance/Kconfig.ipm_name_instance
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2021 Nordic Semiconductor (ASA)
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config RPMSG_MULTI_INSTANCE_$(ipm_name_instance_num)_IPM_TX_NAME
|
||||
string "TX IPM channel name for instance $(ipm_name_instance_num)"
|
||||
help
|
||||
This option specifies the IPM device name to be used for
|
||||
TX communication.
|
||||
|
||||
config RPMSG_MULTI_INSTANCE_$(ipm_name_instance_num)_IPM_RX_NAME
|
||||
string "RX IPM channel name for instance $(ipm_name_instance_num)"
|
||||
help
|
||||
This option specifies the IPM device name to be used for
|
||||
RX communication.
|
438
subsys/ipc/rpmsg_multi_instance/rpmsg_multi_instance.c
Normal file
438
subsys/ipc/rpmsg_multi_instance/rpmsg_multi_instance.c
Normal file
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <ipc/rpmsg_multi_instance.h>
|
||||
#include <zephyr.h>
|
||||
#include <device.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <drivers/ipm.h>
|
||||
#include <openamp/open_amp.h>
|
||||
#include <cache.h>
|
||||
|
||||
LOG_MODULE_REGISTER(rpmsg_multi_instance, CONFIG_RPMSG_MULTI_INSTANCE_LOG_LEVEL);
|
||||
|
||||
#define SHM_DEVICE_NAME "sram0.shm"
|
||||
#define IPM_MSG_ID CONFIG_IPM_MSG_ID
|
||||
|
||||
#define RPMSG_VQ_0 (0) /* TX virtqueue queue index */
|
||||
#define RPMSG_VQ_1 (1) /* RX virtqueue queue index */
|
||||
#define VRING_COUNT (2) /* Number of used vring buffers. */
|
||||
#define IPC_INSTANCE_COUNT (CONFIG_RPMSG_MULTI_INSTANCES_NO) /* Total number of IPC instances.*/
|
||||
|
||||
/* Private macros. */
|
||||
#define VRING_DESC_SIZEOF(num) ((num) * (sizeof(struct vring_desc)))
|
||||
#define VRING_AVAIL_SIZEOF(num) (sizeof(struct vring_avail) + \
|
||||
((num) * sizeof(uint16_t)) + sizeof(uint16_t))
|
||||
#define VRING_USED_SIZEOF(num) (sizeof(struct vring_used) + \
|
||||
((num) * sizeof(struct vring_used_elem)) + \
|
||||
sizeof(uint16_t))
|
||||
|
||||
#define VRING_FIRST_SUM(num) (VRING_DESC_SIZEOF(num) + VRING_AVAIL_SIZEOF(num))
|
||||
|
||||
|
||||
/* Compute size of vring buffer based on its size and alignment. */
|
||||
#define VRING_SIZE_COMPUTE(vring_size, align) (ROUND_UP(VRING_FIRST_SUM((vring_size)), \
|
||||
(align)) + VRING_USED_SIZEOF((vring_size)))
|
||||
/* Macro for calculating used memory by virtqueue buffers for remote device. */
|
||||
#define VIRTQUEUE_SIZE_GET(vring_size) (RPMSG_BUFFER_SIZE * (vring_size))
|
||||
|
||||
/* Macro for getting the size of shared memory occupied by single IPC instance. */
|
||||
#define SHMEM_INST_SIZE_GET(vring_size) (VDEV_STATUS_SIZE + \
|
||||
(VRING_COUNT * VIRTQUEUE_SIZE_GET((vring_size))) + \
|
||||
(VRING_COUNT * VRING_SIZE_COMPUTE((vring_size), (VRING_ALIGNMENT))))
|
||||
|
||||
/* Returns size of used shared memory consumed by all IPC instances*/
|
||||
#define SHMEM_CONSUMED_SIZE_GET(vring_size) (IPC_INSTANCE_COUNT * SHMEM_INST_SIZE_GET((vring_size)))
|
||||
|
||||
/* Returns maximum allowable size of vring buffers to fit memory requirements. */
|
||||
#define VRING_SIZE_GET(shmem_size) \
|
||||
((SHMEM_CONSUMED_SIZE_GET(32)) < (shmem_size) ? 32 : \
|
||||
(SHMEM_CONSUMED_SIZE_GET(16)) < (shmem_size) ? 16 : \
|
||||
(SHMEM_CONSUMED_SIZE_GET(8)) < (shmem_size) ? 8 : \
|
||||
(SHMEM_CONSUMED_SIZE_GET(4)) < (shmem_size) ? 4 : \
|
||||
(SHMEM_CONSUMED_SIZE_GET(2)) < (shmem_size) ? 2 : 1)
|
||||
|
||||
/* Returns size of used shared memory of single instance in case of using
|
||||
* maximum allowable vring buffer size.
|
||||
*/
|
||||
#define SHMEM_INST_SIZE_AUTOALLOC_GET(shmem_size) \
|
||||
(SHMEM_INST_SIZE_GET(VRING_SIZE_GET((shmem_size))))
|
||||
|
||||
/* Returns start address of ipc instance in shared memory. It assumes that
|
||||
* maximum allowable vring buffer size is used.
|
||||
*/
|
||||
#define SHMEM_INST_ADDR_AUTOALLOC_GET(shmem_addr, shmem_size, id) ((shmem_addr) + \
|
||||
((id) * (SHMEM_INST_SIZE_AUTOALLOC_GET(shmem_size))))
|
||||
|
||||
#if IS_ENABLED(CONFIG_RPMSG_MULTI_INSTANCE_MASTER)
|
||||
#define VIRTQUEUE_ID 0
|
||||
#else
|
||||
#define VIRTQUEUE_ID 1
|
||||
#endif
|
||||
|
||||
static bool config_correct;
|
||||
static int instance;
|
||||
|
||||
static void rpmsg_service_unbind(struct rpmsg_endpoint *p_ep)
|
||||
{
|
||||
rpmsg_destroy_ept(p_ep);
|
||||
}
|
||||
|
||||
static unsigned char virtio_get_status(struct virtio_device *p_vdev)
|
||||
{
|
||||
struct rpmsg_mi_ctx *ctx = metal_container_of(p_vdev, struct rpmsg_mi_ctx, vdev);
|
||||
unsigned char ret = VIRTIO_CONFIG_STATUS_DRIVER_OK;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_RPMSG_MULTI_INSTANCE_MASTER)) {
|
||||
sys_cache_data_range(&ctx->shm_status_reg_addr,
|
||||
sizeof(ctx->shm_status_reg_addr), K_CACHE_INVD);
|
||||
ret = sys_read8(ctx->shm_status_reg_addr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t virtio_get_features(struct virtio_device *vdev)
|
||||
{
|
||||
return BIT(VIRTIO_RPMSG_F_NS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RPMSG_MULTI_INSTANCE_MASTER
|
||||
|
||||
static void virtio_set_status(struct virtio_device *p_vdev, unsigned char status)
|
||||
{
|
||||
struct rpmsg_mi_ctx *ctx = metal_container_of(p_vdev, struct rpmsg_mi_ctx, vdev);
|
||||
|
||||
sys_write8(status, ctx->shm_status_reg_addr);
|
||||
sys_cache_data_range(&ctx->shm_status_reg_addr,
|
||||
sizeof(ctx->shm_status_reg_addr), K_CACHE_WB);
|
||||
}
|
||||
|
||||
static void virtio_set_features(struct virtio_device *vdev, uint32_t features)
|
||||
{
|
||||
/* No need for implementation */
|
||||
}
|
||||
#endif
|
||||
|
||||
static void virtio_notify(struct virtqueue *vq)
|
||||
{
|
||||
int status;
|
||||
|
||||
struct rpmsg_mi_ctx *ctx = metal_container_of(vq->vq_dev, struct rpmsg_mi_ctx, vdev);
|
||||
|
||||
if (ctx) {
|
||||
status = ipm_send(ctx->ipm_tx_handle, 0, IPM_MSG_ID, NULL, 0);
|
||||
if (status != 0) {
|
||||
LOG_WRN("Failed to notify: %d", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const static struct virtio_dispatch dispatch = {
|
||||
.get_status = virtio_get_status,
|
||||
.get_features = virtio_get_features,
|
||||
#ifdef CONFIG_RPMSG_MULTI_INSTANCE_MASTER
|
||||
.set_status = virtio_set_status,
|
||||
.set_features = virtio_set_features,
|
||||
#endif
|
||||
.notify = virtio_notify,
|
||||
};
|
||||
|
||||
static void ipm_callback_process(struct k_work *item)
|
||||
{
|
||||
struct rpmsg_mi_ctx *ctx = CONTAINER_OF(item, struct rpmsg_mi_ctx, ipm_work);
|
||||
|
||||
LOG_DBG("Process callback. Instance name: %s", ctx->name);
|
||||
virtqueue_notification(ctx->vq[VIRTQUEUE_ID]);
|
||||
}
|
||||
|
||||
static void ipm_callback(const struct device *dev, void *context, uint32_t id, volatile void *data)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
struct rpmsg_mi_ctx *ctx = (struct rpmsg_mi_ctx *)context;
|
||||
|
||||
k_work_submit_to_queue(&ctx->ipm_work_q, &ctx->ipm_work);
|
||||
}
|
||||
|
||||
int rpmsg_mi_configure_shm(struct rpmsg_mi_ctx *ctx, const struct rpsmg_mi_ctx_cfg *cfg)
|
||||
{
|
||||
uint8_t vring_size = VRING_SIZE_GET(SHM_SIZE);
|
||||
uint32_t shm_addr = SHMEM_INST_ADDR_AUTOALLOC_GET(SHM_START_ADDR, SHM_SIZE, instance);
|
||||
uint32_t shm_size = SHMEM_INST_SIZE_AUTOALLOC_GET(SHM_SIZE);
|
||||
|
||||
uint32_t shm_local_start_addr = shm_addr + VDEV_STATUS_SIZE;
|
||||
uint32_t shm_local_size = shm_size - VDEV_STATUS_SIZE;
|
||||
|
||||
uint32_t rpmsg_reg_size = 2 * VIRTQUEUE_SIZE_GET(vring_size);
|
||||
uint32_t vring_region_size = VRING_SIZE_COMPUTE(vring_size, VRING_ALIGNMENT);
|
||||
|
||||
ctx->shm_status_reg_addr = shm_addr;
|
||||
ctx->shm_physmap[0] = shm_local_start_addr;
|
||||
|
||||
ctx->shm_device.name = SHM_DEVICE_NAME;
|
||||
ctx->shm_device.bus = NULL;
|
||||
ctx->shm_device.num_regions = 1;
|
||||
|
||||
ctx->shm_device.regions->virt = (void *)shm_local_start_addr;
|
||||
ctx->shm_device.regions->physmap = ctx->shm_physmap;
|
||||
|
||||
ctx->shm_device.regions->size = shm_local_size;
|
||||
ctx->shm_device.regions->page_shift = 0xffffffff;
|
||||
ctx->shm_device.regions->page_mask = 0xffffffff;
|
||||
ctx->shm_device.regions->mem_flags = 0;
|
||||
|
||||
ctx->shm_device.irq_num = 0;
|
||||
ctx->shm_device.irq_info = NULL;
|
||||
|
||||
ctx->vring_rx_addr = shm_local_start_addr + rpmsg_reg_size;
|
||||
ctx->vring_tx_addr = ctx->vring_rx_addr + vring_region_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ept_cb(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv)
|
||||
{
|
||||
struct rpmsg_mi_ept *mi_ep = (struct rpmsg_mi_ept *)priv;
|
||||
|
||||
if (len == 0) {
|
||||
if (!mi_ep->bound) {
|
||||
LOG_DBG("Handshake done");
|
||||
rpmsg_send(ept, (uint8_t *) "", 0);
|
||||
mi_ep->bound = true;
|
||||
if (mi_ep->cb->bound) {
|
||||
mi_ep->cb->bound(mi_ep->priv);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (mi_ep->cb->received) {
|
||||
mi_ep->cb->received(data, len, mi_ep->priv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest)
|
||||
{
|
||||
struct rpmsg_virtio_device *p_rvdev = metal_container_of(rdev,
|
||||
struct rpmsg_virtio_device, rdev);
|
||||
|
||||
struct rpmsg_mi_ctx *ctx = metal_container_of(p_rvdev, struct rpmsg_mi_ctx, rvdev);
|
||||
|
||||
LOG_DBG("bind_cb endpoint: %s, for instance: %s", name ? log_strdup(name) : "", ctx->name);
|
||||
|
||||
sys_snode_t *node;
|
||||
|
||||
SYS_SLIST_FOR_EACH_NODE(&ctx->endpoints, node) {
|
||||
struct rpmsg_mi_ept *ept = CONTAINER_OF(node, struct rpmsg_mi_ept, node);
|
||||
|
||||
if (strcmp(name, ept->name) == 0) {
|
||||
LOG_DBG("Master - Create endpoint: %s", ept->name);
|
||||
|
||||
int err = rpmsg_create_ept(&ept->ep, rdev, name, RPMSG_ADDR_ANY,
|
||||
dest, ept_cb, rpmsg_service_unbind);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Creating remote endpoint %s"
|
||||
" failed wirh error %d", name, err);
|
||||
} else {
|
||||
/* Notify the remote site that binding has occurred */
|
||||
rpmsg_send(&ept->ep, (uint8_t *)"", 0);
|
||||
|
||||
ept->bound = true;
|
||||
ept->cb->bound(ept->priv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rpmsg_mi_ctx_init(struct rpmsg_mi_ctx *ctx, const struct rpsmg_mi_ctx_cfg *cfg)
|
||||
{
|
||||
if (!ctx || !cfg) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!config_correct) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_DBG("RPMsg multiple instance initialization");
|
||||
|
||||
int err;
|
||||
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
|
||||
struct metal_device *device;
|
||||
|
||||
/* Start IPM workqueue */
|
||||
k_work_queue_start(&ctx->ipm_work_q, cfg->ipm_stack_area,
|
||||
cfg->ipm_stack_size, cfg->ipm_work_q_prio, NULL);
|
||||
k_thread_name_set(&ctx->ipm_work_q.thread, cfg->ipm_thread_name);
|
||||
k_work_init(&ctx->ipm_work, ipm_callback_process);
|
||||
|
||||
ctx->name = cfg->name;
|
||||
sys_slist_init(&ctx->endpoints);
|
||||
|
||||
/* Configure share memory */
|
||||
err = rpmsg_mi_configure_shm(ctx, cfg);
|
||||
if (err) {
|
||||
LOG_ERR("shmem configuration: failed - error code %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 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(&ctx->shm_device);
|
||||
if (err) {
|
||||
LOG_ERR("Could not 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;
|
||||
}
|
||||
|
||||
ctx->shm_io = metal_device_io_region(device, 0);
|
||||
if (!ctx->shm_io) {
|
||||
LOG_ERR("metal_device_io_region failed to get region");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* IPM setup. */
|
||||
ctx->ipm_tx_handle = device_get_binding(cfg->ipm_tx_name);
|
||||
if (!ctx->ipm_tx_handle) {
|
||||
LOG_ERR("Could not get TX IPM device handle");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctx->ipm_rx_handle = device_get_binding(cfg->ipm_rx_name);
|
||||
if (!ctx->ipm_rx_handle) {
|
||||
LOG_ERR("Could not get RX IPM device handle");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Register IPM callback. This cb executes when msg has come. */
|
||||
ipm_register_callback(ctx->ipm_rx_handle, ipm_callback, ctx);
|
||||
|
||||
/* Virtquue setup */
|
||||
uint8_t vring_size = VRING_SIZE_GET(SHM_SIZE);
|
||||
|
||||
ctx->vq[RPMSG_VQ_0] = virtqueue_allocate(vring_size);
|
||||
if (!ctx->vq[RPMSG_VQ_0]) {
|
||||
LOG_ERR("virtqueue_allocate failed to alloc vq[RPMSG_VQ_0]");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ctx->vq[RPMSG_VQ_1] = virtqueue_allocate(vring_size);
|
||||
if (!ctx->vq[RPMSG_VQ_1]) {
|
||||
LOG_ERR("virtqueue_allocate failed to alloc vq[RPMSG_VQ_1]");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ctx->rvrings[RPMSG_VQ_0].io = ctx->shm_io;
|
||||
ctx->rvrings[RPMSG_VQ_0].info.vaddr = (void *)ctx->vring_tx_addr;
|
||||
ctx->rvrings[RPMSG_VQ_0].info.num_descs = vring_size;
|
||||
ctx->rvrings[RPMSG_VQ_0].info.align = VRING_ALIGNMENT;
|
||||
ctx->rvrings[RPMSG_VQ_0].vq = ctx->vq[RPMSG_VQ_0];
|
||||
|
||||
ctx->rvrings[RPMSG_VQ_1].io = ctx->shm_io;
|
||||
ctx->rvrings[RPMSG_VQ_1].info.vaddr = (void *)ctx->vring_rx_addr;
|
||||
ctx->rvrings[RPMSG_VQ_1].info.num_descs = vring_size;
|
||||
ctx->rvrings[RPMSG_VQ_1].info.align = VRING_ALIGNMENT;
|
||||
ctx->rvrings[RPMSG_VQ_1].vq = ctx->vq[RPMSG_VQ_1];
|
||||
|
||||
ctx->vdev.role = IS_ENABLED(CONFIG_RPMSG_MULTI_INSTANCE_MASTER) ?
|
||||
RPMSG_MASTER : RPMSG_REMOTE;
|
||||
|
||||
ctx->vdev.vrings_num = VRING_COUNT;
|
||||
ctx->vdev.func = &dispatch;
|
||||
ctx->vdev.vrings_info = &ctx->rvrings[0];
|
||||
|
||||
if (IS_ENABLED(CONFIG_RPMSG_MULTI_INSTANCE_MASTER)) {
|
||||
/* This step is only required if you are VirtIO device master.
|
||||
* Initialize the shared buffers pool.
|
||||
*/
|
||||
rpmsg_virtio_init_shm_pool(&ctx->shpool, (void *) ctx->shm_device.regions->virt,
|
||||
ctx->shm_device.regions->size);
|
||||
|
||||
err = rpmsg_init_vdev(&ctx->rvdev, &ctx->vdev, ns_bind_cb,
|
||||
ctx->shm_io, &ctx->shpool);
|
||||
} else {
|
||||
err = rpmsg_init_vdev(&ctx->rvdev, &ctx->vdev, NULL, ctx->shm_io, NULL);
|
||||
}
|
||||
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("RPMSG vdev initialization failed %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get RPMsg device from RPMsg VirtIO device. */
|
||||
ctx->rdev = rpmsg_virtio_get_rpmsg_device(&ctx->rvdev);
|
||||
|
||||
instance++;
|
||||
LOG_DBG("RPMsg multiple instance initialization done");
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int rpmsg_mi_ept_register(struct rpmsg_mi_ctx *ctx, struct rpmsg_mi_ept *ept,
|
||||
struct rpmsg_mi_ept_cfg *cfg)
|
||||
{
|
||||
if (!ctx || !ept || !cfg) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ept->cb = cfg->cb;
|
||||
ept->priv = cfg->priv;
|
||||
ept->ep.priv = ept;
|
||||
ept->bound = false;
|
||||
ept->name = cfg->name;
|
||||
|
||||
sys_slist_append(&ctx->endpoints, &ept->node);
|
||||
if (!IS_ENABLED(CONFIG_RPMSG_MULTI_INSTANCE_MASTER)) {
|
||||
LOG_DBG("Remote - Create endpoint: %s", ept->name);
|
||||
|
||||
int err = rpmsg_create_ept(&ept->ep, ctx->rdev, ept->name, RPMSG_ADDR_ANY,
|
||||
RPMSG_ADDR_ANY, ept_cb, rpmsg_service_unbind);
|
||||
|
||||
if (err != 0) {
|
||||
LOG_ERR("RPMSG endpoint create failed %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rpmsg_mi_send(struct rpmsg_mi_ept *ept, const void *data, size_t len)
|
||||
{
|
||||
return rpmsg_send(&ept->ep, data, len);
|
||||
}
|
||||
|
||||
static bool rpmsg_mi_config_verify(void)
|
||||
{
|
||||
if (SHMEM_INST_SIZE_AUTOALLOC_GET(SHM_SIZE) * IPC_INSTANCE_COUNT > SHM_SIZE) {
|
||||
LOG_ERR("Not enough memory");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int rpmsg_mi_init(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
LOG_DBG("Initialization of RPMsg multiple instance");
|
||||
config_correct = rpmsg_mi_config_verify();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(rpmsg_mi_init, POST_KERNEL, CONFIG_RPMSG_MULTI_INSTANCE_INIT_PRIORITY);
|
Loading…
Add table
Add a link
Reference in a new issue