ipc: ipc_service: Rework multi-instance backend.

Only one single IPC service backend is currently present: multi_instance
backend. This backend is heavily relying on the RPMsg multi_instance
code to instanciate and manage instances and endpoints. Samples exist
for both in the samples/subsys/ipc/ directory.

With this patch we are "unpacking" the RPMsg multi_service code to make
it more modular and reusable by different backends.

In particular we are re-organizing the code into two helper libraries:
an RPMsg library and a VRING / virtqueues static allocation library. At
the same time we rewrite the multi_instance backend to make fully use of
those new libraries and remove the old multi_instance sample.

Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
Carlo Caione 2021-09-06 17:26:02 +02:00 committed by Christopher Friedt
parent 6a3593f4af
commit 6c00e980b2
36 changed files with 938 additions and 1726 deletions

128
include/ipc/ipc_rpmsg.h Normal file
View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_IPC_SERVICE_IPC_RPMSG_H_
#define ZEPHYR_INCLUDE_IPC_SERVICE_IPC_RPMSG_H_
#include <ipc/ipc_service.h>
#include <openamp/open_amp.h>
#include <metal/device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IPC service RPMsg API
* @defgroup ipc_service_rpmsg_api IPC service RPMsg API
* @{
*/
/** Number of endpoints. */
#define NUM_ENDPOINTS CONFIG_IPC_SERVICE_NUM_ENDPOINTS_PER_INSTANCE
/**
* @typedef rpmsg_ept_bound_cb
* @brief Define the bound callback.
*
* This callback is defined at instance level and it is called when an endpoint
* of the instance is bound.
*
* @param ept Endpoint of the instance just bound.
*/
typedef void (*rpmsg_ept_bound_cb)(struct ipc_ept *ept);
/** @brief Endpoint structure.
*
* Used to define an endpoint to be encapsulated in an RPMsg instance.
*/
struct ipc_ept {
/** RPMsg endpoint. */
struct rpmsg_endpoint ep;
/** Name of the endpoint. */
const char *name;
/** Bound flag. */
volatile bool bound;
/** Callbacks. */
const struct ipc_service_cb *cb;
/** Private data to be passed to the endpoint callbacks. */
void *priv;
};
/** @brief RPMsg instance structure.
*
* Struct representation of an RPMsg instance.
*/
struct ipc_rpmsg_instance {
/** Endpoints in the instance. */
struct ipc_ept endpoint[NUM_ENDPOINTS];
/** RPMsg virtIO device. */
struct rpmsg_virtio_device rvdev;
/** SHM pool. */
struct rpmsg_virtio_shm_pool shm_pool;
/** EPT (instance) bound callback. */
rpmsg_ept_bound_cb bound_cb;
/** EPT (instance) callback. */
rpmsg_ept_cb cb;
};
/** @brief Init an RPMsg instance.
*
* Init an RPMsg instance.
*
* @param instance Pointer to the RPMsg instance struct.
* @param role Master / Remote role.
* @param shm_io SHM IO region pointer.
* @param vdev VirtIO device pointer.
* @param shb Shared memory region pointer.
* @param size Size of the shared memory region.
* @param ns_bind_cb callback handler for name service announcement without
* local endpoints waiting to bind. If NULL the
* implementation falls back to the internal implementation.
*
* @retval -EINVAL When some parameter is missing.
* @retval 0 If successful.
* @retval Other errno codes depending on the OpenAMP implementation.
*/
int ipc_rpmsg_init(struct ipc_rpmsg_instance *instance,
unsigned int role,
struct metal_io_region *shm_io,
struct virtio_device *vdev,
void *shb, size_t size,
rpmsg_ns_bind_cb ns_bind_cb);
/** @brief Register an endpoint.
*
* Register an endpoint to a provided RPMsg instance.
*
* @param instance Pointer to the RPMsg instance struct.
* @param role Master / Remote role.
* @param ept Endpoint to register.
*
* @retval -EINVAL When some parameter is missing.
* @retval 0 If successful.
* @retval Other errno codes depending on the OpenAMP implementation.
*/
int ipc_rpmsg_register_ept(struct ipc_rpmsg_instance *instance, unsigned int role,
struct ipc_ept *ept);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_IPC_SERVICE_IPC_RPMSG_H_ */

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_IPC_SERVICE_IPC_STATIC_VRINGS_H_
#define ZEPHYR_INCLUDE_IPC_SERVICE_IPC_STATIC_VRINGS_H_
#include <ipc/ipc_service.h>
#include <openamp/open_amp.h>
#include <metal/device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IPC service static VRINGs API
* @defgroup ipc_service_static_vrings_api IPC service static VRINGs API
* @{
*/
/** Number of used VRING buffers. */
#define VRING_COUNT (2)
/**
* @typedef ipc_notify_cb
* @brief Define the notify callback.
*
* This callback is defined at instance level and it is called on virtqueue notify.
*
* @param vq Virtqueue.
* @param priv Priv data.
*/
typedef void (*ipc_notify_cb)(struct virtqueue *vq, void *priv);
/** @brief Static VRINGs structure.
*
* Struct used to represent and carry information about static allocation of VRINGs.
*/
struct ipc_static_vrings {
/** virtIO device. */
struct virtio_device vdev;
/** SHM physmap. */
metal_phys_addr_t shm_physmap[1];
/** SHM device. */
struct metal_device shm_device;
/** SHM and addresses. */
uintptr_t status_reg_addr;
/** TX VRING address. */
uintptr_t tx_addr;
/** RX VRING address. */
uintptr_t rx_addr;
/** VRING size. */
size_t vring_size;
/** Shared memory region address. */
uintptr_t shm_addr;
/** Share memory region size. */
size_t shm_size;
/** SHM IO region. */
struct metal_io_region *shm_io;
/** VRINGs */
struct virtio_vring_info rvrings[VRING_COUNT];
/** Virtqueues. */
struct virtqueue *vq[VRING_COUNT];
/** Private data to be passed to the notify callback. */
void *priv;
/** Notify callback. */
ipc_notify_cb notify_cb;
};
/** @brief Init the static VRINGs.
*
* Init VRINGs and Virtqueues of an OpenAMP / RPMsg instance.
*
* @param vr Pointer to the VRINGs instance struct.
* @param role Master / Remote role.
*
* @retval -EINVAL When some parameter is missing.
* @retval -ENOMEM When memory is not enough for VQs allocation.
* @retval 0 If successful.
* @retval Other errno codes depending on the OpenAMP implementation.
*/
int ipc_static_vrings_init(struct ipc_static_vrings *vr, unsigned int role);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_IPC_SERVICE_IPC_STATIC_VRINGS_H_ */

View file

@ -1,212 +0,0 @@
/*
* 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 instance. */
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;
unsigned int ipm_tx_id;
uintptr_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;
uintptr_t vring_tx_addr;
uintptr_t vring_rx_addr;
sys_slist_t endpoints;
};
struct rpmsg_mi_ctx_shm_cfg {
/** Physical address shared memory region. */
uintptr_t addr;
/** Size shared memory region. */
size_t size;
/** Internal counter. */
unsigned int instance;
};
/** @brief Configuration of the RPMsg instance. */
struct rpmsg_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;
/** IPM message identifier. */
unsigned int ipm_tx_id;
/** SHM struct. */
struct rpmsg_mi_ctx_shm_cfg *shm;
};
/** @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 rpmsg_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_ */

View file

@ -18,7 +18,7 @@ CONFIG_IPM_MSG_CH_2_RX=y
CONFIG_IPM_MSG_CH_1_TX=y
CONFIG_IPM_MSG_CH_3_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_3"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_2"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_TX_NAME="IPM_1"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_RX_NAME="IPM_0"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_TX_NAME="IPM_3"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_RX_NAME="IPM_2"

View file

@ -18,7 +18,7 @@ CONFIG_IPM_MSG_CH_2_RX=y
CONFIG_IPM_MSG_CH_1_TX=y
CONFIG_IPM_MSG_CH_3_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_3"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_2"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_TX_NAME="IPM_1"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_RX_NAME="IPM_0"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_TX_NAME="IPM_3"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_RX_NAME="IPM_2"

View file

@ -6,14 +6,14 @@ CONFIG_HEAP_MEM_POOL_SIZE=4096
# Configuration IPC Service
CONFIG_IPC_SERVICE=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MULTI_INSTANCE=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_MASTER=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI=y
# Configuration backend for IPC Service
CONFIG_OPENAMP=y
CONFIG_OPENAMP_SLAVE=n
CONFIG_RPMSG_MULTI_INSTANCE_MASTER=y
CONFIG_RPMSG_MULTI_INSTANCES_NO=2
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_NUM_INSTANCES=2
CONFIG_LOG=y
CONFIG_IPC_SERVICE_LOG_LEVEL_INF=y

