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/ipc/ipc_rpmsg.h>
9 
rpmsg_service_unbind(struct rpmsg_endpoint * ep)10 static void rpmsg_service_unbind(struct rpmsg_endpoint *ep)
11 {
12 	rpmsg_destroy_ept(ep);
13 }
14 
ns_bind_cb(struct rpmsg_device * rdev,const char * name,uint32_t dest)15 static void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest)
16 {
17 	struct rpmsg_virtio_device *p_rvdev;
18 	struct ipc_rpmsg_instance *instance;
19 	struct ipc_rpmsg_ept *ept;
20 	int err;
21 
22 	p_rvdev = CONTAINER_OF(rdev, struct rpmsg_virtio_device, rdev);
23 	instance = CONTAINER_OF(p_rvdev->shpool, struct ipc_rpmsg_instance, shm_pool);
24 
25 	for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
26 		ept = &instance->endpoint[i];
27 
28 		if (strcmp(name, ept->name) == 0) {
29 			/*
30 			 * The destination address is 'dest' so ns_bind_cb() is
31 			 * *NOT* called on the REMOTE side. The bound_cb()
32 			 * function will eventually take care of notifying the
33 			 * REMOTE side if needed.
34 			 */
35 			err = rpmsg_create_ept(&ept->ep, rdev, name, RPMSG_ADDR_ANY,
36 					       dest, instance->cb, rpmsg_service_unbind);
37 			if (err != 0) {
38 				return;
39 			}
40 
41 			ept->bound = true;
42 			if (instance->bound_cb) {
43 				instance->bound_cb(ept);
44 			}
45 		}
46 	}
47 }
48 
ipc_rpmsg_register_ept(struct ipc_rpmsg_instance * instance,unsigned int role,struct ipc_rpmsg_ept * ept)49 int ipc_rpmsg_register_ept(struct ipc_rpmsg_instance *instance, unsigned int role,
50 			   struct ipc_rpmsg_ept *ept)
51 {
52 	struct rpmsg_device *rdev;
53 
54 	if (!instance || !ept) {
55 		return -EINVAL;
56 	}
57 
58 	rdev = rpmsg_virtio_get_rpmsg_device(&instance->rvdev);
59 
60 	if (role == RPMSG_REMOTE) {
61 		/*
62 		 * The destination address is RPMSG_ADDR_ANY, this will trigger
63 		 * the ns_bind_cb() callback function on the HOST side.
64 		 */
65 		return rpmsg_create_ept(&ept->ep, rdev, ept->name, RPMSG_ADDR_ANY,
66 					RPMSG_ADDR_ANY, instance->cb, rpmsg_service_unbind);
67 	}
68 
69 	return RPMSG_SUCCESS;
70 }
71 
ipc_rpmsg_init(struct ipc_rpmsg_instance * instance,unsigned int role,unsigned int buffer_size,struct metal_io_region * shm_io,struct virtio_device * vdev,void * shb,size_t size,rpmsg_ns_bind_cb p_bind_cb)72 int ipc_rpmsg_init(struct ipc_rpmsg_instance *instance,
73 		   unsigned int role,
74 		   unsigned int buffer_size,
75 		   struct metal_io_region *shm_io,
76 		   struct virtio_device *vdev,
77 		   void *shb, size_t size,
78 		   rpmsg_ns_bind_cb p_bind_cb)
79 {
80 	rpmsg_ns_bind_cb bind_cb = p_bind_cb;
81 
82 	if (!instance || !shb) {
83 		return -EINVAL;
84 	}
85 
86 	if (p_bind_cb == NULL) {
87 		bind_cb = ns_bind_cb;
88 	}
89 
90 	if (role == RPMSG_HOST) {
91 		struct rpmsg_virtio_config config = { 0 };
92 
93 		config.h2r_buf_size = (uint32_t) buffer_size;
94 		config.r2h_buf_size = (uint32_t) buffer_size;
95 
96 		rpmsg_virtio_init_shm_pool(&instance->shm_pool, shb, size);
97 
98 		return rpmsg_init_vdev_with_config(&instance->rvdev, vdev, bind_cb,
99 						   shm_io, &instance->shm_pool,
100 						   &config);
101 	} else {
102 		return rpmsg_init_vdev(&instance->rvdev, vdev, bind_cb, shm_io, NULL);
103 	}
104 }
105 
ipc_rpmsg_deinit(struct ipc_rpmsg_instance * instance,unsigned int role)106 int ipc_rpmsg_deinit(struct ipc_rpmsg_instance *instance,
107 		   unsigned int role)
108 {
109 	if (!instance) {
110 		return -EINVAL;
111 	}
112 
113 	rpmsg_deinit_vdev(&instance->rvdev);
114 
115 	if (role == RPMSG_HOST) {
116 		memset(&instance->shm_pool, 0, sizeof(struct rpmsg_virtio_shm_pool));
117 	}
118 
119 	return 0;
120 }
121