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