View file

@ -16,7 +16,7 @@ CONFIG_IPM_MSG_CH_3_RX=y
CONFIG_IPM_MSG_CH_0_TX=y
CONFIG_IPM_MSG_CH_2_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_2"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_3"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_TX_NAME="IPM_0"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_RX_NAME="IPM_1"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_TX_NAME="IPM_2"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_RX_NAME="IPM_3"

View file

@ -16,7 +16,7 @@ CONFIG_IPM_MSG_CH_3_RX=y
CONFIG_IPM_MSG_CH_0_TX=y
CONFIG_IPM_MSG_CH_2_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_2"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_3"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_TX_NAME="IPM_0"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_0_IPM_RX_NAME="IPM_1"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_TX_NAME="IPM_2"
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_1_IPM_RX_NAME="IPM_3"

View file

@ -6,14 +6,14 @@ CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
# Configuration IPC Service
CONFIG_IPC_SERVICE=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MULTI_INSTANCE=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_REMOTE=y
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI=y
# Configuration backend for IPC Service
CONFIG_OPENAMP=y
CONFIG_OPENAMP_MASTER=n
CONFIG_RPMSG_MULTI_INSTANCE_REMOTE=y
CONFIG_RPMSG_MULTI_INSTANCES_NO=2
CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_NUM_INSTANCES=2
CONFIG_LOG=y
CONFIG_IPC_SERVICE_LOG_LEVEL_INF=y

View file

@ -1,38 +0,0 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.20.0)
set(REMOTE_ZEPHYR_DIR ${CMAKE_CURRENT_BINARY_DIR}/rpmsg_multi_instance_remote-prefix/src/rpmsg_multi_instance_remote-build/zephyr)
if("${BOARD}" STREQUAL "nrf5340dk_nrf5340_cpuapp")
set(BOARD_REMOTE "nrf5340dk_nrf5340_cpunet")
elseif("${BOARD}" STREQUAL "bl5340_dvk_cpuapp")
set(BOARD_REMOTE "bl5340_dvk_cpunet")
else()
message(FATAL_ERROR "${BOARD} is not supported for this sample")
endif()
message(INFO " ${BOARD} compile as Master in this sample")
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rpmsg_multi_instance)
enable_language(C ASM)
target_sources(app PRIVATE src/main.c)
include(ExternalProject)
ExternalProject_Add(
rpmsg_multi_instance_remote
SOURCE_DIR ${APPLICATION_SOURCE_DIR}/remote
INSTALL_COMMAND "" # This particular build system has no install command
CMAKE_CACHE_ARGS -DBOARD:STRING=${BOARD_REMOTE}
CMAKE_CACHE_ARGS -DDTC_OVERLAY_FILE:STRING=${DTC_OVERLAY_FILE}
BUILD_BYPRODUCTS "${REMOTE_ZEPHYR_DIR}/${KERNEL_BIN_NAME}"
# NB: Do we need to pass on more CMake variables?
BUILD_ALWAYS True
)

View file

@ -1,135 +0,0 @@
.. _Multiple_instance_RPMsg_sample:
Multiple instance of RPMsg
##########################
Overview
********
Multiple instance of RPMsg is an abstraction created over OpenAMP.
It simplifies the initialization and endpoint creation process.
This sample demonstrates how to use multi-instance RPMsg in Zephyr.
Building the application for nrf5340dk_nrf5340_cpuapp
*****************************************************
.. zephyr-app-commands::
:zephyr-app: samples/subsys/ipc/rpmsg_multi_instance
:board: nrf5340dk_nrf5340_cpuapp
:goals: debug
Open a serial terminal (for example Minicom or PuTTY) and connect the board with the following settings:
- Speed: 115200
- Data: 8 bits
- Parity: None
- Stop bits: 1
When you reset the development kit, the following messages (one for master and one for remote) will appear on the corresponding serial ports:
.. code-block:: console
*** Booting Zephyr OS build zephyr-v2.5.0-3564-gf89886d69a8c ***
Starting application thread!
RPMsg Multiple instance [master no 1] demo started
RPMsg Multiple instance [master no 2] demo started
Master [no 1] core received a message: 1
Master [no 2] core received a message: 1
Master [no 1] core received a message: 3
Master [no 2] core received a message: 3
Master [no 1] core received a message: 5
Master [no 2] core received a message: 5
...
Master [no 1] core received a message: 99
RPMsg Multiple instance [no 1] demo ended.
Master [no 2] core received a message: 99
RPMsg Multiple instance [no 2] demo ended.
.. code-block:: console
*** Booting Zephyr OS build zephyr-v2.5.0-3564-gf89886d69a8c ***
Starting application thread!
RPMsg Multiple instance [remote no 1] demo started
RPMsg Multiple instance [remote no 2] demo started
Remote [no 1] core received a message: 0
Remote [no 2] core received a message: 0
Remote [no 1] core received a message: 2
Remote [no 2] core received a message: 2
Remote [no 1] core received a message: 4
Remote [no 2] core received a message: 4
...
Remote [no 1] core received a message: 98
RPMsg Multiple instance [no 1] demo ended.
Remote [no 2] core received a message: 98
RPMsg Multiple instance [no 2] demo ended.
Building the application for bl5340_dvk_cpuapp
**********************************************
.. zephyr-app-commands::
:zephyr-app: samples/subsys/ipc/rpmsg_multi_instance
:board: bl5340_dvk_cpuapp
:goals: debug
.. zephyr-app-commands::
:zephyr-app: samples/subsys/ipc/rpmsg_multi_instance/remote
:board: bl5340_dvk_cpunet
:goals: debug
Open a serial terminal (for example Minicom or PuTTY) and connect to the board
with the following settings on both serial ports:
- Speed: 115200
- Data: 8 bits
- Parity: None
- Stop bits: 1
When you reset the development kit after having flashed both the application
and network core images, the following messages (one for master and one for
remote) will appear on the corresponding serial ports:
.. code-block:: console
*** Booting Zephyr OS build v2.7.0-rc1-103-ge19875c88916 ***
Starting application thread!
RPMsg Multiple instance [master no 1] demo started
RPMsg Multiple instance [master no 2] demo started
Master [no 1] core received a message: 1
Master [no 2] core received a message: 1
Master [no 1] core received a message: 3
Master [no 2] core received a message: 3
Master [no 1] core received a message: 5
Master [no 2] core received a message: 5
...
Master [no 1] core received a message: 99
RPMsg Multiple instance [no 1] demo ended.
Master [no 2] core received a message: 99
RPMsg Multiple instance [no 2] demo ended.
.. code-block:: console
*** Booting Zephyr OS build v2.7.0-rc1-103-ge19875c88916 ***
Starting application thread!
RPMsg Multiple instance [remote no 1] demo started
RPMsg Multiple instance [remote no 2] demo started
Remote [no 1] core received a message: 0
Remote [no 2] core received a message: 0
Remote [no 1] core received a message: 2
Remote [no 2] core received a message: 2
Remote [no 1] core received a message: 4
Remote [no 2] core received a message: 4
...
Remote [no 1] core received a message: 98
RPMsg Multiple instance [no 1] demo ended.
Remote [no 2] core received a message: 98
RPMsg Multiple instance [no 2] demo ended.

View file

@ -1,24 +0,0 @@
CONFIG_BOARD_ENABLE_CPUNET=y
#IPM Configuration
CONFIG_IPM=y
CONFIG_IPM_NRFX=y
# Enable all needed IPM channels
CONFIG_IPM_MSG_CH_0_ENABLE=y
CONFIG_IPM_MSG_CH_1_ENABLE=y
CONFIG_IPM_MSG_CH_2_ENABLE=y
CONFIG_IPM_MSG_CH_3_ENABLE=y
# Configure all RX channels
CONFIG_IPM_MSG_CH_0_RX=y
CONFIG_IPM_MSG_CH_2_RX=y
# Configure all TX channels
CONFIG_IPM_MSG_CH_1_TX=y
CONFIG_IPM_MSG_CH_3_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_3"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_2"

View file

@ -1,24 +0,0 @@
CONFIG_BOARD_ENABLE_CPUNET=y
#IPM Configuration
CONFIG_IPM=y
CONFIG_IPM_NRFX=y
# Enable all needed IPM channels
CONFIG_IPM_MSG_CH_0_ENABLE=y
CONFIG_IPM_MSG_CH_1_ENABLE=y
CONFIG_IPM_MSG_CH_2_ENABLE=y
CONFIG_IPM_MSG_CH_3_ENABLE=y
# Configure all RX channels
CONFIG_IPM_MSG_CH_0_RX=y
CONFIG_IPM_MSG_CH_2_RX=y
# Configure all TX channels
CONFIG_IPM_MSG_CH_1_TX=y
CONFIG_IPM_MSG_CH_3_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_3"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_2"

