1 /*
2  * Copyright (c) 2020, STMICROELECTRONICS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <zephyr/drivers/ipm.h>
15 
16 #include <openamp/open_amp.h>
17 #include <metal/sys.h>
18 #include <metal/io.h>
19 #include <resource_table.h>
20 
21 #ifdef CONFIG_SHELL_BACKEND_RPMSG
22 #include <zephyr/shell/shell_rpmsg.h>
23 #endif
24 
25 #include <zephyr/logging/log.h>
26 LOG_MODULE_REGISTER(openamp_rsc_table, LOG_LEVEL_DBG);
27 
28 #define SHM_DEVICE_NAME	"shm"
29 
30 #if !DT_HAS_CHOSEN(zephyr_ipc_shm)
31 #error "Sample requires definition of shared memory for rpmsg"
32 #endif
33 
34 /* Constants derived from device tree */
35 #define SHM_NODE		DT_CHOSEN(zephyr_ipc_shm)
36 #define SHM_START_ADDR	DT_REG_ADDR(SHM_NODE)
37 #define SHM_SIZE		DT_REG_SIZE(SHM_NODE)
38 
39 #define APP_TASK_STACK_SIZE (1024)
40 
41 /* Add 1024 extra bytes for the TTY task stack for the "tx_buff" buffer. */
42 #define APP_TTY_TASK_STACK_SIZE (1536)
43 
44 K_THREAD_STACK_DEFINE(thread_mng_stack, APP_TASK_STACK_SIZE);
45 K_THREAD_STACK_DEFINE(thread_rp__client_stack, APP_TASK_STACK_SIZE);
46 K_THREAD_STACK_DEFINE(thread_tty_stack, APP_TTY_TASK_STACK_SIZE);
47 
48 static struct k_thread thread_mng_data;
49 static struct k_thread thread_rp__client_data;
50 static struct k_thread thread_tty_data;
51 
52 static const struct device *const ipm_handle =
53 	DEVICE_DT_GET(DT_CHOSEN(zephyr_ipc));
54 
55 static metal_phys_addr_t shm_physmap = SHM_START_ADDR;
56 static metal_phys_addr_t rsc_tab_physmap;
57 
58 static struct metal_io_region shm_io_data; /* shared memory */
59 static struct metal_io_region rsc_io_data; /* rsc_table memory */
60 
61 struct rpmsg_rcv_msg {
62 	void *data;
63 	size_t len;
64 };
65 
66 static struct metal_io_region *shm_io = &shm_io_data;
67 
68 static struct metal_io_region *rsc_io = &rsc_io_data;
69 static struct rpmsg_virtio_device rvdev;
70 
71 static struct fw_resource_table *rsc_table;
72 static struct rpmsg_device *rpdev;
73 
74 static char rx_sc_msg[20];  /* should receive "Hello world!" */
75 static struct rpmsg_endpoint sc_ept;
76 static struct rpmsg_rcv_msg sc_msg = {.data = rx_sc_msg};
77 
78 static struct rpmsg_endpoint tty_ept;
79 static struct rpmsg_rcv_msg tty_msg;
80 
81 static K_SEM_DEFINE(data_sem, 0, 1);
82 static K_SEM_DEFINE(data_sc_sem, 0, 1);
83 static K_SEM_DEFINE(data_tty_sem, 0, 1);
84 
platform_ipm_callback(const struct device * dev,void * context,uint32_t id,volatile void * data)85 static void platform_ipm_callback(const struct device *dev, void *context,
86 				  uint32_t id, volatile void *data)
87 {
88 	LOG_DBG("%s: msg received from mb %d", __func__, id);
89 	k_sem_give(&data_sem);
90 }
91 
rpmsg_recv_cs_callback(struct rpmsg_endpoint * ept,void * data,size_t len,uint32_t src,void * priv)92 static int rpmsg_recv_cs_callback(struct rpmsg_endpoint *ept, void *data,
93 				  size_t len, uint32_t src, void *priv)
94 {
95 	memcpy(sc_msg.data, data, len);
96 	sc_msg.len = len;
97 	k_sem_give(&data_sc_sem);
98 
99 	return RPMSG_SUCCESS;
100 }
101 
rpmsg_recv_tty_callback(struct rpmsg_endpoint * ept,void * data,size_t len,uint32_t src,void * priv)102 static int rpmsg_recv_tty_callback(struct rpmsg_endpoint *ept, void *data,
103 				   size_t len, uint32_t src, void *priv)
104 {
105 	struct rpmsg_rcv_msg *msg = priv;
106 
107 	rpmsg_hold_rx_buffer(ept, data);
108 	msg->data = data;
109 	msg->len = len;
110 	k_sem_give(&data_tty_sem);
111 
112 	return RPMSG_SUCCESS;
113 }
114 
receive_message(unsigned char ** msg,unsigned int * len)115 static void receive_message(unsigned char **msg, unsigned int *len)
116 {
117 	int status = k_sem_take(&data_sem, K_FOREVER);
118 
119 	if (status == 0) {
120 		rproc_virtio_notified(rvdev.vdev, VRING1_ID);
121 	}
122 }
123 
new_service_cb(struct rpmsg_device * rdev,const char * name,uint32_t src)124 static void new_service_cb(struct rpmsg_device *rdev, const char *name,
125 			   uint32_t src)
126 {
127 	LOG_ERR("%s: unexpected ns service receive for name %s",
128 		__func__, name);
129 }
130 
mailbox_notify(void * priv,uint32_t id)131 int mailbox_notify(void *priv, uint32_t id)
132 {
133 	ARG_UNUSED(priv);
134 
135 	LOG_DBG("%s: msg received", __func__);
136 	ipm_send(ipm_handle, 0, id, NULL, 0);
137 
138 	return 0;
139 }
140 
platform_init(void)141 int platform_init(void)
142 {
143 	int rsc_size;
144 	struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
145 	int status;
146 
147 	status = metal_init(&metal_params);
148 	if (status) {
149 		LOG_ERR("metal_init: failed: %d", status);
150 		return -1;
151 	}
152 
153 	/* declare shared memory region */
154 	metal_io_init(shm_io, (void *)SHM_START_ADDR, &shm_physmap,
155 		      SHM_SIZE, -1, 0, NULL);
156 
157 	/* declare resource table region */
158 	rsc_table_get(&rsc_table, &rsc_size);
159 	rsc_tab_physmap = (uintptr_t)rsc_table;
160 
161 	metal_io_init(rsc_io, rsc_table,
162 		      &rsc_tab_physmap, rsc_size, -1, 0, NULL);
163 
164 	/* setup IPM */
165 	if (!device_is_ready(ipm_handle)) {
166 		LOG_ERR("IPM device is not ready");
167 		return -1;
168 	}
169 
170 	ipm_register_callback(ipm_handle, platform_ipm_callback, NULL);
171 
172 	status = ipm_set_enabled(ipm_handle, 1);
173 	if (status) {
174 		LOG_ERR("ipm_set_enabled failed");
175 		return -1;
176 	}
177 
178 	return 0;
179 }
180 
cleanup_system(void)181 static void cleanup_system(void)
182 {
183 	ipm_set_enabled(ipm_handle, 0);
184 	rpmsg_deinit_vdev(&rvdev);
185 	metal_finish();
186 }
187 
188 struct  rpmsg_device *
platform_create_rpmsg_vdev(unsigned int vdev_index,unsigned int role,void (* rst_cb)(struct virtio_device * vdev),rpmsg_ns_bind_cb ns_cb)189 platform_create_rpmsg_vdev(unsigned int vdev_index,
190 			   unsigned int role,
191 			   void (*rst_cb)(struct virtio_device *vdev),
192 			   rpmsg_ns_bind_cb ns_cb)
193 {
194 	struct fw_rsc_vdev_vring *vring_rsc;
195 	struct virtio_device *vdev;
196 	int ret;
197 
198 	vdev = rproc_virtio_create_vdev(VIRTIO_DEV_DEVICE, VDEV_ID,
199 					rsc_table_to_vdev(rsc_table),
200 					rsc_io, NULL, mailbox_notify, NULL);
201 
202 	if (!vdev) {
203 		LOG_ERR("failed to create vdev");
204 		return NULL;
205 	}
206 
207 	/* wait master rpmsg init completion */
208 	rproc_virtio_wait_remote_ready(vdev);
209 
210 	vring_rsc = rsc_table_get_vring0(rsc_table);
211 	ret = rproc_virtio_init_vring(vdev, 0, vring_rsc->notifyid,
212 				      (void *)vring_rsc->da, rsc_io,
213 				      vring_rsc->num, vring_rsc->align);
214 	if (ret) {
215 		LOG_ERR("failed to init vring 0");
216 		goto failed;
217 	}
218 
219 	vring_rsc = rsc_table_get_vring1(rsc_table);
220 	ret = rproc_virtio_init_vring(vdev, 1, vring_rsc->notifyid,
221 				      (void *)vring_rsc->da, rsc_io,
222 				      vring_rsc->num, vring_rsc->align);
223 	if (ret) {
224 		LOG_ERR("failed to init vring 1");
225 		goto failed;
226 	}
227 
228 	ret = rpmsg_init_vdev(&rvdev, vdev, ns_cb, shm_io, NULL);
229 	if (ret) {
230 		LOG_ERR("failed rpmsg_init_vdev");
231 		goto failed;
232 	}
233 
234 	return rpmsg_virtio_get_rpmsg_device(&rvdev);
235 
236 failed:
237 	rproc_virtio_remove_vdev(vdev);
238 
239 	return NULL;
240 }
241 
app_rpmsg_client_sample(void * arg1,void * arg2,void * arg3)242 void app_rpmsg_client_sample(void *arg1, void *arg2, void *arg3)
243 {
244 	ARG_UNUSED(arg1);
245 	ARG_UNUSED(arg2);
246 	ARG_UNUSED(arg3);
247 
248 	unsigned int msg_cnt = 0;
249 	int ret = 0;
250 
251 	k_sem_take(&data_sc_sem,  K_FOREVER);
252 
253 	LOG_INF("OpenAMP[remote] Linux sample client responder started");
254 
255 	ret = rpmsg_create_ept(&sc_ept, rpdev, "rpmsg-client-sample",
256 			       RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
257 			       rpmsg_recv_cs_callback, NULL);
258 	if (ret) {
259 		LOG_ERR("[Linux sample client] Could not create endpoint: %d", ret);
260 		goto task_end;
261 	}
262 
263 	while (msg_cnt < 100) {
264 		k_sem_take(&data_sc_sem,  K_FOREVER);
265 		msg_cnt++;
266 		LOG_INF("[Linux sample client] incoming msg %d: %.*s", msg_cnt, sc_msg.len,
267 			(char *)sc_msg.data);
268 		rpmsg_send(&sc_ept, sc_msg.data, sc_msg.len);
269 	}
270 	rpmsg_destroy_ept(&sc_ept);
271 
272 task_end:
273 	LOG_INF("OpenAMP Linux sample client responder ended");
274 }
275 
app_rpmsg_tty(void * arg1,void * arg2,void * arg3)276 void app_rpmsg_tty(void *arg1, void *arg2, void *arg3)
277 {
278 	ARG_UNUSED(arg1);
279 	ARG_UNUSED(arg2);
280 	ARG_UNUSED(arg3);
281 
282 	unsigned char tx_buff[512];
283 	int ret = 0;
284 
285 	k_sem_take(&data_tty_sem,  K_FOREVER);
286 
287 	LOG_INF("OpenAMP[remote] Linux TTY responder started");
288 
289 	tty_ept.priv = &tty_msg;
290 	ret = rpmsg_create_ept(&tty_ept, rpdev, "rpmsg-tty",
291 			       RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
292 			       rpmsg_recv_tty_callback, NULL);
293 	if (ret) {
294 		LOG_ERR("[Linux TTY] Could not create endpoint: %d", ret);
295 		goto task_end;
296 	}
297 
298 	while (tty_ept.addr !=  RPMSG_ADDR_ANY) {
299 		k_sem_take(&data_tty_sem,  K_FOREVER);
300 		if (tty_msg.len) {
301 			LOG_INF("[Linux TTY] incoming msg: %.*s",
302 				(int)tty_msg.len, (char *)tty_msg.data);
303 			snprintf(tx_buff, 13, "TTY 0x%04x: ", tty_ept.addr);
304 			memcpy(&tx_buff[12], tty_msg.data, tty_msg.len);
305 			rpmsg_send(&tty_ept, tx_buff, tty_msg.len + 12);
306 			rpmsg_release_rx_buffer(&tty_ept, tty_msg.data);
307 		}
308 		tty_msg.len = 0;
309 		tty_msg.data = NULL;
310 	}
311 	rpmsg_destroy_ept(&tty_ept);
312 
313 task_end:
314 	LOG_INF("OpenAMP Linux TTY responder ended");
315 }
316 
rpmsg_mng_task(void * arg1,void * arg2,void * arg3)317 void rpmsg_mng_task(void *arg1, void *arg2, void *arg3)
318 {
319 	ARG_UNUSED(arg1);
320 	ARG_UNUSED(arg2);
321 	ARG_UNUSED(arg3);
322 
323 	unsigned char *msg;
324 	unsigned int len;
325 	int ret = 0;
326 
327 	LOG_INF("OpenAMP[remote] Linux responder demo started");
328 
329 	/* Initialize platform */
330 	ret = platform_init();
331 	if (ret) {
332 		LOG_ERR("Failed to initialize platform");
333 		ret = -1;
334 		goto task_end;
335 	}
336 
337 	rpdev = platform_create_rpmsg_vdev(0, VIRTIO_DEV_DEVICE, NULL,
338 					   new_service_cb);
339 	if (!rpdev) {
340 		LOG_ERR("Failed to create rpmsg virtio device");
341 		ret = -1;
342 		goto task_end;
343 	}
344 
345 #ifdef CONFIG_SHELL_BACKEND_RPMSG
346 	(void)shell_backend_rpmsg_init_transport(rpdev);
347 #endif
348 
349 	/* start the rpmsg clients */
350 	k_sem_give(&data_sc_sem);
351 	k_sem_give(&data_tty_sem);
352 
353 	while (1) {
354 		receive_message(&msg, &len);
355 	}
356 
357 task_end:
358 	cleanup_system();
359 
360 	LOG_INF("OpenAMP demo ended");
361 }
362 
main(void)363 int main(void)
364 {
365 	LOG_INF("Starting application threads!");
366 	k_thread_create(&thread_mng_data, thread_mng_stack, APP_TASK_STACK_SIZE,
367 			rpmsg_mng_task,
368 			NULL, NULL, NULL, K_PRIO_COOP(8), 0, K_NO_WAIT);
369 	k_thread_create(&thread_rp__client_data, thread_rp__client_stack, APP_TASK_STACK_SIZE,
370 			app_rpmsg_client_sample,
371 			NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
372 	k_thread_create(&thread_tty_data, thread_tty_stack, APP_TTY_TASK_STACK_SIZE,
373 			app_rpmsg_tty,
374 			NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
375 	return 0;
376 }
377