1 /*
2  * Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 
10 #include <zephyr/ipc/ipc_service.h>
11 
12 #define STACKSIZE	(1024)
13 #define PRIORITY	K_PRIO_PREEMPT(2)
14 
15 K_THREAD_STACK_DEFINE(ipc0A_stack, STACKSIZE);
16 K_THREAD_STACK_DEFINE(ipc0B_stack, STACKSIZE);
17 K_THREAD_STACK_DEFINE(ipc1_stack, STACKSIZE);
18 
19 static volatile uint8_t ipc0A_received_data;
20 static volatile uint8_t ipc0B_received_data;
21 static volatile uint8_t ipc1_received_data;
22 
23 static K_SEM_DEFINE(ipc0A_bound_sem, 0, 1);
24 static K_SEM_DEFINE(ipc0B_bound_sem, 0, 1);
25 static K_SEM_DEFINE(ipc1_bound_sem, 0, 1);
26 
27 static K_SEM_DEFINE(ipc0A_data_sem, 0, 1);
28 static K_SEM_DEFINE(ipc0B_data_sem, 0, 1);
29 static K_SEM_DEFINE(ipc1_data_sem, 0, 1);
30 
31 /*
32  * ==> THREAD 0A (IPC instance 0 - endpoint A) <==
33  */
34 
ipc0A_ept_bound(void * priv)35 static void ipc0A_ept_bound(void *priv)
36 {
37 	k_sem_give(&ipc0A_bound_sem);
38 }
39 
ipc0A_ept_recv(const void * data,size_t len,void * priv)40 static void ipc0A_ept_recv(const void *data, size_t len, void *priv)
41 {
42 	ipc0A_received_data = *((uint8_t *) data);
43 
44 	k_sem_give(&ipc0A_data_sem);
45 }
46 
47 static struct ipc_ept_cfg ipc0A_ept_cfg = {
48 	.name = "ipc0A",
49 	.cb = {
50 		.bound    = ipc0A_ept_bound,
51 		.received = ipc0A_ept_recv,
52 	},
53 };
54 
ipc0A_entry(void * dummy0,void * dummy1,void * dummy2)55 static void ipc0A_entry(void *dummy0, void *dummy1, void *dummy2)
56 {
57 	ARG_UNUSED(dummy0);
58 	ARG_UNUSED(dummy1);
59 	ARG_UNUSED(dummy2);
60 
61 	const struct device *ipc0_instance;
62 	unsigned char message = 0;
63 	struct ipc_ept ipc0A_ept;
64 	int ret;
65 
66 	printk("IPC-service REMOTE [INST 0 - ENDP A] demo started\n");
67 
68 	ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
69 
70 	ret = ipc_service_open_instance(ipc0_instance);
71 	if (ret < 0 && ret != -EALREADY) {
72 		printk("ipc_service_open_instance() failure\n");
73 		return;
74 	}
75 
76 	ret = ipc_service_register_endpoint(ipc0_instance, &ipc0A_ept, &ipc0A_ept_cfg);
77 	if (ret < 0) {
78 		printf("ipc_service_register_endpoint() failure\n");
79 		return;
80 	}
81 
82 	k_sem_take(&ipc0A_bound_sem, K_FOREVER);
83 
84 	while (message < 99) {
85 		k_sem_take(&ipc0A_data_sem, K_FOREVER);
86 		message = ipc0A_received_data;
87 
88 		printk("REMOTE [0A]: %d\n", message);
89 
90 		message++;
91 
92 		ret = ipc_service_send(&ipc0A_ept, &message, sizeof(message));
93 		if (ret < 0) {
94 			printk("send_message(%d) failed with ret %d\n", message, ret);
95 			break;
96 		}
97 	}
98 
99 	printk("IPC-service REMOTE [INST 0 - ENDP A] demo ended.\n");
100 }
101 K_THREAD_DEFINE(ipc0A_thread_id, STACKSIZE, ipc0A_entry, NULL, NULL, NULL, PRIORITY, 0, 0);
102 
103 /*
104  * ==> THREAD 0B (IPC instance 0 - endpoint B) <==
105  */
106 
ipc0B_ept_bound(void * priv)107 static void ipc0B_ept_bound(void *priv)
108 {
109 	k_sem_give(&ipc0B_bound_sem);
110 }
111 
ipc0B_ept_recv(const void * data,size_t len,void * priv)112 static void ipc0B_ept_recv(const void *data, size_t len, void *priv)
113 {
114 	ipc0B_received_data = *((uint8_t *) data);
115 
116 	k_sem_give(&ipc0B_data_sem);
117 }
118 
119 static struct ipc_ept_cfg ipc0B_ept_cfg = {
120 	.name = "ipc0B",
121 	.cb = {
122 		.bound    = ipc0B_ept_bound,
123 		.received = ipc0B_ept_recv,
124 	},
125 };
126 
ipc0B_entry(void * dummy0,void * dummy1,void * dummy2)127 static void ipc0B_entry(void *dummy0, void *dummy1, void *dummy2)
128 {
129 	ARG_UNUSED(dummy0);
130 	ARG_UNUSED(dummy1);
131 	ARG_UNUSED(dummy2);
132 
133 	const struct device *ipc0_instance;
134 	unsigned char message = 0;
135 	struct ipc_ept ipc0B_ept;
136 	int ret;
137 
138 	printk("IPC-service REMOTE [INST 0 - ENDP B] demo started\n");
139 
140 	ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
141 
142 	ret = ipc_service_open_instance(ipc0_instance);
143 	if (ret < 0 && ret != -EALREADY) {
144 		printk("ipc_service_open_instance() failure\n");
145 		return;
146 	}
147 
148 	/*
149 	 * Wait 2 sec to give the opportunity to the PRIMARY core to register
150 	 * the endpoint first
151 	 */
152 
153 	k_sleep(K_MSEC(2000));
154 
155 	ret = ipc_service_register_endpoint(ipc0_instance, &ipc0B_ept, &ipc0B_ept_cfg);
156 	if (ret < 0) {
157 		printf("ipc_service_register_endpoint() failure\n");
158 		return;
159 	}
160 
161 	k_sem_take(&ipc0B_bound_sem, K_FOREVER);
162 
163 	while (message < 99) {
164 		k_sem_take(&ipc0B_data_sem, K_FOREVER);
165 		message = ipc0B_received_data;
166 
167 		printk("REMOTE [0B]: %d\n", message);
168 
169 		message++;
170 
171 		ret = ipc_service_send(&ipc0B_ept, &message, sizeof(message));
172 		if (ret < 0) {
173 			printk("send_message(%d) failed with ret %d\n", message, ret);
174 			break;
175 		}
176 	}
177 
178 	printk("IPC-service REMOTE [INST 0 - ENDP B] demo ended.\n");
179 }
180 K_THREAD_DEFINE(ipc0B_thread_id, STACKSIZE, ipc0B_entry, NULL, NULL, NULL, PRIORITY, 0, 0);
181 
182 /*
183  * ==> THREAD 1 (IPC instance 1) <==
184  *
185  * NOTE: This instance is using the NOCOPY copability of the backend.
186  */
187 
188 static struct ipc_ept ipc1_ept;
189 static void *recv_data;
190 
ipc1_ept_bound(void * priv)191 static void ipc1_ept_bound(void *priv)
192 {
193 	k_sem_give(&ipc1_bound_sem);
194 }
195 
ipc1_ept_recv(const void * data,size_t len,void * priv)196 static void ipc1_ept_recv(const void *data, size_t len, void *priv)
197 {
198 	int ret;
199 
200 	ret = ipc_service_hold_rx_buffer(&ipc1_ept, (void *) data);
201 	if (ret < 0) {
202 		printk("ipc_service_hold_rx_buffer failed with ret %d\n", ret);
203 	}
204 
205 	/*
206 	 * This will only support a synchronous request-answer mechanism. For
207 	 * asynchronous cases a chain list should be implemented.
208 	 */
209 	recv_data = (void *) data;
210 
211 	k_sem_give(&ipc1_data_sem);
212 }
213 
214 static struct ipc_ept_cfg ipc1_ept_cfg = {
215 	.name = "ipc1",
216 	.cb = {
217 		.bound    = ipc1_ept_bound,
218 		.received = ipc1_ept_recv,
219 	},
220 };
221 
ipc1_entry(void * dummy0,void * dummy1,void * dummy2)222 static void ipc1_entry(void *dummy0, void *dummy1, void *dummy2)
223 {
224 	ARG_UNUSED(dummy0);
225 	ARG_UNUSED(dummy1);
226 	ARG_UNUSED(dummy2);
227 
228 	const struct device *ipc1_instance;
229 	unsigned char message = 0;
230 	int ret;
231 
232 	printk("IPC-service REMOTE [INST 1] demo started\n");
233 
234 	ipc1_instance = DEVICE_DT_GET(DT_NODELABEL(ipc1));
235 
236 	ret = ipc_service_open_instance(ipc1_instance);
237 	if (ret < 0 && ret != -EALREADY) {
238 		printk("ipc_service_open_instance() failure\n");
239 		return;
240 	}
241 
242 	ret = ipc_service_register_endpoint(ipc1_instance, &ipc1_ept, &ipc1_ept_cfg);
243 	if (ret < 0) {
244 		printf("ipc_service_register_endpoint() failure\n");
245 		return;
246 	}
247 
248 	k_sem_take(&ipc1_bound_sem, K_FOREVER);
249 
250 	while (message < 50) {
251 		uint32_t len = sizeof(message);
252 		void *data;
253 
254 		k_sem_take(&ipc1_data_sem, K_FOREVER);
255 
256 		printk("REMOTE [1]: %d\n", *((unsigned char *) recv_data));
257 
258 		ret = ipc_service_get_tx_buffer(&ipc1_ept, &data, &len, K_FOREVER);
259 		if (ret < 0) {
260 			printk("ipc_service_get_tx_buffer failed with ret %d\n", ret);
261 			break;
262 		}
263 
264 		*((unsigned char *) data) = *((unsigned char *) recv_data) + 1;
265 
266 		ret = ipc_service_release_rx_buffer(&ipc1_ept, recv_data);
267 		if (ret < 0) {
268 			printk("ipc_service_release_rx_buffer failed with ret %d\n", ret);
269 			break;
270 		}
271 
272 		ret = ipc_service_send_nocopy(&ipc1_ept, data, sizeof(unsigned char));
273 		if (ret < 0) {
274 			printk("send_message(%d) failed with ret %d\n", message, ret);
275 			break;
276 		}
277 
278 		message++;
279 	}
280 
281 	printk("IPC-service REMOTE [INST 1] demo ended.\n");
282 }
283 K_THREAD_DEFINE(ipc1_thread_id, STACKSIZE, ipc1_entry, NULL, NULL, NULL, PRIORITY, 0, 0);
284