View file

@ -1,16 +0,0 @@
CONFIG_PRINTK=y
CONFIG_IPM=y
CONFIG_TIMESLICE_SIZE=1
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_HEAP_MEM_POOL_SIZE=4096
# Enable RPMsg multiple instance
CONFIG_RPMSG_MULTI_INSTANCE=y
CONFIG_RPMSG_MULTI_INSTANCE_MASTER=y
CONFIG_RPMSG_MULTI_INSTANCES_NO=2
CONFIG_OPENAMP=y
CONFIG_OPENAMP_SLAVE=n
CONFIG_LOG=y
CONFIG_RPMSG_MULTI_INSTANCE_LOG_LEVEL_INF=y

View file

@ -1,18 +0,0 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.20.0)
if("${BOARD}" STREQUAL "nrf5340dk_nrf5340_cpunet"
OR "${BOARD}" STREQUAL "bl5340_dvk_cpunet")
message(INFO " ${BOARD} compile as slave in this sample")
else()
message(FATAL_ERROR "${BOARD} is not supported for this sample")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rpmsg_multi_instance_remote)
target_sources(app PRIVATE src/main.c)

View file

@ -1,22 +0,0 @@
#IPM Configuration
CONFIG_IPM=y
CONFIG_IPM_NRFX=y
# Enable all needed IPM channels
CONFIG_IPM_MSG_CH_0_ENABLE=y
CONFIG_IPM_MSG_CH_1_ENABLE=y
CONFIG_IPM_MSG_CH_2_ENABLE=y
CONFIG_IPM_MSG_CH_3_ENABLE=y
# Configure all RX channels
CONFIG_IPM_MSG_CH_1_RX=y
CONFIG_IPM_MSG_CH_3_RX=y
# Configure all TX channels
CONFIG_IPM_MSG_CH_0_TX=y
CONFIG_IPM_MSG_CH_2_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_2"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_3"

View file

@ -1,22 +0,0 @@
#IPM Configuration
CONFIG_IPM=y
CONFIG_IPM_NRFX=y
# Enable all needed IPM channels
CONFIG_IPM_MSG_CH_0_ENABLE=y
CONFIG_IPM_MSG_CH_1_ENABLE=y
CONFIG_IPM_MSG_CH_2_ENABLE=y
CONFIG_IPM_MSG_CH_3_ENABLE=y
# Configure all RX channels
CONFIG_IPM_MSG_CH_1_RX=y
CONFIG_IPM_MSG_CH_3_RX=y
# Configure all TX channels
CONFIG_IPM_MSG_CH_0_TX=y
CONFIG_IPM_MSG_CH_2_TX=y
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME="IPM_0"
CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME="IPM_1"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME="IPM_2"
CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME="IPM_3"

View file

@ -1,16 +0,0 @@
CONFIG_STDOUT_CONSOLE=n
CONFIG_PRINTK=n
CONFIG_IPM=y
CONFIG_HEAP_MEM_POOL_SIZE=4096
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
# Enable Rpmsg multiple instance
CONFIG_RPMSG_MULTI_INSTANCE=y
CONFIG_RPMSG_MULTI_INSTANCE_REMOTE=y
CONFIG_RPMSG_MULTI_INSTANCES_NO=2
CONFIG_OPENAMP=y
CONFIG_OPENAMP_MASTER=n
CONFIG_LOG=y
CONFIG_RPMSG_MULTI_INSTANCE_LOG_LEVEL_INF=y

View file

