diff --git a/CODEOWNERS b/CODEOWNERS index 15918b4cb16..007833e9897 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/include/ipc/rpmsg_service.h b/include/ipc/rpmsg_service.h new file mode 100644 index 00000000000..8a010a1f4c4 --- /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/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index d528abafbfc..80d7a838b35 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 fb64e0add7a..6c5a95d671f 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 00000000000..41bb76fc997 --- /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 00000000000..c2a106d3ebd --- /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 00000000000..357a2bc6c3f --- /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 00000000000..1c053f0bfe5 --- /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 00000000000..bd5e7bf8a22 --- /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 00000000000..ab5df7c4297 --- /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 00000000000..44ce0a9b0a5 --- /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);