1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #undef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 200809L
9 
10 #include <zephyr/ipc/icmsg_me.h>
11 #include <zephyr/sys/math_extras.h>
12 
13 #include <string.h>
14 
15 #define SEND_BUF_SIZE CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_SEND_BUF_SIZE
16 #define NUM_EP        CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NUM_EP
17 
18 #define EVENT_BOUND 0x01
19 
20 #define HEADER_SIZE (sizeof(icmsg_me_ept_id_t))
21 
icmsg_buffer_to_user_buffer(const void * icmsg_buffer)22 static void *icmsg_buffer_to_user_buffer(const void *icmsg_buffer)
23 {
24 	return (void *)(((char *)icmsg_buffer) + HEADER_SIZE);
25 }
26 
icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)27 static ssize_t icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len)
28 {
29 	if (icmsg_buffer_len < HEADER_SIZE) {
30 		return -EINVAL;
31 	}
32 
33 	return (ssize_t)(icmsg_buffer_len - HEADER_SIZE);
34 }
35 
user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)36 static ssize_t user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len)
37 {
38 	size_t ret;
39 
40 	if (size_add_overflow(user_buffer_len, HEADER_SIZE, &ret)) {
41 		return -EINVAL;
42 	}
43 
44 	return (ssize_t)ret;
45 }
46 
set_ept_id_in_send_buffer(uint8_t * send_buffer,icmsg_me_ept_id_t ept_id)47 static void set_ept_id_in_send_buffer(uint8_t *send_buffer,
48 				      icmsg_me_ept_id_t ept_id)
49 {
50 	send_buffer[0] = ept_id;
51 }
52 
icmsg_me_init(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data)53 int icmsg_me_init(const struct icmsg_config_t *conf,
54 		  struct icmsg_me_data_t *data)
55 {
56 	k_event_init(&data->event);
57 	k_mutex_init(&data->send_mutex);
58 
59 	return 0;
60 }
61 
icmsg_me_open(const struct icmsg_config_t * conf,struct icmsg_me_data_t * data,const struct ipc_service_cb * cb,void * ctx)62 int icmsg_me_open(const struct icmsg_config_t *conf,
63 		  struct icmsg_me_data_t *data,
64 		  const struct ipc_service_cb *cb,
65 		  void *ctx)
66 {
67 	data->ept_cfg.cb = *cb;
68 	data->ept_cfg.priv = ctx;
69 
70 	return icmsg_open(conf, &data->icmsg_data, &data->ept_cfg.cb,
71 			  data->ept_cfg.priv);
72 }
73 
icmsg_me_icmsg_bound(struct icmsg_me_data_t * data)74 void icmsg_me_icmsg_bound(struct icmsg_me_data_t *data)
75 {
76 	k_event_post(&data->event, EVENT_BOUND);
77 }
78 
icmsg_me_wait_for_icmsg_bind(struct icmsg_me_data_t * data)79 void icmsg_me_wait_for_icmsg_bind(struct icmsg_me_data_t *data)
80 {
81 	k_event_wait(&data->event, EVENT_BOUND, false, K_FOREVER);
82 }
83 
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)84 int icmsg_me_set_empty_ept_cfg_slot(struct icmsg_me_data_t *data,
85 				    const struct ipc_ept_cfg *ept_cfg,
86 				    icmsg_me_ept_id_t *id)
87 {
88 	int i;
89 
90 	for (i = 0; i < NUM_EP; i++) {
91 		if (data->epts[i] == NULL) {
92 			break;
93 		}
94 	}
95 
96 	if (i >= NUM_EP) {
97 		return -ENOMEM;
98 	}
99 
100 	data->epts[i] = ept_cfg;
101 	*id = i + 1;
102 	return 0;
103 }
104 
get_ept_cfg_index(icmsg_me_ept_id_t id)105 static int get_ept_cfg_index(icmsg_me_ept_id_t id)
106 {
107 	int i = id - 1;
108 
109 	if (i >= NUM_EP || i < 0) {
110 		return -ENOENT;
111 	}
112 
113 	return i;
114 }
115 
icmsg_me_set_ept_cfg(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const struct ipc_ept_cfg * ept_cfg)116 int icmsg_me_set_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
117 			 const struct ipc_ept_cfg *ept_cfg)
118 {
119 	int i = get_ept_cfg_index(id);
120 
121 	if (i < 0) {
122 		return i;
123 	}
124 
125 	data->epts[i] = ept_cfg;
126 	return 0;
127 }
128 
icmsg_me_get_ept_cfg(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const struct ipc_ept_cfg ** ept_cfg)129 int icmsg_me_get_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
130 			 const struct ipc_ept_cfg **ept_cfg)
131 {
132 	int i = get_ept_cfg_index(id);
133 
134 	if (i < 0) {
135 		return i;
136 	}
137 
138 	*ept_cfg = data->epts[i];
139 	return 0;
140 }
141 
icmsg_me_reset_ept_cfg(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id)142 void icmsg_me_reset_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id)
143 {
144 	int i = get_ept_cfg_index(id);
145 
146 	if (i < 0) {
147 		return;
148 	}
149 
150 	data->epts[i] = NULL;
151 }
152 
icmsg_me_received_data(struct icmsg_me_data_t * data,icmsg_me_ept_id_t id,const void * msg,size_t len)153 void icmsg_me_received_data(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
154 			    const void *msg, size_t len)
155 {
156 	int r;
157 	const struct ipc_ept_cfg *ept;
158 	ssize_t user_buffer_len;
159 
160 	r = icmsg_me_get_ept_cfg(data, id, &ept);
161 	if (r < 0) {
162 		return;
163 	}
164 
165 	if (ept == NULL) {
166 		return;
167 	}
168 
169 	user_buffer_len = icmsg_buffer_len_to_user_buffer_len(len);
170 	if (user_buffer_len < 0) {
171 		return;
172 	}
173 
174 	if (ept->cb.received) {
175 		ept->cb.received(icmsg_buffer_to_user_buffer(msg),
176 				 user_buffer_len, ept->priv);
177 	}
178 }
179 
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)180 int icmsg_me_send(const struct icmsg_config_t *conf,
181 		  struct icmsg_me_data_t *data, icmsg_me_ept_id_t id,
182 		  const void *msg, size_t len)
183 {
184 	int r;
185 	int sent_bytes = 0;
186 	ssize_t icmsg_buffer_len = user_buffer_len_to_icmsg_buffer_len(len);
187 
188 	if ((icmsg_buffer_len < 0) || (icmsg_buffer_len >= SEND_BUF_SIZE)) {
189 		return -EBADMSG;
190 	}
191 
192 	k_mutex_lock(&data->send_mutex, K_FOREVER);
193 
194 	/* TODO: Optimization: How to avoid this copying? */
195 	/* We could implement scatter list for icmsg_send, but it would require
196 	 * scatter list also for SPSC buffer implementation.
197 	 */
198 	set_ept_id_in_send_buffer(data->send_buffer, id);
199 	memcpy(icmsg_buffer_to_user_buffer(data->send_buffer), msg, len);
200 
201 	r = icmsg_send(conf, &data->icmsg_data, data->send_buffer,
202 		       icmsg_buffer_len);
203 	if (r > 0) {
204 		sent_bytes = icmsg_buffer_len_to_user_buffer_len(r);
205 	}
206 
207 	k_mutex_unlock(&data->send_mutex);
208 
209 	if (r < 0) {
210 		return r;
211 	}
212 
213 	__ASSERT_NO_MSG(r >= HEADER_SIZE);
214 	if (r < HEADER_SIZE) {
215 		return 0;
216 	}
217 
218 	return sent_bytes;
219 }
220