@ -1,216 +0,0 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <drivers/ipm.h>
#include <sys/printk.h>
#include <device.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <init.h>
#include <ipc/rpmsg_multi_instance.h>
#define IPM_WORK_QUEUE_STACK_SIZE 1024
#define APP_TASK_STACK_SIZE 1024
#define IPM_MSG_ID 0
K_THREAD_STACK_DEFINE(ipm_stack_area_1, IPM_WORK_QUEUE_STACK_SIZE);
K_THREAD_STACK_DEFINE(ipm_stack_area_2, IPM_WORK_QUEUE_STACK_SIZE);
K_THREAD_STACK_DEFINE(thread_stack_1, APP_TASK_STACK_SIZE);
K_THREAD_STACK_DEFINE(thread_stack_2, APP_TASK_STACK_SIZE);
static struct k_thread thread_data_1;
static struct k_thread thread_data_2;
static K_SEM_DEFINE(bound_ept1_sem, 0, 1);
static K_SEM_DEFINE(bound_ept2_sem, 0, 1);
static K_SEM_DEFINE(data_rx1_sem, 0, 1);
static K_SEM_DEFINE(data_rx2_sem, 0, 1);
static volatile uint8_t received_data_1;
static volatile uint8_t received_data_2;
static struct rpmsg_mi_ctx ctx_1;
static struct rpmsg_mi_ctx ctx_2;
static struct rpmsg_mi_ept ept_1;
static struct rpmsg_mi_ept ept_2;
static void boud1_cb(void *priv)
{
k_sem_give(&bound_ept1_sem);
}
static void received1_cb(const void *data, size_t len, void *priv)
{
received_data_1 = *((uint8_t *)data);
k_sem_give(&data_rx1_sem);
}
static void boud2_cb(void *priv)
{
k_sem_give(&bound_ept2_sem);
}
static void received2_cb(const void *data, size_t len, void *priv)
{
received_data_2 = *((uint8_t *)data);
k_sem_give(&data_rx2_sem);
}
static struct rpmsg_mi_ctx_shm_cfg shm = {
.addr = SHM_START_ADDR,
.size = SHM_SIZE,
};
static const struct rpmsg_mi_ctx_cfg cfg_1 = {
.name = "instance 1",
.ipm_stack_area = ipm_stack_area_1,
.ipm_stack_size = K_THREAD_STACK_SIZEOF(ipm_stack_area_1),
.ipm_thread_name = "ipm_work_q_1",
.ipm_work_q_prio = 0,
.ipm_tx_name = CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME,
.ipm_rx_name = CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME,
.ipm_tx_id = IPM_MSG_ID,
.shm = &shm,
};
static const struct rpmsg_mi_ctx_cfg cfg_2 = {
.name = "instance 2",
.ipm_stack_area = ipm_stack_area_2,
.ipm_stack_size = K_THREAD_STACK_SIZEOF(ipm_stack_area_2),
.ipm_thread_name = "ipm_work_q_2",
.ipm_work_q_prio = 0,
.ipm_tx_name = CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME,
.ipm_rx_name = CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME,
.ipm_tx_id = IPM_MSG_ID,
.shm = &shm,
};
static struct rpmsg_mi_cb cb_1 = {
.bound = boud1_cb,
.received = received1_cb,
};
static struct rpmsg_mi_cb cb_2 = {
.bound = boud2_cb,
.received = received2_cb,
};
static struct rpmsg_mi_ept_cfg ept_cfg_1 = {
.name = "ept1",
.cb = &cb_1,
.priv = &ept_1,
};
static struct rpmsg_mi_ept_cfg ept_cfg_2 = {
.name = "ept2",
.cb = &cb_2,
.priv = &ept_2,
};
void app_task_1(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
int status = 0;
uint8_t message = 0U;
printk("\r\nRPMsg Multiple instance [remote no 1] demo started\r\n");
/* Initialization of 1 instance */
status = rpmsg_mi_ctx_init(&ctx_1, &cfg_1);
if (status < 0) {
printk("rpmsg_mi_init [no 1] failed with status %d\n", status);
}
status = rpmsg_mi_ept_register(&ctx_1, &ept_1, &ept_cfg_1);
if (status < 0) {
printk("rpmsg_mi_ept_register [no 1] failed with status %d\n",
status);
}
/* Waiting to be bound. */
k_sem_take(&bound_ept1_sem, K_FOREVER);
while (message < 99) {
k_sem_take(&data_rx1_sem, K_FOREVER);
message = received_data_1;
printk("Remote [no 1] core received a message: %d\n", message);
message++;
status = rpmsg_mi_send(&ept_1, &message, sizeof(message));
if (status < 0) {
printk("send_message(%d) failed with status %d\n",
message, status);
}
}
printk("RPMsg Multiple instance [no 1] demo ended.\n");
}
void app_task_2(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
int status = 0;
uint8_t message = 0U;
printk("\r\nRPMsg Multiple instance [remote no 2] demo started\r\n");
/* Initialization of 2 instance */
status = rpmsg_mi_ctx_init(&ctx_2, &cfg_2);
if (status < 0) {
printk("rpmsg_mi_init [no 2] failed with status %d\n", status);
}
status = rpmsg_mi_ept_register(&ctx_2, &ept_2, &ept_cfg_2);
if (status < 0) {
printk("rpmsg_mi_ept_register [no 2] failed with status %d\n",
status);
}
/* Waiting to be bound. */
k_sem_take(&bound_ept2_sem, K_FOREVER);
while (message < 99) {
k_sem_take(&data_rx2_sem, K_FOREVER);
message = received_data_2;
printk("Remote [no 2] core received a message: %d\n", message);
/* k_sleep(K_MSEC(1));*/ /* for testing*/
message++;
status = rpmsg_mi_send(&ept_2, &message, sizeof(message));
if (status < 0) {
printk("send_message(%d) failed with status %d\n",
message, status);
}
}
printk("RPMsg Multiple instance [no 2] demo ended.\n");
}
void main(void)
{
printk("Starting application thread!\n");
k_thread_create(&thread_data_1, thread_stack_1, APP_TASK_STACK_SIZE,
(k_thread_entry_t)app_task_1,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
k_thread_create(&thread_data_2, thread_stack_2, APP_TASK_STACK_SIZE,
(k_thread_entry_t)app_task_2,
NULL, NULL, NULL, K_PRIO_COOP(8), 0, K_NO_WAIT);
}

View file

@ -1,11 +0,0 @@
sample:
description: This app provides an example of how to integrate
RPMsg Multiple Instance with Zephyr.
name: RPMsg Multiple Instance example integration
tests:
sample.ipc.rpmsg_multiple_instance.nrf:
platform_allow: nrf5340dk_nrf5340_cpuapp bl5340_dvk_cpuapp
integration_platforms:
- nrf5340dk_nrf5340_cpuapp bl5340_dvk_cpuapp
tags: ipm
build_only: true

View file

@ -1,217 +0,0 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <drivers/ipm.h>
#include <sys/printk.h>
#include <device.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <init.h>
#include <ipc/rpmsg_multi_instance.h>
#define IPM_WORK_QUEUE_STACK_SIZE 1024
#define APP_TASK_STACK_SIZE 1024
#define IPM_MSG_ID 0
K_THREAD_STACK_DEFINE(ipm_stack_area_1, IPM_WORK_QUEUE_STACK_SIZE);
K_THREAD_STACK_DEFINE(ipm_stack_area_2, IPM_WORK_QUEUE_STACK_SIZE);
K_THREAD_STACK_DEFINE(thread_stack_1, APP_TASK_STACK_SIZE);
K_THREAD_STACK_DEFINE(thread_stack_2, APP_TASK_STACK_SIZE);
static struct k_thread thread_data_1;
static struct k_thread thread_data_2;
static K_SEM_DEFINE(bound_ept1_sem, 0, 1);
static K_SEM_DEFINE(bound_ept2_sem, 0, 1);
static K_SEM_DEFINE(data_rx1_sem, 0, 1);
static K_SEM_DEFINE(data_rx2_sem, 0, 1);
static volatile uint8_t received_data_1;
static volatile uint8_t received_data_2;
static struct rpmsg_mi_ctx ctx_1;
static struct rpmsg_mi_ctx ctx_2;
static struct rpmsg_mi_ept ept_1;
static struct rpmsg_mi_ept ept_2;
static void boud1_cb(void *priv)
{
k_sem_give(&bound_ept1_sem);
}
static void received1_cb(const void *data, size_t len, void *priv)
{
received_data_1 = *((uint8_t *)data);
k_sem_give(&data_rx1_sem);
}
static void boud2_cb(void *priv)
{
k_sem_give(&bound_ept2_sem);
}
static void received2_cb(const void *data, size_t len, void *priv)
{
received_data_2 = *((uint8_t *)data);
k_sem_give(&data_rx2_sem);
}
static struct rpmsg_mi_ctx_shm_cfg shm = {
.addr = SHM_START_ADDR,
.size = SHM_SIZE,
};
static const struct rpmsg_mi_ctx_cfg cfg_1 = {
.name = "instance 1",
.ipm_stack_area = ipm_stack_area_1,
.ipm_stack_size = K_THREAD_STACK_SIZEOF(ipm_stack_area_1),
.ipm_thread_name = "ipm_work_q_1",
.ipm_work_q_prio = 0,
.ipm_tx_name = CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_TX_NAME,
.ipm_rx_name = CONFIG_RPMSG_MULTI_INSTANCE_0_IPM_RX_NAME,
.ipm_tx_id = IPM_MSG_ID,
.shm = &shm,
};
static const struct rpmsg_mi_ctx_cfg cfg_2 = {
.name = "instance 2",
.ipm_stack_area = ipm_stack_area_2,
.ipm_stack_size = K_THREAD_STACK_SIZEOF(ipm_stack_area_2),
.ipm_thread_name = "ipm_work_q_2",
.ipm_work_q_prio = 0,
.ipm_tx_name = CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_TX_NAME,
.ipm_rx_name = CONFIG_RPMSG_MULTI_INSTANCE_1_IPM_RX_NAME,
.ipm_tx_id = IPM_MSG_ID,
.shm = &shm,
};
static struct rpmsg_mi_cb cb_1 = {
.bound = boud1_cb,
.received = received1_cb,
};
static struct rpmsg_mi_cb cb_2 = {
.bound = boud2_cb,
.received = received2_cb,
};
static struct rpmsg_mi_ept_cfg ept_cfg_1 = {
.name = "ept1",
.cb = &cb_1,
.priv = &ept_1,
};
static struct rpmsg_mi_ept_cfg ept_cfg_2 = {
.name = "ept2",
.cb = &cb_2,
.priv = &ept_2,
};
void app_task_1(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
int status = 0;
uint8_t message = 0U;
printk("\r\nRPMsg Multiple instance [master no 1] demo started\r\n");
/* Initialization of 1 instance */
status = rpmsg_mi_ctx_init(&ctx_1, &cfg_1);
if (status < 0) {
printk("rpmsg_mi_init for [no 1] failed with status %d\n",
status);
}
status = rpmsg_mi_ept_register(&ctx_1, &ept_1, &ept_cfg_1);
if (status < 0) {
printk("rpmsg_mi_ept_register [no 1] failed with status %d\n",
status);
}
/* Waiting to be bound. */
k_sem_take(&bound_ept1_sem, K_FOREVER);
while (message < 100) {
status = rpmsg_mi_send(&ept_1, &message, sizeof(message));
if (status < 0) {
printk("send_message(%d) failed with status %d\n",
message, status);
}
k_sem_take(&data_rx1_sem, K_FOREVER);
message = received_data_1;
printk("Master [no 1] core received a message: %d\n", message);
message++;
}
printk("RPMsg Multiple instance [no 1] demo ended.\n");
}
void app_task_2(void *arg1, void *arg2, void *arg3)
{
ARG_UNUSED(arg1);
ARG_UNUSED(arg2);
ARG_UNUSED(arg3);
int status = 0;
uint8_t message = 0U;
printk("\r\nRPMsg Multiple instance [master no 2] demo started\r\n");
/* Initialization of 2 instance */
status = rpmsg_mi_ctx_init(&ctx_2, &cfg_2);
if (status < 0) {
printk("rpmsg_mi_init [no 2] failed with status %d\n", status);
}
status = rpmsg_mi_ept_register(&ctx_2, &ept_2, &ept_cfg_2);
if (status < 0) {
printk("rpmsg_mi_ept_register [no 2] failed with status %d\n",
status);
}
/* Waiting to be bound. */
k_sem_take(&bound_ept2_sem, K_FOREVER);
while (message < 100) {
status = rpmsg_mi_send(&ept_2, &message, sizeof(message));
if (status < 0) {
printk("send_message(%d) failed with status %d\n",
message, status);
}
k_sem_take(&data_rx2_sem, K_FOREVER);
message = received_data_2;
printk("Master [no 2] core received a message: %d\n", message);
message++;
}
printk("RPMsg Multiple instance [no 2] demo ended.\n");
}
void main(void)
{
printk("Starting application thread!\n");
k_thread_create(&thread_data_1, thread_stack_1, APP_TASK_STACK_SIZE,
(k_thread_entry_t)app_task_1,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
k_thread_create(&thread_data_2, thread_stack_2, APP_TASK_STACK_SIZE,
(k_thread_entry_t)app_task_2,
NULL, NULL, NULL, K_PRIO_COOP(8), 0, K_NO_WAIT);
}

View file

@ -1,5 +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)
add_subdirectory_ifdef(CONFIG_IPC_SERVICE ipc_service)

View file

@ -6,7 +6,6 @@
menu "Inter Processor Communication"
source "subsys/ipc/rpmsg_service/Kconfig"
source "subsys/ipc/rpmsg_multi_instance/Kconfig"
source "subsys/ipc/ipc_service/Kconfig"
endmenu

View file

@ -11,23 +11,15 @@ menuconfig IPC_SERVICE
if IPC_SERVICE
choice IPC_SERVICE_BACKEND
prompt "IPC Service backend"
rsource "backends/Kconfig"
config IPC_SERVICE_BACKEND_RPMSG_MULTI_INSTANCE
bool "RPMsg multiple instance backend"
select RPMSG_MULTI_INSTANCE
config IPC_SERVICE_NUM_ENDPOINTS_PER_INSTANCE
int "Max number of registered endpoints per instance"
default 2
help
Maximal number of endpoints that can be registered for one instance.
endchoice
if IPC_SERVICE_BACKEND_RPMSG_MULTI_INSTANCE
rsource "backends/Kconfig.rpmsg_mi"
endif # IPC_SERVICE_BACKEND_RPMSG_MULTI_INSTANCE
config IPC_SERVICE_BACKEND_REG_PRIORITY
config IPC_SERVICE_REG_BACKEND_PRIORITY
int "Initialization priority of modules registering IPC backend"
default 46
help

View file

@ -1,3 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_RPMSG_MULTI_INSTANCE ipc_rpmsg_multi_instance.c)
# libraries
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_RPMSG ipc_rpmsg.c)
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_STATIC_VRINGS ipc_static_vrings.c)
# backends
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI ipc_rpmsg_static_vrings_mi.c)

