1 /*
2  * Copyright (c) 2014, Mentor Graphics Corporation
3  * All rights reserved.
4  * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
5  * Copyright (c) 2018 Linaro, Inc. All rights reserved.
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 
10 #include <openamp/rpmsg.h>
11 #include <metal/alloc.h>
12 
13 #include "rpmsg_internal.h"
14 
15 /**
16  * rpmsg_get_address
17  *
18  * This function provides unique 32 bit address.
19  *
20  * @param bitmap - bit map for addresses
21  * @param size   - size of bitmap
22  *
23  * return - a unique address
24  */
rpmsg_get_address(unsigned long * bitmap,int size)25 static uint32_t rpmsg_get_address(unsigned long *bitmap, int size)
26 {
27 	unsigned int addr = RPMSG_ADDR_ANY;
28 	unsigned int nextbit;
29 
30 	nextbit = metal_bitmap_next_clear_bit(bitmap, 0, size);
31 	if (nextbit < (uint32_t)size) {
32 		addr = nextbit;
33 		metal_bitmap_set_bit(bitmap, nextbit);
34 	}
35 
36 	return addr;
37 }
38 
39 /**
40  * rpmsg_release_address
41  *
42  * Frees the given address.
43  *
44  * @param bitmap - bit map for addresses
45  * @param size   - size of bitmap
46  * @param addr   - address to free
47  */
rpmsg_release_address(unsigned long * bitmap,int size,int addr)48 static void rpmsg_release_address(unsigned long *bitmap, int size,
49 				  int addr)
50 {
51 	if (addr < size)
52 		metal_bitmap_clear_bit(bitmap, addr);
53 }
54 
55 /**
56  * rpmsg_is_address_set
57  *
58  * Checks whether address is used or free.
59  *
60  * @param bitmap - bit map for addresses
61  * @param size   - size of bitmap
62  * @param addr   - address to free
63  *
64  * return - TRUE/FALSE
65  */
rpmsg_is_address_set(unsigned long * bitmap,int size,int addr)66 static int rpmsg_is_address_set(unsigned long *bitmap, int size, int addr)
67 {
68 	if (addr < size)
69 		return metal_bitmap_is_bit_set(bitmap, addr);
70 	else
71 		return RPMSG_ERR_PARAM;
72 }
73 
74 /**
75  * rpmsg_set_address
76  *
77  * Marks the address as consumed.
78  *
79  * @param bitmap - bit map for addresses
80  * @param size   - size of bitmap
81  * @param addr   - address to free
82  *
83  * return - none
84  */
rpmsg_set_address(unsigned long * bitmap,int size,int addr)85 static int rpmsg_set_address(unsigned long *bitmap, int size, int addr)
86 {
87 	if (addr < size) {
88 		metal_bitmap_set_bit(bitmap, addr);
89 		return RPMSG_SUCCESS;
90 	} else {
91 		return RPMSG_ERR_PARAM;
92 	}
93 }
94 
95 /**
96  * This function sends rpmsg "message" to remote device.
97  *
98  * @param ept     - pointer to end point
99  * @param src     - source address of channel
100  * @param dst     - destination address of channel
101  * @param data    - data to transmit
102  * @param size    - size of data
103  * @param wait    - boolean, wait or not for buffer to become
104  *                  available
105  *
106  * @return - size of data sent or negative value for failure.
107  *
108  */
rpmsg_send_offchannel_raw(struct rpmsg_endpoint * ept,uint32_t src,uint32_t dst,const void * data,int size,int wait)109 int rpmsg_send_offchannel_raw(struct rpmsg_endpoint *ept, uint32_t src,
110 			      uint32_t dst, const void *data, int size,
111 			      int wait)
112 {
113 	struct rpmsg_device *rdev;
114 
115 	if (!ept || !ept->rdev || !data || dst == RPMSG_ADDR_ANY)
116 		return RPMSG_ERR_PARAM;
117 
118 	rdev = ept->rdev;
119 
120 	if (rdev->ops.send_offchannel_raw)
121 		return rdev->ops.send_offchannel_raw(rdev, src, dst, data,
122 						      size, wait);
123 
124 	return RPMSG_ERR_PARAM;
125 }
126 
rpmsg_send_ns_message(struct rpmsg_endpoint * ept,unsigned long flags)127 int rpmsg_send_ns_message(struct rpmsg_endpoint *ept, unsigned long flags)
128 {
129 	struct rpmsg_ns_msg ns_msg;
130 	int ret;
131 
132 	ns_msg.flags = flags;
133 	ns_msg.addr = ept->addr;
134 	strncpy(ns_msg.name, ept->name, sizeof(ns_msg.name));
135 	ret = rpmsg_send_offchannel_raw(ept, ept->addr,
136 					RPMSG_NS_EPT_ADDR,
137 					&ns_msg, sizeof(ns_msg), true);
138 	if (ret < 0)
139 		return ret;
140 	else
141 		return RPMSG_SUCCESS;
142 }
143 
rpmsg_get_endpoint(struct rpmsg_device * rdev,const char * name,uint32_t addr,uint32_t dest_addr)144 struct rpmsg_endpoint *rpmsg_get_endpoint(struct rpmsg_device *rdev,
145 					  const char *name, uint32_t addr,
146 					  uint32_t dest_addr)
147 {
148 	struct metal_list *node;
149 	struct rpmsg_endpoint *ept;
150 
151 	metal_list_for_each(&rdev->endpoints, node) {
152 		int name_match = 0;
153 
154 		ept = metal_container_of(node, struct rpmsg_endpoint, node);
155 		/* try to get by local address only */
156 		if (addr != RPMSG_ADDR_ANY && ept->addr == addr)
157 			return ept;
158 		/* try to find match on local end remote address */
159 		if (addr == ept->addr && dest_addr == ept->dest_addr)
160 			return ept;
161 		/* else use name service and destination address */
162 		if (name)
163 			name_match = !strncmp(ept->name, name,
164 					      sizeof(ept->name));
165 		if (!name || !name_match)
166 			continue;
167 		/* destination address is known, equal to ept remote address*/
168 		if (dest_addr != RPMSG_ADDR_ANY && ept->dest_addr == dest_addr)
169 			return ept;
170 		/* ept is registered but not associated to remote ept*/
171 		if (addr == RPMSG_ADDR_ANY && ept->dest_addr == RPMSG_ADDR_ANY)
172 			return ept;
173 	}
174 	return NULL;
175 }
176 
rpmsg_unregister_endpoint(struct rpmsg_endpoint * ept)177 static void rpmsg_unregister_endpoint(struct rpmsg_endpoint *ept)
178 {
179 	struct rpmsg_device *rdev;
180 
181 	if (!ept)
182 		return;
183 
184 	rdev = ept->rdev;
185 
186 	if (ept->addr != RPMSG_ADDR_ANY)
187 		rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
188 				      ept->addr);
189 	metal_list_del(&ept->node);
190 }
191 
rpmsg_register_endpoint(struct rpmsg_device * rdev,struct rpmsg_endpoint * ept)192 int rpmsg_register_endpoint(struct rpmsg_device *rdev,
193 			    struct rpmsg_endpoint *ept)
194 {
195 	ept->rdev = rdev;
196 
197 	metal_list_add_tail(&rdev->endpoints, &ept->node);
198 	return RPMSG_SUCCESS;
199 }
200 
rpmsg_create_ept(struct rpmsg_endpoint * ept,struct rpmsg_device * rdev,const char * name,uint32_t src,uint32_t dest,rpmsg_ept_cb cb,rpmsg_ns_unbind_cb unbind_cb)201 int rpmsg_create_ept(struct rpmsg_endpoint *ept, struct rpmsg_device *rdev,
202 		     const char *name, uint32_t src, uint32_t dest,
203 		     rpmsg_ept_cb cb, rpmsg_ns_unbind_cb unbind_cb)
204 {
205 	int status;
206 	uint32_t addr = src;
207 
208 	if (!ept)
209 		return RPMSG_ERR_PARAM;
210 
211 	metal_mutex_acquire(&rdev->lock);
212 	if (src != RPMSG_ADDR_ANY) {
213 		status = rpmsg_is_address_set(rdev->bitmap,
214 					      RPMSG_ADDR_BMP_SIZE, src);
215 		if (!status) {
216 			/* Mark the address as used in the address bitmap. */
217 			rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
218 					  src);
219 		} else if (status > 0) {
220 			status = RPMSG_SUCCESS;
221 			goto ret_status;
222 		} else {
223 			goto ret_status;
224 		}
225 	} else {
226 		addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE);
227 	}
228 
229 	rpmsg_init_ept(ept, name, addr, dest, cb, unbind_cb);
230 
231 	status = rpmsg_register_endpoint(rdev, ept);
232 	if (status < 0)
233 		rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr);
234 
235 	if (!status  && ept->dest_addr == RPMSG_ADDR_ANY) {
236 		/* Send NS announcement to remote processor */
237 		metal_mutex_release(&rdev->lock);
238 		status = rpmsg_send_ns_message(ept, RPMSG_NS_CREATE);
239 		metal_mutex_acquire(&rdev->lock);
240 		if (status)
241 			rpmsg_unregister_endpoint(ept);
242 	}
243 
244 ret_status:
245 	metal_mutex_release(&rdev->lock);
246 	return status;
247 }
248 
249 /**
250  * rpmsg_destroy_ept
251  *
252  * This function deletes rpmsg endpoint and performs cleanup.
253  *
254  * @param ept - pointer to endpoint to destroy
255  *
256  */
rpmsg_destroy_ept(struct rpmsg_endpoint * ept)257 void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
258 {
259 	struct rpmsg_device *rdev;
260 
261 	if (!ept)
262 		return;
263 
264 	rdev = ept->rdev;
265 	if (ept->addr != RPMSG_NS_EPT_ADDR)
266 		(void)rpmsg_send_ns_message(ept, RPMSG_NS_DESTROY);
267 	metal_mutex_acquire(&rdev->lock);
268 	rpmsg_unregister_endpoint(ept);
269 	metal_mutex_release(&rdev->lock);
270 }
271