zephyr/subsys/ipc/rpmsg_service/rpmsg_service.c

163 lines
3.2 KiB
C
Raw Normal View History

/*
* Copyright (c) 2020-2021, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ipc/rpmsg_service.h>
#include "rpmsg_backend.h"
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/logging/log.h>
#include <openamp/open_amp.h>
#define LOG_MODULE_NAME rpmsg_service
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_RPMSG_SERVICE_LOG_LEVEL);
#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
init: remove the need for a dummy device pointer in SYS_INIT functions The init infrastructure, found in `init.h`, is currently used by: - `SYS_INIT`: to call functions before `main` - `DEVICE_*`: to initialize devices They are all sorted according to an initialization level + a priority. `SYS_INIT` calls are really orthogonal to devices, however, the required function signature requires a `const struct device *dev` as a first argument. The only reason for that is because the same init machinery is used by devices, so we have something like: ```c struct init_entry { int (*init)(const struct device *dev); /* only set by DEVICE_*, otherwise NULL */ const struct device *dev; } ``` As a result, we end up with such weird/ugly pattern: ```c static int my_init(const struct device *dev) { /* always NULL! add ARG_UNUSED to avoid compiler warning */ ARG_UNUSED(dev); ... } ``` This is really a result of poor internals isolation. This patch proposes a to make init entries more flexible so that they can accept sytem initialization calls like this: ```c static int my_init(void) { ... } ``` This is achieved using a union: ```c union init_function { /* for SYS_INIT, used when init_entry.dev == NULL */ int (*sys)(void); /* for DEVICE*, used when init_entry.dev != NULL */ int (*dev)(const struct device *dev); }; struct init_entry { /* stores init function (either for SYS_INIT or DEVICE*) union init_function init_fn; /* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows * to know which union entry to call. */ const struct device *dev; } ``` This solution **does not increase ROM usage**, and allows to offer clean public APIs for both SYS_INIT and DEVICE*. Note that however, init machinery keeps a coupling with devices. **NOTE**: This is a breaking change! All `SYS_INIT` functions will need to be converted to the new signature. See the script offered in the following commit. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> init: convert SYS_INIT functions to the new signature Conversion scripted using scripts/utils/migrate_sys_init.py. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> manifest: update projects for SYS_INIT changes Update modules with updated SYS_INIT calls: - hal_ti - lvgl - sof - TraceRecorderSource Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: devicetree: devices: adjust test Adjust test according to the recently introduced SYS_INIT infrastructure. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: kernel: threads: adjust SYS_INIT call Adjust to the new signature: int (*init_fn)(void); Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-10-19 15:33:44 +08:00
static int rpmsg_service_init(void)
{
int32_t err;
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);