View file

@ -0,0 +1,30 @@
# Copyright (c) 2021 Nordic Semiconductor (ASA)
# SPDX-License-Identifier: Apache-2.0
choice IPC_SERVICE_BACKEND
prompt "IPC service backend"
config IPC_SERVICE_BACKEND_RPMSG_MI
bool "RPMSG backend - static VRINGs (multi-instance)"
select IPC_SERVICE_RPMSG
select IPC_SERVICE_STATIC_VRINGS
select OPENAMP
select IPM
endchoice
if IPC_SERVICE_BACKEND_RPMSG_MI
rsource "Kconfig.rpmsg_mi"
endif # IP_SERVICE_BACKEND_RPMSG_MI
config IPC_SERVICE_RPMSG
bool "RPMsg support library"
help
"RPMsg library"
config IPC_SERVICE_STATIC_VRINGS
bool "Static VRINGs support library"
help
"Static VRINGs library"

View file

@ -1,13 +1,13 @@
# Copyright (c) 2021 Nordic Semiconductor (ASA)
# SPDX-License-Identifier: Apache-2.0
config RPMSG_MULTI_INSTANCE_$(ipm_name_instance_num)_IPM_TX_NAME
config IPC_SERVICE_BACKEND_RPMSG_MI_$(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
config IPC_SERVICE_BACKEND_RPMSG_MI_$(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

View file

@ -1,17 +1,60 @@
# Copyright (c) 2020-2021 Nordic Semiconductor (ASA)
# SPDX-License-Identifier: Apache-2.0
config IPC_BACKEND_RPMSG_MI_WORK_QUEUE_STACK_SIZE
int "Size of RX work queue stack"
choice IPC_SERVICE_BACKEND_RPMSG_MI_ROLE
prompt "IPC service device role"
config IPC_SERVICE_BACKEND_RPMSG_MI_REMOTE
bool "Remote"
config IPC_SERVICE_BACKEND_RPMSG_MI_MASTER
bool "Master"
endchoice
config IPC_SERVICE_BACKEND_RPMSG_MI_WQ_STACK_SIZE
int "Size of RX work queue stack"
default 1024
help
Size of stack used by work queue RX thread. This work queue is
created in the multi-instance RPMsg backend module to prevent notifying
service users about received data from the system work queue.
Size is the same for all instances.
created in the multi-instance / multi-core RPMsg backend module to
prevent notifying service users about received data from the system
work queue. Size is the same for all instances.
config IPC_BACKEND_RPMSG_MI_NUM_ENDPOINTS_PER_INSTANCE
int "Max number of registered endpoints per instance"
default 2
config IPC_SERVICE_BACKEND_RPMSG_MI_SHM_BASE_ADDRESS
hex
default "$(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_IPC_SHM))"
help
Maximal number of endpoints that can be registered for one instance.
This option specifies base address of the memory region to
be used for the OpenAMP IPC shared memory.
config IPC_SERVICE_BACKEND_RPMSG_MI_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 IPC_SERVICE_BACKEND_RPMSG_MI_NUM_INSTANCES
int "Number of RPMsg instances"
default 2
range 1 8
help
How many instances are to be used.
ipm_name_instance_num = 0
rsource "Kconfig.ipm_name_instance"
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"

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <ipc/ipc_rpmsg.h>
static void rpmsg_service_unbind(struct rpmsg_endpoint *ep)
{
rpmsg_destroy_ept(ep);
}
static void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest)
{
struct rpmsg_virtio_device *p_rvdev;
struct ipc_rpmsg_instance *instance;
struct ipc_ept *ept;
int err;
p_rvdev = CONTAINER_OF(rdev, struct rpmsg_virtio_device, rdev);
instance = CONTAINER_OF(p_rvdev->shpool, struct ipc_rpmsg_instance, shm_pool);
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
ept = &instance->endpoint[i];
if (strcmp(name, ept->name) == 0) {
err = rpmsg_create_ept(&ept->ep, rdev, name, RPMSG_ADDR_ANY,
dest, instance->cb, rpmsg_service_unbind);
if (err != 0) {
return;
}
ept->bound = true;
if (instance->bound_cb) {
instance->bound_cb(ept);
}
}
}
}
int ipc_rpmsg_register_ept(struct ipc_rpmsg_instance *instance, unsigned int role,
struct ipc_ept *ept)
{
struct rpmsg_device *rdev;
if (!instance || !ept) {
return -EINVAL;
}
rdev = rpmsg_virtio_get_rpmsg_device(&instance->rvdev);
if (role == RPMSG_REMOTE) {
return rpmsg_create_ept(&ept->ep, rdev, ept->name, RPMSG_ADDR_ANY,
RPMSG_ADDR_ANY, instance->cb, rpmsg_service_unbind);
}
return RPMSG_SUCCESS;
}
int ipc_rpmsg_init(struct ipc_rpmsg_instance *instance,
unsigned int role,
struct metal_io_region *shm_io,
struct virtio_device *vdev,
void *shb, size_t size,
rpmsg_ns_bind_cb p_bind_cb)
{
rpmsg_ns_bind_cb bind_cb = p_bind_cb;
if (!instance || !shb) {
return -EINVAL;
}
if (p_bind_cb == NULL) {
bind_cb = ns_bind_cb;
}
if (role == RPMSG_MASTER) {
rpmsg_virtio_init_shm_pool(&instance->shm_pool, shb, size);
return rpmsg_init_vdev(&instance->rvdev, vdev, bind_cb,
shm_io, &instance->shm_pool);
} else {
return rpmsg_init_vdev(&instance->rvdev, vdev, bind_cb, shm_io, NULL);
}
}

View file

