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