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	(4096)
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 HOST [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 	/*
77 	 * Wait 1 sec to give the opportunity to the SECONDARY core to register
78 	 * the endpoint first
79 	 */
80 
81 	k_sleep(K_MSEC(1000));
82 
83 	ret = ipc_service_register_endpoint(ipc0_instance, &ipc0A_ept, &ipc0A_ept_cfg);
84 	if (ret < 0) {
85 		printf("ipc_service_register_endpoint() failure\n");
86 		return;
87 	}
88 
89 	k_sem_take(&ipc0A_bound_sem, K_FOREVER);
90 
91 	while (message < 100) {
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 		k_sem_take(&ipc0A_data_sem, K_FOREVER);
99 		message = ipc0A_received_data;
100 
101 		printk("HOST [0A]: %d\n", message);
102 		message++;
103 	}
104 
105 	printk("IPC-service HOST [INST 0 - ENDP A] demo ended.\n");
106 }
107 K_THREAD_DEFINE(ipc0A_thread_id, STACKSIZE, ipc0A_entry, NULL, NULL, NULL, PRIORITY, 0, 0);
108 
109 /*
110  * ==> THREAD 0B (IPC instance 0 - endpoint B) <==
111  */
112 
ipc0B_ept_bound(void * priv)113 static void ipc0B_ept_bound(void *priv)
114 {
115 	k_sem_give(&ipc0B_bound_sem);
116 }
117 
ipc0B_ept_recv(const void * data,size_t len,void * priv)118 static void ipc0B_ept_recv(const void *data, size_t len, void *priv)
119 {
120 	ipc0B_received_data = *((uint8_t *) data);
121 
122 	k_sem_give(&ipc0B_data_sem);
123 }
124 
125 static struct ipc_ept_cfg ipc0B_ept_cfg = {
126 	.name = "ipc0B",
127 	.cb = {
128 		.bound    = ipc0B_ept_bound,
129 		.received = ipc0B_ept_recv,
130 	},
131 };
132 
ipc0B_entry(void * dummy0,void * dummy1,void * dummy2)133 static void ipc0B_entry(void *dummy0, void *dummy1, void *dummy2)
134 {
135 	ARG_UNUSED(dummy0);
136 	ARG_UNUSED(dummy1);
137 	ARG_UNUSED(dummy2);
138 
139 	const struct device *ipc0_instance;
140 	unsigned char message = 0;
141 	struct ipc_ept ipc0B_ept;
142 	int ret;
143 
144 	printk("IPC-service HOST [INST 0 - ENDP B] demo started\n");
145 
146 	ipc0_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
147 
148 	ret = ipc_service_open_instance(ipc0_instance);
149 	if (ret < 0 && ret != -EALREADY) {
150 		printk("ipc_service_open_instance() failure\n");
151 		return;
152 	}
153 
154 	ret = ipc_service_register_endpoint(ipc0_instance, &ipc0B_ept, &ipc0B_ept_cfg);
155 	if (ret < 0) {
156 		printf("ipc_service_register_endpoint() failure\n");
157 		return;
158 	}
159 
160 	k_sem_take(&ipc0B_bound_sem, K_FOREVER);
161 
162 	while (message < 100) {
163 		ret = ipc_service_send(&ipc0B_ept, &message, sizeof(message));
164 		if (ret < 0) {
165 			printk("send_message(%d) failed with ret %d\n", message, ret);
166 			break;
167 		}
168 
169 		k_sem_take(&ipc0B_data_sem, K_FOREVER);
170 		message = ipc0B_received_data;
171 
172 		printk("HOST [0B]: %d\n", message);
173 		message++;
174 	}
175 
176 	printk("IPC-service HOST [INST 0 - ENDP B] demo ended.\n");
177 }
178 K_THREAD_DEFINE(ipc0B_thread_id, STACKSIZE, ipc0B_entry, NULL, NULL, NULL, PRIORITY, 0, 0);
179 
180 /*
181  * ==> THREAD 1 (IPC instance 1) <==
182  *
183  * NOTE: This instance is using the NOCOPY copability of the backend.
184  */
185 
186 static struct ipc_ept ipc1_ept;
187 static void *recv_data;
188 
ipc1_ept_bound(void * priv)189 static void ipc1_ept_bound(void *priv)
190 {
191 	k_sem_give(&ipc1_bound_sem);
192 }
193 
ipc1_ept_recv(const void * data,size_t len,void * priv)194 static void ipc1_ept_recv(const void *data, size_t len, void *priv)
195 {
196 	int ret;
197 
198 	ret = ipc_service_hold_rx_buffer(&ipc1_ept, (void *) data);
199 	if (ret < 0) {
200 		printk("ipc_service_hold_rx_buffer failed with ret %d\n", ret);
201 	}
202 
203 	/*
204 	 * This will only support a synchronous request-answer mechanism. For
205 	 * asynchronous cases a chain list should be implemented.
206 	 */
207 	recv_data = (void *) data;
208 
209 	k_sem_give(&ipc1_data_sem);
210 }
211 
212 static struct ipc_ept_cfg ipc1_ept_cfg = {
213 	.name = "ipc1",
214 	.cb = {
215 		.bound    = ipc1_ept_bound,
216 		.received = ipc1_ept_recv,
217 	},
218 };
219 
ipc1_entry(void * dummy0,void * dummy1,void * dummy2)220 static void ipc1_entry(void *dummy0, void *dummy1, void *dummy2)
221 {
222 	ARG_UNUSED(dummy0);
223 	ARG_UNUSED(dummy1);
224 	ARG_UNUSED(dummy2);
225 
226 	const struct device *ipc1_instance;
227 	unsigned char message = 0;
228 	int ret;
229 
230 	printk("IPC-service HOST [INST 1] demo started\n");
231 
232 	ipc1_instance = DEVICE_DT_GET(DT_NODELABEL(ipc1));
233 
234 	ret = ipc_service_open_instance(ipc1_instance);
235 	if (ret < 0 && ret != -EALREADY) {
236 		printk("ipc_service_open_instance() failure\n");
237 		return;
238 	}
239 
240 	ret = ipc_service_register_endpoint(ipc1_instance, &ipc1_ept, &ipc1_ept_cfg);
241 	if (ret < 0) {
242 		printf("ipc_service_register_endpoint() failure\n");
243 		return;
244 	}
245 
246 	k_sem_take(&ipc1_bound_sem, K_FOREVER);
247 
248 	while (message < 50) {
249 		uint32_t len = 0;
250 		void *data;
251 
252 		ret = ipc_service_get_tx_buffer(&ipc1_ept, &data, &len, K_FOREVER);
253 		if (ret < 0) {
254 			printk("ipc_service_get_tx_buffer failed with ret %d\n", ret);
255 			break;
256 		}
257 
258 		if (message != 0) {
259 			*((unsigned char *) data) = *((unsigned char *) recv_data) + 1;
260 
261 			ret = ipc_service_release_rx_buffer(&ipc1_ept, recv_data);
262 			if (ret < 0) {
263 				printk("ipc_service_release_rx_buffer failed with ret %d\n", ret);
264 				break;
265 			}
266 		} else {
267 			*((unsigned char *) data) = 0;
268 		}
269 
270 		ret = ipc_service_send_nocopy(&ipc1_ept, data, sizeof(unsigned char));
271 		if (ret < 0) {
272 			printk("send_message(%d) failed with ret %d\n", message, ret);
273 			break;
274 		}
275 
276 		k_sem_take(&ipc1_data_sem, K_FOREVER);
277 
278 		printk("HOST [1]: %d\n", *((unsigned char *) recv_data));
279 
280 		message++;
281 	}
282 
283 	printk("IPC-service HOST [INST 1] demo ended.\n");
284 }
285 K_THREAD_DEFINE(ipc1_thread_id, STACKSIZE, ipc1_entry, NULL, NULL, NULL, PRIORITY, 0, 0);
286