@ -1,201 +0,0 @@
/*
* Copyright (c) 2020-2021, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ipc/ipc_service_backend.h>
#include <ipc/rpmsg_multi_instance.h>
#include <logging/log.h>
#include <sys/util_macro.h>
#include <zephyr.h>
#include <device.h>
LOG_MODULE_REGISTER(ipc_rpmsg_multi_instance, CONFIG_IPC_SERVICE_LOG_LEVEL);
#define NUM_INSTANCES CONFIG_RPMSG_MULTI_INSTANCES_NO
#define NUM_ENDPOINTS CONFIG_IPC_BACKEND_RPMSG_MI_NUM_ENDPOINTS_PER_INSTANCE
#define WORK_QUEUE_STACK_SIZE CONFIG_IPC_BACKEND_RPMSG_MI_WORK_QUEUE_STACK_SIZE
#define PRIO_INIT_VAL INT_MAX
#define INSTANCE_NAME_SIZE 16
#define IPM_MSG_ID 0
#define CH_NAME(idx, sub) (CONFIG_RPMSG_MULTI_INSTANCE_ ## idx ## _IPM_ ## sub ## _NAME)
static char *ipm_rx_name[] = {
FOR_EACH_FIXED_ARG(CH_NAME, (,), RX, 0, 1, 2, 3, 4, 5, 6, 7),
};
static char *ipm_tx_name[] = {
FOR_EACH_FIXED_ARG(CH_NAME, (,), TX, 0, 1, 2, 3, 4, 5, 6, 7),
};
BUILD_ASSERT(ARRAY_SIZE(ipm_rx_name) >= NUM_INSTANCES, "Invalid configuration");
BUILD_ASSERT(ARRAY_SIZE(ipm_tx_name) >= NUM_INSTANCES, "Invalid configuration");
K_THREAD_STACK_ARRAY_DEFINE(ipm_stack, NUM_INSTANCES, WORK_QUEUE_STACK_SIZE);
struct ipc_ept {
struct rpmsg_mi_ept rpmsg_ep;
struct ipc_service_cb cb;
const char *name;
void *priv;
};
struct ipc_rpmsg_mi_instances {
struct ipc_ept endpoints[NUM_ENDPOINTS];
char name[INSTANCE_NAME_SIZE];
struct rpmsg_mi_ctx ctx;
bool is_initialized;
int prio;
};
static struct ipc_rpmsg_mi_instances instances[NUM_INSTANCES];
static struct rpmsg_mi_ctx_shm_cfg shm = {
.addr = SHM_START_ADDR,
.size = SHM_SIZE,
};
static void common_bound_cb(void *priv)
{
struct ipc_ept *ept = (struct ipc_ept *)priv;
if (ept->cb.bound) {
ept->cb.bound(ept->priv);
}
}
static void common_recv_cb(const void *data, size_t len, void *priv)
{
struct ipc_ept *ept = (struct ipc_ept *)priv;
if (ept->cb.received) {
ept->cb.received(data, len, ept->priv);
}
}
static struct rpmsg_mi_cb cb = {
.bound = common_bound_cb,
.received = common_recv_cb,
};
static int send(struct ipc_ept *ept, const void *data, size_t len)
{
return rpmsg_mi_send(&ept->rpmsg_ep, data, len);
}
static int get_available_instance(const struct ipc_ept_cfg *cfg)
{
/* Endpoints with the same priority are
* registered to the same instance.
*/
for (size_t i = 0; i < NUM_INSTANCES; i++) {
if (instances[i].prio == cfg->prio || instances[i].prio == PRIO_INIT_VAL) {
return (int)i;
}
}
return -ENOMEM;
}
static int get_available_ept_slot(struct ipc_rpmsg_mi_instances *instance)
{
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (!(instance->endpoints[i].name)) {
return (int)i;
}
}
return -ENOMEM;
}
static int register_ept(struct ipc_ept **ept, const struct ipc_ept_cfg *cfg)
{
struct rpmsg_mi_ept_cfg ept_cfg = { 0 };
int i_idx, e_idx;
if (!cfg || !ept) {
return -EINVAL;
}
i_idx = get_available_instance(cfg);
if (i_idx < 0) {
LOG_ERR("Available instance not found");
return -EIO;
}
/* Initialization of the instance context is performed only once.
* When registering the first endpoint for the instance.
*/
if (!instances[i_idx].is_initialized) {
struct rpmsg_mi_ctx_cfg ctx_cfg = { 0 };
snprintf(instances[i_idx].name, INSTANCE_NAME_SIZE, "rpmsg_mi_%d", i_idx);
ctx_cfg.name = instances[i_idx].name;
ctx_cfg.ipm_stack_area = ipm_stack[i_idx];
ctx_cfg.ipm_stack_size = K_THREAD_STACK_SIZEOF(ipm_stack[i_idx]);
ctx_cfg.ipm_work_q_prio = cfg->prio;
ctx_cfg.ipm_thread_name = instances[i_idx].name;
ctx_cfg.ipm_rx_name = ipm_rx_name[i_idx];
ctx_cfg.ipm_tx_name = ipm_tx_name[i_idx];
ctx_cfg.ipm_tx_id = IPM_MSG_ID;
ctx_cfg.shm = &shm;
if (rpmsg_mi_ctx_init(&instances[i_idx].ctx, &ctx_cfg) < 0) {
LOG_ERR("Instance initialization failed");
return -EIO;
}
instances[i_idx].is_initialized = true;
}
e_idx = get_available_ept_slot(&instances[i_idx]);
if (e_idx < 0) {
LOG_ERR("No free slots to register endpoint %s", log_strdup(cfg->name));
return -EIO;
}
instances[i_idx].endpoints[e_idx].priv = cfg->priv;
instances[i_idx].endpoints[e_idx].cb = cfg->cb;
ept_cfg.cb = &cb;
ept_cfg.priv = &instances[i_idx].endpoints[e_idx];
ept_cfg.name = cfg->name;
if (rpmsg_mi_ept_register(&instances[i_idx].ctx,
&instances[i_idx].endpoints[e_idx].rpmsg_ep, &ept_cfg) < 0) {
LOG_ERR("Register endpoint failed");
return -EIO;
}
instances[i_idx].endpoints[e_idx].name = cfg->name;
instances[i_idx].prio = cfg->prio;
*ept = &instances[i_idx].endpoints[e_idx];
return 0;
}
const static struct ipc_service_backend backend = {
.name = "RPMsg multi-instace backend",
.send = send,
.register_endpoint = register_ept,
};
static int backend_init(const struct device *dev)
{
ARG_UNUSED(dev);
for (size_t i = 0; i < NUM_INSTANCES; i++) {
instances[i].prio = PRIO_INIT_VAL;
}
return ipc_service_register_backend(&backend);
}
SYS_INIT(backend_init, POST_KERNEL, CONFIG_IPC_SERVICE_BACKEND_REG_PRIORITY);

View file

