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