diff --git a/include/ipc/rpmsg_multi_instance.h b/include/ipc/rpmsg_multi_instance.h new file mode 100644 index 00000000000..27982688a39 --- /dev/null +++ b/include/ipc/rpmsg_multi_instance.h @@ -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 +#include +#include +#include + +#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_ */ diff --git a/subsys/ipc/CMakeLists.txt b/subsys/ipc/CMakeLists.txt index 41bb76fc997..baf897bd5f6 100644 --- a/subsys/ipc/CMakeLists.txt +++ b/subsys/ipc/CMakeLists.txt @@ -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) diff --git a/subsys/ipc/Kconfig b/subsys/ipc/Kconfig index c2a106d3ebd..67b393e2298 100644 --- a/subsys/ipc/Kconfig +++ b/subsys/ipc/Kconfig @@ -6,5 +6,6 @@ menu "Inter Processor Communication" source "subsys/ipc/rpmsg_service/Kconfig" +source "subsys/ipc/rpmsg_multi_instance/Kconfig" endmenu diff --git a/subsys/ipc/rpmsg_multi_instance/CMakeLists.txt b/subsys/ipc/rpmsg_multi_instance/CMakeLists.txt new file mode 100644 index 00000000000..7e6afffd7da --- /dev/null +++ b/subsys/ipc/rpmsg_multi_instance/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_sources(rpmsg_multi_instance.c) diff --git a/subsys/ipc/rpmsg_multi_instance/Kconfig b/subsys/ipc/rpmsg_multi_instance/Kconfig new file mode 100644 index 00000000000..4ab81d198bf --- /dev/null +++ b/subsys/ipc/rpmsg_multi_instance/Kconfig @@ -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 diff --git a/subsys/ipc/rpmsg_multi_instance/Kconfig.ipm_name_instance b/subsys/ipc/rpmsg_multi_instance/Kconfig.ipm_name_instance new file mode 100644 index 00000000000..bd80decdc60 --- /dev/null +++ b/subsys/ipc/rpmsg_multi_instance/Kconfig.ipm_name_instance @@ -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. diff --git a/subsys/ipc/rpmsg_multi_instance/rpmsg_multi_instance.c b/subsys/ipc/rpmsg_multi_instance/rpmsg_multi_instance.c new file mode 100644 index 00000000000..331a37255e9 --- /dev/null +++ b/subsys/ipc/rpmsg_multi_instance/rpmsg_multi_instance.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include + +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);