@ -0,0 +1,326 @@
/*
* Copyright (c) 2020-2021, Nordic Semiconductor ASA
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ipc/ipc_service_backend.h>
#include <ipc/ipc_rpmsg.h>
#include <ipc/ipc_static_vrings.h>
#include <logging/log.h>
#include <zephyr.h>
#include <cache.h>
#include <device.h>
#include <drivers/ipm.h>
#include "ipc_rpmsg_static_vrings_mi.h"
LOG_MODULE_REGISTER(ipc_rpmsg_multi_instance, CONFIG_IPC_SERVICE_LOG_LEVEL);
#define WQ_STACK_SIZE CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_WQ_STACK_SIZE
#define PRIO_INIT_VAL INT_MAX
#define INST_NAME_SIZE 16
#define IPM_MSG_ID 0
#define CH_NAME(idx, sub) (CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_ ## idx ## _IPM_ ## sub ## _NAME)
static char *ipm_rx_name[] = {
FOR_EACH_FIXED_ARG(CH_NAME, (,), RX, 0, 1, 2, 3, 4, 5, 6, 7),
};
static char *ipm_tx_name[] = {
FOR_EACH_FIXED_ARG(CH_NAME, (,), TX, 0, 1, 2, 3, 4, 5, 6, 7),
};
BUILD_ASSERT(ARRAY_SIZE(ipm_rx_name) >= NUM_INSTANCES, "Invalid configuration");
BUILD_ASSERT(ARRAY_SIZE(ipm_tx_name) >= NUM_INSTANCES, "Invalid configuration");
K_THREAD_STACK_ARRAY_DEFINE(ipm_stack, NUM_INSTANCES, WQ_STACK_SIZE);
struct rpmsg_mi_instance {
/* RPMsg */
struct ipc_rpmsg_instance rpmsg_inst;
/* Static VRINGs */
struct ipc_static_vrings vr;
/* General */
char name[INST_NAME_SIZE];
bool is_initialized;
unsigned int id;
/* IPM */
const struct device *ipm_tx_handle;
const struct device *ipm_rx_handle;
struct k_work_q ipm_wq;
struct k_work ipm_work;
int priority;
/* Role */
unsigned int role;
};
struct {
uintptr_t addr;
size_t size;
size_t instance;
} shm = {
.addr = SHM_START_ADDR,
.size = SHM_SIZE,
};
static struct rpmsg_mi_instance instance[NUM_INSTANCES];
static int send(struct ipc_ept *ept, const void *data, size_t len)
{
return rpmsg_send(&ept->ep, data, len);
}
static struct rpmsg_mi_instance *get_available_instance(const struct ipc_ept_cfg *cfg)
{
/* Endpoints with the same priority are registered to the same instance. */
for (size_t i = 0; i < NUM_INSTANCES; i++) {
if (instance[i].priority == cfg->prio || instance[i].priority == PRIO_INIT_VAL) {
return &instance[i];
}
}
return NULL;
}
static struct ipc_ept *get_available_ept_slot(struct ipc_rpmsg_instance *rpmsg_instance)
{
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (rpmsg_instance->endpoint[i].name == NULL) {
return &rpmsg_instance->endpoint[i];
}
}
return NULL;
}
static void ipm_callback_process(struct k_work *item)
{
struct rpmsg_mi_instance *instance;
unsigned int id;
instance = CONTAINER_OF(item, struct rpmsg_mi_instance, ipm_work);
id = (instance->role == VIRTIO_DEV_MASTER) ?
VIRTQUEUE_ID_MASTER : VIRTQUEUE_ID_REMOTE;
virtqueue_notification(instance->vr.vq[id]);
}
static void ipm_callback(const struct device *dev, void *context, uint32_t id, volatile void *data)
{
struct rpmsg_mi_instance *instance = (struct rpmsg_mi_instance *) context;
k_work_submit_to_queue(&instance->ipm_wq, &instance->ipm_work);
}
static int ipm_setup(struct rpmsg_mi_instance *instance)
{
instance->ipm_tx_handle = device_get_binding(ipm_tx_name[instance->id]);
if (instance->ipm_tx_handle == NULL) {
return -ENODEV;
}
instance->ipm_rx_handle = device_get_binding(ipm_rx_name[instance->id]);
if (instance->ipm_rx_handle == NULL) {
return -ENODEV;
}
k_work_queue_start(&instance->ipm_wq, ipm_stack[instance->id],
K_THREAD_STACK_SIZEOF(ipm_stack[instance->id]),
instance->priority, NULL);
k_thread_name_set(&instance->ipm_wq.thread, instance->name);
k_work_init(&instance->ipm_work, ipm_callback_process);
ipm_register_callback(instance->ipm_rx_handle, ipm_callback, instance);
return ipm_set_enabled(instance->ipm_rx_handle, 1);
}
static void shm_configure(struct rpmsg_mi_instance *instance)
{
size_t vring_size, shm_size, shm_local_size;
size_t rpmsg_reg_size, vring_reg_size;
uintptr_t shm_addr, shm_local_addr;
vring_size = VRING_SIZE_GET(shm.size);
shm_addr = SHMEM_INST_ADDR_AUTOALLOC_GET(shm.addr, shm.size, shm.instance);
shm_size = SHMEM_INST_SIZE_AUTOALLOC_GET(shm.size);
shm_local_addr = shm_addr + VDEV_STATUS_SIZE;
shm_local_size = shm_size - VDEV_STATUS_SIZE;
rpmsg_reg_size = VRING_COUNT * VIRTQUEUE_SIZE_GET(vring_size);
vring_reg_size = VRING_SIZE_COMPUTE(vring_size, VRING_ALIGNMENT);
instance->vr.status_reg_addr = shm_addr;
instance->vr.vring_size = vring_size;
instance->vr.rx_addr = shm_local_addr + rpmsg_reg_size;
instance->vr.tx_addr = instance->vr.rx_addr + vring_reg_size;
instance->vr.shm_addr = shm_local_addr;
instance->vr.shm_size = shm_local_size;
}
static void bound_cb(struct ipc_ept *ept)
{
/* Notify the remote site that binding has occurred */
rpmsg_send(&ept->ep, (uint8_t *)"", 0);
if (ept->cb->bound) {
ept->cb->bound(ept->priv);
}
}
static int ept_cb(struct rpmsg_endpoint *ep, void *data, size_t len, uint32_t src, void *priv)
{
struct ipc_ept *ept;
ept = (struct ipc_ept *) priv;
if (len == 0) {
if (!ept->bound) {
ept->bound = true;
bound_cb(ept);
}
return RPMSG_SUCCESS;
}
if (ept->cb->received) {
ept->cb->received(data, len, ept->priv);
}
return RPMSG_SUCCESS;
}
static void virtio_notify_cb(struct virtqueue *vq, void *priv)
{
struct rpmsg_mi_instance *instance;
instance = (struct rpmsg_mi_instance *) priv;
if (instance) {
ipm_send(instance->ipm_tx_handle, 0, IPM_MSG_ID, NULL, 0);
}
}
static int init_instance(struct rpmsg_mi_instance *instance)
{
int err = 0;
/* Check if there is enough space in the SHM */
if (SHMEM_INST_SIZE_AUTOALLOC_GET(shm.size) * NUM_INSTANCES > shm.size) {
return -ENOMEM;
}
shm_configure(instance);
instance->vr.notify_cb = virtio_notify_cb;
instance->vr.priv = instance;
ipc_static_vrings_init(&instance->vr, instance->role);
if (err != 0) {
return err;
}
err = ipm_setup(instance);
if (err != 0) {
return err;
}
shm.instance++;
return 0;
}
static int register_ept(struct ipc_ept **r_ept, const struct ipc_ept_cfg *cfg)
{
struct ipc_rpmsg_instance *rpmsg_instance;
struct rpmsg_mi_instance *instance;
struct ipc_ept *ept;
int err;
if (!cfg || !r_ept) {
return -EINVAL;
}
instance = get_available_instance(cfg);
if (instance == NULL) {
return -ENODEV;
}
rpmsg_instance = &instance->rpmsg_inst;
if (!instance->is_initialized) {
snprintf(instance->name, INST_NAME_SIZE, "rpmsg_mi_%d", instance->id);
instance->priority = cfg->prio;
err = init_instance(instance);
if (err) {
return err;
}
rpmsg_instance->bound_cb = bound_cb;
rpmsg_instance->cb = ept_cb;
err = ipc_rpmsg_init(rpmsg_instance,
instance->role,
instance->vr.shm_io,
&instance->vr.vdev,
(void *) instance->vr.shm_device.regions->virt,
instance->vr.shm_device.regions->size, NULL);
if (err != 0) {
return err;
}
instance->is_initialized = true;
}
ept = get_available_ept_slot(rpmsg_instance);
if (ept == NULL) {
return -ENODEV;
}
ept->name = cfg->name;
ept->cb = &cfg->cb;
ept->priv = cfg->priv;
ept->bound = false;
ept->ep.priv = ept;
err = ipc_rpmsg_register_ept(rpmsg_instance, instance->role, ept);
if (err != 0) {
return err;
}
*r_ept = ept;
return 0;
}
const static struct ipc_service_backend backend = {
.name = "RPMSG backend - static VRINGs (multi-instance)",
.send = send,
.register_endpoint = register_ept,
};
static int backend_init(const struct device *dev)
{
ARG_UNUSED(dev);
for (size_t i = 0; i < NUM_INSTANCES; i++) {
instance[i].priority = PRIO_INIT_VAL;
instance[i].id = i;
instance[i].role = IS_ENABLED(CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_MASTER) ?
VIRTIO_DEV_MASTER : VIRTIO_DEV_SLAVE;
}
return ipc_service_register_backend(&backend);
}
SYS_INIT(backend_init, POST_KERNEL, CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY);

View file

