1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ipc/icmsg_me.h>
8 
9 #include <string.h>
10 
11 #define SEND_BUF_SIZE CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_SEND_BUF_SIZE
12 #define NUM_EP        CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NUM_EP
13 
14 #define EVENT_BOUND 0x01
15 
16 #define HEADER_SIZE (sizeof(icmsg_me_ept_id_t))
17 
icmsg_buffer_to_user_buffer(const void * icmsg_buffer)18 static void *icmsg_buffer_to_user_buffer(const void *icmsg_buffer)
19 {
20 	return (void *)(((char *)icmsg_buffer) + HEADER_SIZE);
21 }
22 
user_buffer_to_icmsg_buffer(const void * user_buffer)23 static void *user_buffer_to_icmsg_buffer(const void *user_buffer)
24 {
25 	return (void *)(((char *)user_buffer) - HEADER_SIZE);
26 }
27 
icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)28 static size_t icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)
29 {
30 	return icmsg_buffer_len - HEADER_SIZE;
31 }
32 
user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)33 static size_t user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)
34 {
35 	return user_buffer_len + HEADER_SIZE;
36 }
37 
set_ept_id_in_send_buffer(uint8_t * send_buffer,icmsg_me_ept_id_t ept_id)38 static void set_ept_id_in_send_buffer(uint8_t *send_buffer,
39 				      icmsg_me_ept_id_t ept_id)
40 {
41 	send_buffer[0] = ept_id;
42 }
43 
icmsg_me_init(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data)44 int icmsg_me_init(const struct icmsg_config_t *conf,
45 		  struct icmsg_me_data_t *data)
46 {
47 	k_event_init(&data->event);
48 	k_mutex_init(&data->send_mutex);
49 
50 	return 0;
51 }
52 
icmsg_me_open(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,const struct ipc_service_cb * cb,void * ctx)53 int icmsg_me_open(const struct icmsg_config_t *conf,
54 		  struct icmsg_me_data_t *data,
55 		  const struct ipc_service_cb *cb,
56 		  void *ctx)
57 {
58 	data->ept_cfg.cb = *cb;
59 	data->ept_cfg.priv = ctx;
60 
61 	return icmsg_open(conf, &data->icmsg_data, &data->ept_cfg.cb,
62 			  data->ept_cfg.priv);
63 }
64 
icmsg_me_icmsg_bound(struct icmsg_me_data_t * data)65 void icmsg_me_icmsg_bound(struct icmsg_me_data_t *data)
66 {
67 	k_event_post(&data->event, EVENT_BOUND);
68 }
69 
icmsg_me_wait_for_icmsg_bind(struct icmsg_me_data_t * data)70 void icmsg_me_wait_for_icmsg_bind(struct icmsg_me_data_t *data)
71 {
72 	k_event_wait(&data->event, EVENT_BOUND, false, K_FOREVER);
73 }
74 
icmsg_me_set_empty_ept_cfg_slot(struct icmsg_me_data_t * data,const struct ipc_ept_cfg * ept_cfg,icmsg_me_ept_id_t * id)75 int icmsg_me_set_empty_ept_cfg_slot(struct icmsg_me_data_t *data,
76 				    const struct ipc_ept_cfg *ept_cfg,
77 				    icmsg_me_ept_id_t *id)
78 {
79 	int i;
80 
81 	for (i = 0; i < NUM_EP; i++) {
82 		if (data->epts[i] == NULL) {
83 			break;
84 		}
85 	}
86 
87 	if (i >= NUM_EP) {
88 		return -ENOMEM;
89 	}
90 
91 	data->epts[i] = ept_cfg;
92 	*id = i + 1;
93 	return 0;
94 }
95 
get_ept_cfg_index(icmsg_me_ept_id_t id)96 static int get_ept_cfg_index(icmsg_me_ept_id_t id)
97 {
98 	int i = id - 1;
99 
100 	if (i >= NUM_EP || i < 0) {
101 		return -ENOENT;
102 	}
103 
104 	return i;
105 }
106 
icmsg_me_set_ept_cfg(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const struct ipc_ept_cfg * ept_cfg)107 int icmsg_me_set_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
108 			 const struct ipc_ept_cfg *ept_cfg)
109 {
110 	int i = get_ept_cfg_index(id);
111 
112 	if (i < 0) {
113 		return i;
114 	}
115 
116 	data->epts[i] = ept_cfg;
117 	return 0;
118 }
119 
icmsg_me_get_ept_cfg(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const struct ipc_ept_cfg ** ept_cfg)120 int icmsg_me_get_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
121 			 const struct ipc_ept_cfg **ept_cfg)
122 {
123 	int i = get_ept_cfg_index(id);
124 
125 	if (i < 0) {
126 		return i;
127 	}
128 
129 	*ept_cfg = data->epts[i];
130 	return 0;
131 }
132 
icmsg_me_reset_ept_cfg(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id)133 void icmsg_me_reset_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id)
134 {
135 	int i = get_ept_cfg_index(id);
136 
137 	if (i < 0) {
138 		return;
139 	}
140 
141 	data->epts[i] = NULL;
142 }
143 
icmsg_me_received_data(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const void * msg,size_t len)144 void icmsg_me_received_data(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
145 			    const void *msg, size_t len)
146 {
147 	int r;
148 	const struct ipc_ept_cfg *ept;
149 
150 	r = icmsg_me_get_ept_cfg(data, id, &ept);
151 	if (r < 0) {
152 		return;
153 	}
154 
155 	if (ept == NULL) {
156 		return;
157 	}
158 
159 	if (ept->cb.received) {
160 		ept->cb.received(icmsg_buffer_to_user_buffer(msg),
161 				 icmsg_buffer_len_to_user_buffer_len(len),
162 				 ept->priv);
163 	}
164 }
165 
icmsg_me_send(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const void * msg,size_t len)166 int icmsg_me_send(const struct icmsg_config_t *conf,
167 		  struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
168 		  const void *msg, size_t len)
169 {
170 	int r;
171 	int sent_bytes = 0;
172 
173 	if (user_buffer_len_to_icmsg_buffer_len(len) >= SEND_BUF_SIZE) {
174 		return -EBADMSG;
175 	}
176 
177 	k_mutex_lock(&data->send_mutex, K_FOREVER);
178 
179 	/* TODO: Optimization: How to avoid this copying? */
180 	/* We could implement scatter list for icmsg_send, but it would require
181 	 * scatter list also for SPSC buffer implementation.
182 	 */
183 	set_ept_id_in_send_buffer(data->send_buffer, id);
184 	memcpy(icmsg_buffer_to_user_buffer(data->send_buffer), msg, len);
185 
186 	r = icmsg_send(conf, &data->icmsg_data, data->send_buffer,
187 		       user_buffer_len_to_icmsg_buffer_len(len));
188 	if (r > 0) {
189 		sent_bytes = icmsg_buffer_len_to_user_buffer_len(r);
190 	}
191 
192 	k_mutex_unlock(&data->send_mutex);
193 
194 	if (r < 0) {
195 		return r;
196 	}
197 
198 	__ASSERT_NO_MSG(r >= HEADER_SIZE);
199 	if (r < HEADER_SIZE) {
200 		return 0;
201 	}
202 
203 	return sent_bytes;
204 }
205 
get_buffer_length_to_pass(size_t icmsg_buffer_len)206 static size_t get_buffer_length_to_pass(size_t icmsg_buffer_len)
207 {
208 	if (icmsg_buffer_len >= HEADER_SIZE) {
209 		return icmsg_buffer_len_to_user_buffer_len(icmsg_buffer_len);
210 	} else {
211 		return 0;
212 	}
213 }
214 
icmsg_me_get_tx_buffer(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,void ** buffer,uint32_t * user_len,k_timeout_t wait)215 int icmsg_me_get_tx_buffer(const struct icmsg_config_t *conf,
216 			   struct icmsg_me_data_t *data,
217 			   void **buffer, uint32_t *user_len, k_timeout_t wait)
218 {
219 	void *icmsg_buffer;
220 	int r;
221 	size_t icmsg_len;
222 
223 	if (!K_TIMEOUT_EQ(wait, K_NO_WAIT)) {
224 		return -ENOTSUP;
225 	}
226 
227 	if (*user_len) {
228 		icmsg_len = user_buffer_len_to_icmsg_buffer_len(*user_len);
229 	} else {
230 		icmsg_len = 0;
231 	}
232 
233 	r = icmsg_get_tx_buffer(conf, &data->icmsg_data,
234 				&icmsg_buffer, &icmsg_len);
235 	if (r == -ENOMEM) {
236 		*user_len = get_buffer_length_to_pass(icmsg_len);
237 		return -ENOMEM;
238 	}
239 
240 	if (r < 0) {
241 		return r;
242 	}
243 
244 	/* If requested max buffer length (*len == 0) allocated buffer might be
245 	 * shorter than HEADER_SIZE. In such circumstances drop the buffer
246 	 * and return error.
247 	 */
248 	*user_len = get_buffer_length_to_pass(icmsg_len);
249 
250 	if (!(*user_len)) {
251 		r = icmsg_drop_tx_buffer(conf, &data->icmsg_data, icmsg_buffer);
252 		__ASSERT_NO_MSG(!r);
253 		return -ENOBUFS;
254 	}
255 
256 	*buffer = icmsg_buffer_to_user_buffer(icmsg_buffer);
257 	return 0;
258 
259 }
260 
icmsg_me_drop_tx_buffer(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,const void * buffer)261 int icmsg_me_drop_tx_buffer(const struct icmsg_config_t *conf,
262 			    struct icmsg_me_data_t *data,
263 			    const void *buffer)
264 {
265 	const void *buffer_to_drop = user_buffer_to_icmsg_buffer(buffer);
266 
267 	return icmsg_drop_tx_buffer(conf, &data->icmsg_data, buffer_to_drop);
268 }
269 
icmsg_me_send_nocopy(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const void * msg,size_t len)270 int icmsg_me_send_nocopy(const struct icmsg_config_t *conf,
271 			 struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
272 			 const void *msg, size_t len)
273 {
274 	void *buffer_to_send;
275 	size_t len_to_send;
276 	int r;
277 	int sent_bytes;
278 
279 	buffer_to_send = user_buffer_to_icmsg_buffer(msg);
280 	len_to_send = user_buffer_len_to_icmsg_buffer_len(len);
281 
282 	set_ept_id_in_send_buffer(buffer_to_send, id);
283 
284 	r = icmsg_send_nocopy(conf, &data->icmsg_data,
285 			      buffer_to_send, len_to_send);
286 
287 	if (r < 0) {
288 		return r;
289 	}
290 
291 	__ASSERT_NO_MSG(r >= HEADER_SIZE);
292 	if (r < HEADER_SIZE) {
293 		return 0;
294 	}
295 
296 	sent_bytes = icmsg_buffer_len_to_user_buffer_len(r);
297 
298 	return sent_bytes;
299 }
300 
301 #ifdef CONFIG_IPC_SERVICE_ICMSG_ME_NOCOPY_RX
icmsg_me_hold_rx_buffer(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,void * buffer)302 int icmsg_me_hold_rx_buffer(const struct icmsg_config_t *conf,
303 			    struct icmsg_me_data_t *data, void *buffer)
304 {
305 	void *icmsg_buffer = user_buffer_to_icmsg_buffer(buffer);
306 
307 	return icmsg_hold_rx_buffer(conf, &data->icmsg_data, icmsg_buffer);
308 }
309 
icmsg_me_release_rx_buffer(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,void * buffer)310 int icmsg_me_release_rx_buffer(const struct icmsg_config_t *conf,
311 			       struct icmsg_me_data_t *data, void *buffer)
312 {
313 	void *icmsg_buffer = user_buffer_to_icmsg_buffer(buffer);
314 
315 	return icmsg_release_rx_buffer(conf, &data->icmsg_data, icmsg_buffer);
316 }
317 #endif /* CONFIG_IPC_SERVICE_ICMSG_ME_NOCOPY_RX */
318