zephyr/subsys/ipc/ipc_service/ipc_service.c

321 lines
6.3 KiB
C
Raw Permalink Normal View History

/*
* Copyright (c) 2021 Nordic Semiconductor ASA
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ipc/ipc_service.h>
#include <zephyr/ipc/ipc_service_backend.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
LOG_MODULE_REGISTER(ipc_service, CONFIG_IPC_SERVICE_LOG_LEVEL);
ipc_service: Add open_instance function As know, an instance is the representation of a physical communication channel between two domains / CPUs. This communication channel must be usually known and initialized (that is "opened") by both parties in the communication before a proper communication can be instaurated using endpoints. Depending on the backend and on the library / protocol used by the backend, this "opening" can go through some handshaking or synchronization procedure run by the parties that sometimes can be blocking or time-consuming. For example in the simplest case of a backend using OpenAMP, the remote side of the communication is waiting for the local part to be up and running by loop-waiting on some flag set in the shared memory by the local party. This is a blocking process so a particular attention must be paid to where this is going to be placed in the backend code. Currently it is only possible to have this synchronization procedure in two points: (1) the init function of the instance, (2) during ipc_service_register_endpoint(). It should be highly discouraged to put any blocking routine in the init code, so (1) must be excluded. It is also frowned upon using the endpoint registration function (2) because the synchronization is something concerning the instance, not the single endpoints. This patch is adding a new optional ipc_service_open_instance() function that can be used to host the handshaking or synchronization code between the two parties of the instance. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
2021-11-11 16:34:51 +01:00
int ipc_service_open_instance(const struct device *instance)
{
const struct ipc_service_backend *backend;
if (!instance) {
LOG_ERR("Invalid instance");
return -EINVAL;
}
backend = (const struct ipc_service_backend *) instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
if (!backend->open_instance) {
/* maybe not needed on backend */
return 0;
}
return backend->open_instance(instance);
}
int ipc_service_close_instance(const struct device *instance)
{
const struct ipc_service_backend *backend;
if (!instance) {
LOG_ERR("Invalid instance");
return -EINVAL;
}
backend = (const struct ipc_service_backend *) instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
if (!backend->close_instance) {
/* maybe not needed on backend */
return 0;
}
return backend->close_instance(instance);
}
int ipc_service_register_endpoint(const struct device *instance,
struct ipc_ept *ept,
const struct ipc_ept_cfg *cfg)
{
const struct ipc_service_backend *backend;
if (!instance || !ept || !cfg) {
LOG_ERR("Invalid instance, endpoint or configuration");
return -EINVAL;
}
backend = (const struct ipc_service_backend *) instance->api;
if (!backend || !backend->register_endpoint) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
LOG_DBG("Register endpoint %s", cfg->name ? cfg->name : "");
ept->instance = instance;
return backend->register_endpoint(instance, &ept->token, cfg);
}
int ipc_service_deregister_endpoint(struct ipc_ept *ept)
{
const struct ipc_service_backend *backend;
int err;
if (!ept) {
LOG_ERR("Invalid endpoint");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend || !backend->deregister_endpoint) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
err = backend->deregister_endpoint(ept->instance, ept->token);
if (err != 0) {
return err;
}
ept->instance = 0;
return 0;
}
int ipc_service_send(struct ipc_ept *ept, const void *data, size_t len)
{
const struct ipc_service_backend *backend;
if (!ept) {
LOG_ERR("Invalid endpoint");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend || !backend->send) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
return backend->send(ept->instance, ept->token, data, len);
}
int ipc_service_get_tx_buffer_size(struct ipc_ept *ept)
{
const struct ipc_service_backend *backend;
if (!ept) {
LOG_ERR("Invalid endpoint");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
if (!backend->get_tx_buffer_size) {
LOG_ERR("No-copy feature not available");
return -EIO;
}
return backend->get_tx_buffer_size(ept->instance, ept->token);
}
int ipc_service_get_tx_buffer(struct ipc_ept *ept, void **data, uint32_t *len, k_timeout_t wait)
{
const struct ipc_service_backend *backend;
if (!ept || !data || !len) {
LOG_ERR("Invalid endpoint, data or len pointer");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
if (!backend->send_nocopy || !backend->get_tx_buffer) {
LOG_ERR("No-copy feature not available");
return -EIO;
}
return backend->get_tx_buffer(ept->instance, ept->token, data, len, wait);
}
int ipc_service_drop_tx_buffer(struct ipc_ept *ept, const void *data)
{
const struct ipc_service_backend *backend;
if (!ept || !data) {
LOG_ERR("Invalid endpoint or data pointer");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
if (!backend->drop_tx_buffer) {
LOG_ERR("No-copy feature not available");
return -EIO;
}
return backend->drop_tx_buffer(ept->instance, ept->token, data);
}
int ipc_service_send_nocopy(struct ipc_ept *ept, const void *data, size_t len)
{
const struct ipc_service_backend *backend;
if (!ept) {
LOG_ERR("Invalid endpoint");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
if (!backend->get_tx_buffer || !backend->send_nocopy) {
LOG_ERR("No-copy feature not available");
return -EIO;
}
return backend->send_nocopy(ept->instance, ept->token, data, len);
}
int ipc_service_hold_rx_buffer(struct ipc_ept *ept, void *data)
{
const struct ipc_service_backend *backend;
if (!ept) {
LOG_ERR("Invalid endpoint");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
/* We also need the release function */
if (!backend->release_rx_buffer || !backend->hold_rx_buffer) {
LOG_ERR("No-copy feature not available");
return -EIO;
}
return backend->hold_rx_buffer(ept->instance, ept->token, data);
}
int ipc_service_release_rx_buffer(struct ipc_ept *ept, void *data)
{
const struct ipc_service_backend *backend;
if (!ept) {
LOG_ERR("Invalid endpoint");
return -EINVAL;
}
if (!ept->instance) {
LOG_ERR("Endpoint not registered\n");
return -ENOENT;
}
backend = ept->instance->api;
if (!backend) {
LOG_ERR("Invalid backend configuration");
return -EIO;
}
/* We also need the hold function */
if (!backend->hold_rx_buffer || !backend->release_rx_buffer) {
LOG_ERR("No-copy feature not available");
return -EIO;
}
return backend->release_rx_buffer(ept->instance, ept->token, data);
}