@ -4,13 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
#define SHM_DEVICE_NAME "sram0.shm"
#define SHM_START_ADDR CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_SHM_BASE_ADDRESS
#define SHM_SIZE CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_SHM_SIZE
#define VRING_ALIGNMENT (4) /* Alignment of vring buffer */
#define VDEV_STATUS_SIZE (0x4) /* Size of status region */
#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) /* Number of IPC instances.*/
#define NUM_INSTANCES (CONFIG_IPC_SERVICE_BACKEND_RPMSG_MI_NUM_INSTANCES)
/* Private macros. */
#define VRING_DESC_SIZEOF(num) ((num) * (sizeof(struct vring_desc)))
@ -37,7 +39,7 @@
(VRING_ALIGNMENT))))
/* Returns size of used shared memory consumed by all IPC instances*/
#define SHMEM_CONSUMED_SIZE_GET(vring_size) (IPC_INSTANCE_COUNT * \
#define SHMEM_CONSUMED_SIZE_GET(vring_size) (NUM_INSTANCES * \
SHMEM_INST_SIZE_GET((vring_size)))
/* Returns maximum allowable size of vring buffers to fit memory requirements. */
@ -60,8 +62,5 @@
((shmem_addr) + \
((id) * (SHMEM_INST_SIZE_AUTOALLOC_GET(shmem_size))))
#ifdef CONFIG_RPMSG_MULTI_INSTANCE_MASTER
#define VIRTQUEUE_ID (0)
#else
#define VIRTQUEUE_ID (1)
#endif
#define VIRTQUEUE_ID_MASTER (0)
#define VIRTQUEUE_ID_REMOTE (1)

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ipc/ipc_static_vrings.h>
#include <cache.h>
#define SHM_DEVICE_NAME "sram0.shm"
#define VRING_ALIGNMENT (4) /* Alignment of vring buffer */
#define RPMSG_VQ_0 (0) /* TX virtqueue queue index */
#define RPMSG_VQ_1 (1) /* RX virtqueue queue index */
static void virtio_notify(struct virtqueue *vq)
{
struct ipc_static_vrings *vr;
vr = CONTAINER_OF(vq->vq_dev, struct ipc_static_vrings, vdev);
if (vr->notify_cb) {
vr->notify_cb(vq, vr->priv);
}
}
static void virtio_set_features(struct virtio_device *vdev, uint32_t features)
{
/* No need for implementation */
}
static void virtio_set_status(struct virtio_device *p_vdev, unsigned char status)
{
struct ipc_static_vrings *vr;
if (p_vdev->role != VIRTIO_DEV_MASTER) {
return;
}
vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
sys_write8(status, vr->status_reg_addr);
sys_cache_data_range((void *) vr->status_reg_addr,
sizeof(status), K_CACHE_WB);
}
static uint32_t virtio_get_features(struct virtio_device *vdev)
{
return BIT(VIRTIO_RPMSG_F_NS);
}
static unsigned char virtio_get_status(struct virtio_device *p_vdev)
{
struct ipc_static_vrings *vr;
uint8_t ret;
vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
ret = VIRTIO_CONFIG_STATUS_DRIVER_OK;
if (p_vdev->role == VIRTIO_DEV_SLAVE) {
sys_cache_data_range((void *) vr->status_reg_addr,
sizeof(ret), K_CACHE_INVD);
ret = sys_read8(vr->status_reg_addr);
}
return ret;
}
const static struct virtio_dispatch dispatch = {
.get_status = virtio_get_status,
.get_features = virtio_get_features,
.set_status = virtio_set_status,
.set_features = virtio_set_features,
.notify = virtio_notify,
};
static int libmetal_setup(struct ipc_static_vrings *vr)
{
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
struct metal_device *device;
int err;
err = metal_init(&metal_params);
if (err != 0) {
return err;
}
err = metal_register_generic_device(&vr->shm_device);
if (err != 0) {
return err;
}
err = metal_device_open("generic", SHM_DEVICE_NAME, &device);
if (err != 0) {
return err;
}
vr->shm_io = metal_device_io_region(device, 0);
if (vr->shm_io == NULL) {
return err;
}
return 0;
}
static int vq_setup(struct ipc_static_vrings *vr, unsigned int role)
{
vr->vq[RPMSG_VQ_0] = virtqueue_allocate(vr->vring_size);
if (vr->vq[RPMSG_VQ_0] == NULL) {
return -ENOMEM;
}
vr->vq[RPMSG_VQ_1] = virtqueue_allocate(vr->vring_size);
if (vr->vq[RPMSG_VQ_1] == NULL) {
return -ENOMEM;
}
vr->rvrings[RPMSG_VQ_0].io = vr->shm_io;
vr->rvrings[RPMSG_VQ_0].info.vaddr = (void *) vr->tx_addr;
vr->rvrings[RPMSG_VQ_0].info.num_descs = vr->vring_size;
vr->rvrings[RPMSG_VQ_0].info.align = VRING_ALIGNMENT;
vr->rvrings[RPMSG_VQ_0].vq = vr->vq[RPMSG_VQ_0];
vr->rvrings[RPMSG_VQ_1].io = vr->shm_io;
vr->rvrings[RPMSG_VQ_1].info.vaddr = (void *) vr->rx_addr;
vr->rvrings[RPMSG_VQ_1].info.num_descs = vr->vring_size;
vr->rvrings[RPMSG_VQ_1].info.align = VRING_ALIGNMENT;
vr->rvrings[RPMSG_VQ_1].vq = vr->vq[RPMSG_VQ_1];
vr->vdev.role = role;
vr->vdev.vrings_num = VRING_COUNT;
vr->vdev.func = &dispatch;
vr->vdev.vrings_info = &vr->rvrings[0];
return 0;
}
int ipc_static_vrings_init(struct ipc_static_vrings *vr, unsigned int role)
{
int err = 0;
if (!vr) {
return -EINVAL;
}
vr->shm_device.name = SHM_DEVICE_NAME;
vr->shm_device.num_regions = 1;
vr->shm_physmap[0] = vr->shm_addr;
metal_io_init(vr->shm_device.regions, (void *) vr->shm_addr,
vr->shm_physmap, vr->shm_size, -1, 0, NULL);
err = libmetal_setup(vr);
if (err != 0) {
return err;
}
return vq_setup(vr, role);
}

View file

@ -1,3 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_sources(rpmsg_multi_instance.c)

View file

@ -1,75 +0,0 @@
# 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 = 0
rsource "Kconfig.ipm_name_instance"
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"
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.
module = RPMSG_MULTI_INSTANCE
module-str = RPMsg multi instance
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"
endif # RPMSG_MULTI_INSTANCE

View file

@ -1,415 +0,0 @@
/*
* 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>
#include "rpmsg_multi_instance.h"
LOG_MODULE_REGISTER(rpmsg_multi_instance, CONFIG_RPMSG_MULTI_INSTANCE_LOG_LEVEL);
K_MUTEX_DEFINE(shm_mutex);
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);
uint8_t 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 /* CONFIG_RPMSG_MULTI_INSTANCE_MASTER */
static void virtio_notify(struct virtqueue *vq)
{
struct rpmsg_mi_ctx *ctx = metal_container_of(vq->vq_dev, struct rpmsg_mi_ctx, vdev);
int status;
if (ctx) {
status = ipm_send(ctx->ipm_tx_handle, 0, ctx->ipm_tx_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 /* CONFIG_RPMSG_MULTI_INSTANCE_MASTER */
.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);
}
static void rpmsg_mi_configure_shm(struct rpmsg_mi_ctx *ctx, const struct rpmsg_mi_ctx_cfg *cfg)
{
size_t vring_size = VRING_SIZE_GET(cfg->shm->size);
uintptr_t shm_addr = SHMEM_INST_ADDR_AUTOALLOC_GET(cfg->shm->addr,
cfg->shm->size,
cfg->shm->instance);
size_t shm_size = SHMEM_INST_SIZE_AUTOALLOC_GET(cfg->shm->size);
uintptr_t shm_local_start_addr = shm_addr + VDEV_STATUS_SIZE;
size_t shm_local_size = shm_size - VDEV_STATUS_SIZE;
size_t rpmsg_reg_size = VRING_COUNT * VIRTQUEUE_SIZE_GET(vring_size);
size_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;
}
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;
}
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)
{
sys_snode_t *node;
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_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;
}
}
}
static bool rpmsg_mi_config_verify(const struct rpmsg_mi_ctx_cfg *cfg)
{
if (SHMEM_INST_SIZE_AUTOALLOC_GET(cfg->shm->size) * IPC_INSTANCE_COUNT > cfg->shm->size) {
LOG_ERR("Not enough memory");
return false;
}
return true;
}
static int libmetal_setup(struct rpmsg_mi_ctx *ctx)
{
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
struct metal_device *device;
int err;
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 -ENODEV;
}
return 0;
}
static int ipm_setup(struct rpmsg_mi_ctx *ctx, const struct rpmsg_mi_ctx_cfg *cfg)
{
int err;
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_tx_id = cfg->ipm_tx_id;
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;
}
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);
ipm_register_callback(ctx->ipm_rx_handle, ipm_callback, ctx);
err = ipm_set_enabled(ctx->ipm_rx_handle, 1);
if (err != 0) {
LOG_ERR("Could not enable IPM interrupts and callbacks for RX");
return err;
}
return 0;
}
static int vq_setup(struct rpmsg_mi_ctx *ctx, size_t vring_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];
return 0;
}
int rpmsg_mi_ctx_init(struct rpmsg_mi_ctx *ctx, const struct rpmsg_mi_ctx_cfg *cfg)
{
int err = 0;
if (!ctx || !cfg) {
return -EINVAL;
}
LOG_DBG("RPMsg multiple instance initialization");
if (!rpmsg_mi_config_verify(cfg)) {
return -EIO;
}
k_mutex_lock(&shm_mutex, K_FOREVER);
/* Configure shared memory */
rpmsg_mi_configure_shm(ctx, cfg);
/* Setup libmetal */
err = libmetal_setup(ctx);
if (err) {
LOG_ERR("Failed to setup libmetal");
goto out;
}
/* Setup IPM */
err = ipm_setup(ctx, cfg);
if (err) {
LOG_ERR("Failed to setup IPM");
goto out;
}
/* Setup VQs / VRINGs */
err = vq_setup(ctx, VRING_SIZE_GET(cfg->shm->size));
if (err) {
LOG_ERR("Failed to setup VQs / VRINGs");
goto out;
}
ctx->name = cfg->name;
sys_slist_init(&ctx->endpoints);
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);
goto out;
}
/* Get RPMsg device from RPMsg VirtIO device. */
ctx->rdev = rpmsg_virtio_get_rpmsg_device(&ctx->rvdev);
cfg->shm->instance++;
LOG_DBG("RPMsg multiple instance initialization done");
out:
k_mutex_unlock(&shm_mutex);
return err;
}
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);
}