1 /*
2  * Copyright (c) 2024 EPAM Systems
3  * Copyright (c) 2024-2025 Renesas Electronics Corporation
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/mbox.h>
15 
16 #include <openamp/open_amp.h>
17 #include <metal/device.h>
18 #include "resource_table.h"
19 
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(openamp_rsc_table);
22 
23 #define SHM_DEVICE_NAME "shm"
24 
25 #if !DT_HAS_CHOSEN(zephyr_ipc_shm)
26 #error "Sample requires definition of shared memory for rpmsg"
27 #endif
28 
29 #define APP_EPT_ADDR 1024
30 
31 #define SHUTDOWN_MSG (0xEF56A55A)
32 
33 /* Constants derived from device tree */
34 #define SHM_NODE       DT_CHOSEN(zephyr_ipc_shm)
35 #define SHM_START_ADDR DT_REG_ADDR(SHM_NODE)
36 #define SHM_SIZE       DT_REG_SIZE(SHM_NODE)
37 
38 #define RSC_TABLE_ADDR DT_REG_ADDR(DT_NODELABEL(rsctbl))
39 
40 #define APP_TASK_STACK_SIZE (1024)
41 
42 K_THREAD_STACK_DEFINE(thread_mng_stack, APP_TASK_STACK_SIZE);
43 K_THREAD_STACK_DEFINE(thread_rp__client_stack, APP_TASK_STACK_SIZE);
44 
45 static struct k_thread thread_mng_data;
46 static struct k_thread thread_rp__client_data;
47 
48 const struct mbox_dt_spec tx_channel = MBOX_DT_SPEC_GET(DT_CHOSEN(zephyr_ipc), tx);
49 const struct mbox_dt_spec rx_channel = MBOX_DT_SPEC_GET(DT_CHOSEN(zephyr_ipc), rx);
50 
51 static metal_phys_addr_t shm_physmap = CM33_TO_A55_ADDR_NS(SHM_START_ADDR);
52 static metal_phys_addr_t rsc_physmap = RSC_TABLE_ADDR;
53 
54 struct metal_device shm_device = {
55 	.name = SHM_DEVICE_NAME,
56 	.num_regions = 2,
57 	{
58 		{.virt = NULL}, /* shared memory */
59 		{.virt = NULL}, /* rsc_table memory */
60 	},
61 	.node = {NULL},
62 	.irq_num = 0,
63 
64 };
65 
66 struct rpmsg_rcv_msg {
67 	void *data;
68 	size_t len;
69 };
70 
71 static struct metal_io_region *shm_io;
72 static struct rpmsg_virtio_shm_pool shpool;
73 
74 static struct metal_io_region *rsc_io;
75 static struct rpmsg_virtio_device rvdev;
76 
77 static void *rsc_table;
78 static struct rpmsg_device *rpdev;
79 
80 static char rx_sc_msg[512];
81 static struct rpmsg_endpoint sc_ept;
82 static struct rpmsg_rcv_msg sc_msg = {.data = rx_sc_msg};
83 
84 static K_SEM_DEFINE(data_sem, 0, 1);
85 static K_SEM_DEFINE(data_sc_sem, 0, 1);
86 
87 static volatile int finish;
88 
platform_mbox_callback(const struct device * dev,mbox_channel_id_t channel_id,void * user_data,struct mbox_msg * data)89 static void platform_mbox_callback(const struct device *dev, mbox_channel_id_t channel_id,
90 				   void *user_data, struct mbox_msg *data)
91 {
92 	k_sem_give(&data_sem);
93 }
94 
rpmsg_recv_cs_callback(struct rpmsg_endpoint * ept,void * data,size_t len,uint32_t src,void * priv)95 static int rpmsg_recv_cs_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src,
96 				  void *priv)
97 {
98 	memcpy(sc_msg.data, data, len);
99 	sc_msg.len = len;
100 	if ((*(unsigned int *)data) == SHUTDOWN_MSG) {
101 		finish = 1;
102 	}
103 	k_sem_give(&data_sc_sem);
104 
105 	return RPMSG_SUCCESS;
106 }
107 
receive_message(unsigned char ** msg,unsigned int * len)108 static void receive_message(unsigned char **msg, unsigned int *len)
109 {
110 	int status = k_sem_take(&data_sem, K_FOREVER);
111 
112 	if (status == 0) {
113 		rproc_virtio_notified(rvdev.vdev, VRING1_ID);
114 	}
115 }
116 
new_service_cb(struct rpmsg_device * rdev,const char * name,uint32_t src)117 static void new_service_cb(struct rpmsg_device *rdev, const char *name, uint32_t src)
118 {
119 	LOG_INF("%s: message received from service %s", __func__, name);
120 }
121 
mailbox_notify(void * priv,uint32_t id)122 int mailbox_notify(void *priv, uint32_t id)
123 {
124 	ARG_UNUSED(priv);
125 
126 	mbox_send_dt(&tx_channel, NULL);
127 	return 0;
128 }
129 
platform_init(void)130 int platform_init(void)
131 {
132 	void *rsc_tab_addr;
133 	int rsc_size;
134 	struct metal_device *device;
135 	struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
136 	int status;
137 
138 	status = metal_init(&metal_params);
139 	if (status) {
140 		LOG_ERR("metal_init: failed: %d", status);
141 		return -1;
142 	}
143 
144 	status = metal_register_generic_device(&shm_device);
145 	if (status) {
146 		LOG_ERR("Couldn't register shared memory: %d", status);
147 		return -1;
148 	}
149 
150 	status = metal_device_open("generic", SHM_DEVICE_NAME, &device);
151 	if (status) {
152 		LOG_ERR("metal_device_open failed: %d", status);
153 		return -1;
154 	}
155 
156 	/* declare shared memory region */
157 	metal_io_init(&device->regions[0], (void *)SHM_START_ADDR, &shm_physmap, SHM_SIZE, -1, 0,
158 		      NULL);
159 
160 	shm_io = metal_device_io_region(device, 0);
161 	if (!shm_io) {
162 		LOG_ERR("Failed to get shm_io region");
163 		return -1;
164 	}
165 
166 	/* declare resource table region */
167 	rsc_table_get(&rsc_tab_addr, &rsc_size);
168 	memcpy((void *)RSC_TABLE_ADDR, rsc_tab_addr, rsc_size);
169 	rsc_table = (struct st_resource_table *)RSC_TABLE_ADDR;
170 
171 	metal_io_init(&device->regions[1], (void *)RSC_TABLE_ADDR, &rsc_physmap, rsc_size, -1, 0,
172 		      NULL);
173 
174 	rsc_io = metal_device_io_region(device, 1);
175 	if (!rsc_io) {
176 		LOG_ERR("Failed to get rsc_io region");
177 		return -1;
178 	}
179 
180 	/* Setup MBOX */
181 	if (!mbox_is_ready_dt(&tx_channel)) {
182 		LOG_ERR("MBOX TX device is not ready");
183 		return -1;
184 	}
185 
186 	if (!mbox_is_ready_dt(&rx_channel)) {
187 		LOG_ERR("MBOX RX device is not ready");
188 		return -1;
189 	}
190 
191 	mbox_register_callback_dt(&rx_channel, platform_mbox_callback, NULL);
192 
193 	status = mbox_set_enabled_dt(&rx_channel, true);
194 	if (status) {
195 		LOG_ERR("mbox_set_enabled_dt failed");
196 		return -1;
197 	}
198 
199 	return 0;
200 }
201 
platform_deinit(void)202 static void platform_deinit(void)
203 {
204 	mbox_set_enabled_dt(&rx_channel, false);
205 
206 	metal_finish();
207 }
208 
cleanup_system(void)209 static void cleanup_system(void)
210 {
211 	struct fw_resource_table *rsc_tbl = (struct fw_resource_table *)RSC_TABLE_ADDR;
212 
213 	rpmsg_deinit_vdev(&rvdev);
214 	rproc_virtio_remove_vdev(rvdev.vdev);
215 	/*
216 	 * Clean vdev status in rsc_table because it may not be cleared from
217 	 * master end. This is not default work behavior. By default rsc_table
218 	 * should be provided and managed by master.
219 	 */
220 	rsc_tbl->vdev.status = 0;
221 }
222 
platform_create_rpmsg_vdev(unsigned int vdev_index,unsigned int role,void (* rst_cb)(struct virtio_device * vdev),rpmsg_ns_bind_cb ns_cb)223 struct rpmsg_device *platform_create_rpmsg_vdev(unsigned int vdev_index, unsigned int role,
224 						void (*rst_cb)(struct virtio_device *vdev),
225 						rpmsg_ns_bind_cb ns_cb)
226 {
227 	struct fw_rsc_vdev_vring *vring_rsc;
228 	struct virtio_device *vdev;
229 	int ret;
230 
231 	vdev = rproc_virtio_create_vdev(VIRTIO_DEV_DEVICE, VDEV_ID, rsc_table_to_vdev(rsc_table),
232 					rsc_io, NULL, mailbox_notify, NULL);
233 
234 	if (!vdev) {
235 		LOG_ERR("failed to create vdev");
236 		return NULL;
237 	}
238 
239 	/* Set gfeatures because they should be equal to dfeatures
240 	 * when create_ept is called. As rproc_virtio_create_vdev
241 	 * doesn't set them because its VIRTIO_DEVICE not DRIVER.
242 	 * Assume the virtio driver support all remote features.
243 	 */
244 	virtio_set_features(vdev, 0x1);
245 
246 	/* wait master rpmsg init completion */
247 	rproc_virtio_wait_remote_ready(vdev);
248 
249 	vring_rsc = rsc_table_get_vring0(rsc_table);
250 
251 	ret = rproc_virtio_init_vring(vdev, 0, vring_rsc->notifyid, (void *)VRING_TX_ADDR_CM33,
252 				      rsc_io, vring_rsc->num, vring_rsc->align);
253 	if (ret) {
254 		LOG_ERR("failed to init vring 0");
255 		goto failed;
256 	}
257 
258 	vring_rsc = rsc_table_get_vring1(rsc_table);
259 
260 	ret = rproc_virtio_init_vring(vdev, 1, vring_rsc->notifyid, (void *)VRING_RX_ADDR_CM33,
261 				      rsc_io, vring_rsc->num, vring_rsc->align);
262 	if (ret) {
263 		LOG_ERR("failed to init vring 1");
264 		goto failed;
265 	}
266 
267 	rpmsg_virtio_init_shm_pool(&shpool, NULL, SHM_SIZE);
268 	ret = rpmsg_init_vdev(&rvdev, vdev, ns_cb, shm_io, &shpool);
269 
270 	if (ret) {
271 		LOG_ERR("failed rpmsg_init_vdev");
272 		goto failed;
273 	}
274 
275 	return rpmsg_virtio_get_rpmsg_device(&rvdev);
276 
277 failed:
278 	rproc_virtio_remove_vdev(vdev);
279 
280 	return NULL;
281 }
282 
app_rpmsg_client_sample(void * arg1,void * arg2,void * arg3)283 void app_rpmsg_client_sample(void *arg1, void *arg2, void *arg3)
284 {
285 	ARG_UNUSED(arg1);
286 	ARG_UNUSED(arg2);
287 	ARG_UNUSED(arg3);
288 	unsigned int msg_cnt = 0;
289 	int ret = 0;
290 
291 	k_sem_take(&data_sc_sem, K_FOREVER);
292 
293 	LOG_INF("OpenAMP[remote] Linux sample client responder started");
294 
295 	ret = rpmsg_create_ept(&sc_ept, rpdev, "rpmsg-service-0", APP_EPT_ADDR, RPMSG_ADDR_ANY,
296 			       rpmsg_recv_cs_callback, NULL);
297 
298 	while (!finish) {
299 		k_sem_take(&data_sc_sem, K_FOREVER);
300 		msg_cnt++;
301 		rpmsg_send(&sc_ept, sc_msg.data, sc_msg.len);
302 	}
303 
304 	rpmsg_destroy_ept(&sc_ept);
305 	k_sem_reset(&data_sc_sem);
306 
307 	LOG_INF("OpenAMP Linux sample client responder ended");
308 }
309 
rpmsg_mng_task(void * arg1,void * arg2,void * arg3)310 void rpmsg_mng_task(void *arg1, void *arg2, void *arg3)
311 {
312 	ARG_UNUSED(arg1);
313 	ARG_UNUSED(arg2);
314 	ARG_UNUSED(arg3);
315 	unsigned char *msg;
316 	unsigned int len;
317 	int ret = 0;
318 
319 	LOG_INF("OpenAMP[remote]  linux responder demo started");
320 
321 	/* Initialize platform */
322 	rpdev = platform_create_rpmsg_vdev(0, VIRTIO_DEV_DEVICE, NULL, new_service_cb);
323 	if (!rpdev) {
324 		LOG_ERR("Failed to create rpmsg virtio device");
325 		ret = -1;
326 		goto task_end;
327 	}
328 
329 	/* start the rpmsg clients */
330 	k_sem_give(&data_sc_sem);
331 
332 	while (!finish) {
333 		receive_message(&msg, &len);
334 	}
335 
336 task_end:
337 	cleanup_system();
338 
339 	LOG_INF("OpenAMP demo ended");
340 }
341 
main(void)342 int main(void)
343 {
344 	LOG_INF("Starting application..!");
345 
346 	/* Initialize platform */
347 	int ret = platform_init();
348 
349 	if (ret) {
350 		LOG_ERR("Failed to initialize platform");
351 		return -1;
352 	}
353 
354 	while (1) {
355 		finish = 0;
356 
357 		LOG_INF("Starting application threads!");
358 		k_thread_create(&thread_mng_data, thread_mng_stack, APP_TASK_STACK_SIZE,
359 				(k_thread_entry_t)rpmsg_mng_task, NULL, NULL, NULL, K_PRIO_COOP(8),
360 				0, K_NO_WAIT);
361 		k_thread_create(&thread_rp__client_data, thread_rp__client_stack,
362 				APP_TASK_STACK_SIZE, (k_thread_entry_t)app_rpmsg_client_sample,
363 				NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
364 
365 		k_thread_join(&thread_mng_data, K_FOREVER);
366 		k_thread_join(&thread_rp__client_data, K_FOREVER);
367 	}
368 
369 	platform_deinit();
370 	return 0;
371 }
372