blob: da6912c83880863f974a18569df11d69fd301866 [file] [log] [blame]
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/ipc/icmsg.h>
#include <zephyr/ipc/icmsg_me.h>
#include <zephyr/ipc/ipc_service_backend.h>
#define DT_DRV_COMPAT zephyr_ipc_icmsg_me_initiator
#define NUM_EP CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NUM_EP
#define EP_NAME_LEN CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_EP_NAME_LEN
struct backend_data_t {
struct icmsg_me_data_t icmsg_me_data;
struct k_mutex epts_mutex;
icmsg_me_ept_id_t ids[NUM_EP];
};
static void bound(void *priv)
{
const struct device *instance = priv;
struct backend_data_t *dev_data = instance->data;
icmsg_me_icmsg_bound(&dev_data->icmsg_me_data);
}
static void received(const void *data, size_t len, void *priv)
{
const struct device *instance = priv;
struct backend_data_t *dev_data = instance->data;
const icmsg_me_ept_id_t *id = data;
__ASSERT_NO_MSG(len > 0);
if (*id == 0) {
const struct ipc_ept_cfg *ept;
int r;
__ASSERT_NO_MSG(len > 1);
id++;
icmsg_me_ept_id_t ept_id = *id;
r = icmsg_me_get_ept_cfg(&dev_data->icmsg_me_data, ept_id,
&ept);
if (r < 0) {
return;
}
if (ept && ept->cb.bound) {
ept->cb.bound(ept->priv);
}
} else {
icmsg_me_received_data(&dev_data->icmsg_me_data, *id,
data, len);
}
}
static const struct ipc_service_cb cb = {
.bound = bound,
.received = received,
.error = NULL,
};
static int open(const struct device *instance)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
return icmsg_me_open(conf, &dev_data->icmsg_me_data, &cb,
(void *)instance);
}
static int store_id_for_token(struct backend_data_t *data, icmsg_me_ept_id_t id,
void **token)
{
int i;
for (i = 0; i < NUM_EP; i++) {
if (data->ids[i] == 0) {
break;
}
}
__ASSERT_NO_MSG(i < NUM_EP);
if (i >= NUM_EP) {
return -ENOENT;
}
data->ids[i] = id;
*token = &data->ids[i];
return 0;
}
static int register_ept(const struct device *instance, void **token,
const struct ipc_ept_cfg *cfg)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *data = instance->data;
int r = 0;
icmsg_me_ept_id_t id;
size_t name_len = strlen(cfg->name);
if (name_len > EP_NAME_LEN) {
return -EINVAL;
}
k_mutex_lock(&data->epts_mutex, K_FOREVER);
r = icmsg_me_set_empty_ept_cfg_slot(&data->icmsg_me_data, cfg, &id);
if (r < 0) {
goto exit;
}
__ASSERT_NO_MSG(id > 0);
if (id <= 0) {
r = -ENOENT;
goto reset_slot;
}
r = store_id_for_token(data, id, token);
if (r < 0) {
goto reset_slot;
}
uint8_t ep_disc_req[EP_NAME_LEN + 2 * sizeof(icmsg_me_ept_id_t)] = {
0, /* EP discovery endpoint id */
id, /* Bound endpoint id */
};
memcpy(&ep_disc_req[2], cfg->name, name_len);
icmsg_me_wait_for_icmsg_bind(&data->icmsg_me_data);
r = icmsg_send(conf, &data->icmsg_me_data.icmsg_data, ep_disc_req,
2 * sizeof(icmsg_me_ept_id_t) + name_len);
if (r < 0) {
goto reset_slot;
} else {
r = 0;
}
reset_slot:
if (r < 0) {
icmsg_me_reset_ept_cfg(&data->icmsg_me_data, id);
}
exit:
k_mutex_unlock(&data->epts_mutex);
return r;
}
static int send(const struct device *instance, void *token,
const void *msg, size_t len)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
icmsg_me_ept_id_t *id = token;
return icmsg_me_send(conf, &dev_data->icmsg_me_data, *id, msg, len);
}
static int get_tx_buffer(const struct device *instance, void *token,
void **data, uint32_t *user_len, k_timeout_t wait)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
return icmsg_me_get_tx_buffer(conf, &dev_data->icmsg_me_data, data,
user_len, wait);
}
static int drop_tx_buffer(const struct device *instance, void *token,
const void *data)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
return icmsg_me_drop_tx_buffer(conf, &dev_data->icmsg_me_data, data);
}
static int send_nocopy(const struct device *instance, void *token,
const void *data, size_t len)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
icmsg_me_ept_id_t *id = token;
return icmsg_me_send_nocopy(conf, &dev_data->icmsg_me_data, *id,
data, len);
}
#ifdef CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
int hold_rx_buffer(const struct device *instance, void *token, void *data)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
return icmsg_me_hold_rx_buffer(conf, &dev_data->icmsg_me_data, data);
}
int release_rx_buffer(const struct device *instance, void *token, void *data)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
return icmsg_me_release_rx_buffer(conf, &dev_data->icmsg_me_data, data);
}
#endif /* CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX */
const static struct ipc_service_backend backend_ops = {
.open_instance = open,
.register_endpoint = register_ept,
.send = send,
.get_tx_buffer = get_tx_buffer,
.drop_tx_buffer = drop_tx_buffer,
.send_nocopy = send_nocopy,
#ifdef CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NOCOPY_RX
.hold_rx_buffer = hold_rx_buffer,
.release_rx_buffer = release_rx_buffer,
#endif
};
static int backend_init(const struct device *instance)
{
const struct icmsg_config_t *conf = instance->config;
struct backend_data_t *dev_data = instance->data;
k_mutex_init(&dev_data->epts_mutex);
return icmsg_me_init(conf, &dev_data->icmsg_me_data);
}
#define DEFINE_BACKEND_DEVICE(i) \
static const struct icmsg_config_t backend_config_##i = \
{ \
.tx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
.tx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
.rx_shm_size = DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
.rx_shm_addr = DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
.mbox_tx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), tx), \
.mbox_rx = MBOX_DT_CHANNEL_GET(DT_DRV_INST(i), rx), \
}; \
static struct backend_data_t backend_data_##i; \
\
DEVICE_DT_INST_DEFINE(i, \
&backend_init, \
NULL, \
&backend_data_##i, \
&backend_config_##i, \
POST_KERNEL, \
CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY, \
&backend_ops);
DT_INST_FOREACH_STATUS_OKAY(DEFINE_BACKEND_DEVICE)