1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 
9 #include <zephyr/device.h>
10 #include <zephyr/ipc/icmsg.h>
11 #include <zephyr/ipc/icmsg_me.h>
12 #include <zephyr/ipc/ipc_service_backend.h>
13 
14 #define DT_DRV_COMPAT	zephyr_ipc_icmsg_me_initiator
15 
16 #define NUM_EP        CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NUM_EP
17 #define EP_NAME_LEN   CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_EP_NAME_LEN
18 
19 struct backend_data_t {
20 	struct icmsg_me_data_t icmsg_me_data;
21 
22 	struct k_mutex epts_mutex;
23 	icmsg_me_ept_id_t ids[NUM_EP];
24 };
25 
bound(void * priv)26 static void bound(void *priv)
27 {
28 	const struct device *instance = priv;
29 	struct backend_data_t *dev_data = instance->data;
30 
31 	icmsg_me_icmsg_bound(&dev_data->icmsg_me_data);
32 }
33 
received(const void * data,size_t len,void * priv)34 static void received(const void *data, size_t len, void *priv)
35 {
36 	const struct device *instance = priv;
37 	struct backend_data_t *dev_data = instance->data;
38 
39 	const icmsg_me_ept_id_t *id = data;
40 
41 	__ASSERT_NO_MSG(len > 0);
42 
43 	if (*id == 0) {
44 		const struct ipc_ept_cfg *ept;
45 		int r;
46 
47 		__ASSERT_NO_MSG(len > 1);
48 
49 		id++;
50 		icmsg_me_ept_id_t ept_id = *id;
51 
52 		r = icmsg_me_get_ept_cfg(&dev_data->icmsg_me_data, ept_id,
53 					 &ept);
54 		if (r < 0) {
55 			return;
56 		}
57 
58 		if (ept && ept->cb.bound) {
59 			ept->cb.bound(ept->priv);
60 		}
61 	} else {
62 		icmsg_me_received_data(&dev_data->icmsg_me_data, *id,
63 				       data, len);
64 	}
65 }
66 
67 static const struct ipc_service_cb cb = {
68 	.bound = bound,
69 	.received = received,
70 	.error = NULL,
71 };
72 
open(const struct device * instance)73 static int open(const struct device *instance)
74 {
75 	const struct icmsg_config_t *conf = instance->config;
76 	struct backend_data_t *dev_data = instance->data;
77 
78 	return icmsg_me_open(conf, &dev_data->icmsg_me_data, &cb,
79 			     (void *)instance);
80 }
81 
store_id_for_token(struct backend_data_t * data,icmsg_me_ept_id_t id,void ** token)82 static int store_id_for_token(struct backend_data_t *data, icmsg_me_ept_id_t id,
83 			      void **token)
84 {
85 	int i;
86 
87 	for (i = 0; i < NUM_EP; i++) {
88 		if (data->ids[i] == 0) {
89 			break;
90 		}
91 	}
92 	__ASSERT_NO_MSG(i < NUM_EP);
93 	if (i >= NUM_EP) {
94 		return -ENOENT;
95 	}
96 
97 	data->ids[i] = id;
98 	*token = &data->ids[i];
99 
100 	return 0;
101 }
102 
register_ept(const struct device * instance,void ** token,const struct ipc_ept_cfg * cfg)103 static int register_ept(const struct device *instance, void **token,
104 			const struct ipc_ept_cfg *cfg)
105 {
106 	const struct icmsg_config_t *conf = instance->config;
107 	struct backend_data_t *data = instance->data;
108 	int r = 0;
109 	icmsg_me_ept_id_t id;
110 	size_t name_len = strlen(cfg->name);
111 
112 	if (name_len > EP_NAME_LEN) {
113 		return -EINVAL;
114 	}
115 
116 	k_mutex_lock(&data->epts_mutex, K_FOREVER);
117 
118 	r = icmsg_me_set_empty_ept_cfg_slot(&data->icmsg_me_data, cfg, &id);
119 	if (r < 0) {
120 		goto exit;
121 	}
122 	__ASSERT_NO_MSG(id > 0);
123 	if (id <= 0) {
124 		r = -ENOENT;
125 		goto reset_slot;
126 	}
127 
128 	r = store_id_for_token(data, id, token);
129 	if (r < 0) {
130 		goto reset_slot;
131 	}
132 
133 	uint8_t ep_disc_req[EP_NAME_LEN + 2 * sizeof(icmsg_me_ept_id_t)] = {
134 		0,  /* EP discovery endpoint id */
135 		id, /* Bound endpoint id */
136 	};
137 	memcpy(&ep_disc_req[2], cfg->name, name_len);
138 
139 	icmsg_me_wait_for_icmsg_bind(&data->icmsg_me_data);
140 
141 	r = icmsg_send(conf, &data->icmsg_me_data.icmsg_data, ep_disc_req,
142 		       2 * sizeof(icmsg_me_ept_id_t) + name_len);
143 	if (r < 0) {
144 		goto reset_slot;
145 	} else {
146 		r = 0;
147 	}
148 
149 reset_slot:
150 	if (r < 0) {
151 		icmsg_me_reset_ept_cfg(&data->icmsg_me_data, id);
152 	}
153 
154 exit:
155 	k_mutex_unlock(&data->epts_mutex);
156 	return r;
157 }
158 
send(const struct device * instance,void * token,const void * msg,size_t len)159 static int send(const struct device *instance, void *token,
160 		const void *msg, size_t len)
161 {
162 	const struct icmsg_config_t *conf = instance->config;
163 	struct backend_data_t *dev_data = instance->data;
164 	icmsg_me_ept_id_t *id = token;
165 
166 	return icmsg_me_send(conf, &dev_data->icmsg_me_data, *id, msg, len);
167 }
168 
169 const static struct ipc_service_backend backend_ops = {
170 	.open_instance = open,
171 	.register_endpoint = register_ept,
172 	.send = send,
173 };
174 
backend_init(const struct device * instance)175 static int backend_init(const struct device *instance)
176 {
177 	const struct icmsg_config_t *conf = instance->config;
178 	struct backend_data_t *dev_data = instance->data;
179 
180 	k_mutex_init(&dev_data->epts_mutex);
181 
182 	return icmsg_me_init(conf, &dev_data->icmsg_me_data);
183 }
184 
185 #define DEFINE_BACKEND_DEVICE(i)						\
186 	static const struct icmsg_config_t backend_config_##i = {		\
187 		.mbox_tx = MBOX_DT_SPEC_INST_GET(i, tx),			\
188 		.mbox_rx = MBOX_DT_SPEC_INST_GET(i, rx),			\
189 	};									\
190 										\
191 	PBUF_DEFINE(tx_pb_##i,							\
192 			DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)),		\
193 			DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)),		\
194 			DT_INST_PROP_OR(i, dcache_alignment, 0));		\
195 	PBUF_DEFINE(rx_pb_##i,							\
196 			DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)),		\
197 			DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)),		\
198 			DT_INST_PROP_OR(i, dcache_alignment, 0));		\
199 										\
200 	static struct backend_data_t backend_data_##i = {			\
201 		.icmsg_me_data = {						\
202 			.icmsg_data = {						\
203 				.tx_pb = &tx_pb_##i,				\
204 				.rx_pb = &rx_pb_##i,				\
205 			}							\
206 		}								\
207 	};									\
208 										\
209 	DEVICE_DT_INST_DEFINE(i,						\
210 			 &backend_init,						\
211 			 NULL,							\
212 			 &backend_data_##i,					\
213 			 &backend_config_##i,					\
214 			 POST_KERNEL,						\
215 			 CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY,		\
216 			 &backend_ops);
217 
218 DT_INST_FOREACH_STATUS_OKAY(DEFINE_BACKEND_DEVICE)
219