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