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