1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
31 /**************************************************************************
32 * FILE NAME
33 *
34 * rpmsg.c
35 *
36 * COMPONENT
37 *
38 * OpenAMP stack.
39 *
40 * DESCRIPTION
41 *
42 * Main file for the RPMSG driver. This file implements APIs as defined by
43 * RPMSG documentation(Linux docs) and also provides some utility functions.
44 *
45 * RPMSG driver represents each processor/core to which it communicates with
46 * remote_device control block.
47 * Each remote device(processor) defines its role in the communication i.e
48 * whether it is RPMSG Master or Remote. If the device(processor) to which
49 * driver is talking is RPMSG master then RPMSG driver implicitly behaves as
50 * Remote and vice versa.
51 * RPMSG Master is responsible for initiating communications with the Remote
52 * and shared buffers management. Terms remote device/core/proc are used
53 * interchangeably for the processor to which RPMSG driver is communicating
54 * irrespective of the fact whether it is RPMSG Remote or Master.
55 *
56 **************************************************************************/
57 #include "openamp/rpmsg.h"
59 /**
60 * rpmsg_init
61 *
62 * Thus function allocates and initializes the rpmsg driver resources for
63 * given device ID(cpu id). The successful return from this function leaves
64 * fully enabled IPC link.
65 *
66 * @param dev_id - remote device for which driver is to
67 * be initialized
68 * @param rdev - pointer to newly created remote device
69 * @param channel_created - callback function for channel creation
70 * @param channel_destroyed - callback function for channel deletion
71 * @param default_cb - default callback for channel I/O
72 * @param role - role of the other device, Master or Remote
73 *
74 * @return - status of function execution
75 *
76 */
78 int rpmsg_init(int dev_id, struct remote_device **rdev,
79 rpmsg_chnl_cb_t channel_created,
80 rpmsg_chnl_cb_t channel_destroyed,
81 rpmsg_rx_cb_t default_cb, int role)
82 {
83 int status;
85 /* Initialize IPC environment */
86 status = env_init();
87 if (status == RPMSG_SUCCESS) {
88 /* Initialize the remote device for given cpu id */
89 status = rpmsg_rdev_init(rdev, dev_id, role, channel_created,
90 channel_destroyed, default_cb);
91 if (status == RPMSG_SUCCESS) {
92 /* Kick off IPC with the remote device */
93 status = rpmsg_start_ipc(*rdev);
94 }
95 }
97 /* Deinit system in case of error */
98 if (status != RPMSG_SUCCESS) {
99 rpmsg_deinit(*rdev);
100 }
102 return status;
103 }
105 /**
106 * rpmsg_deinit
107 *
108 * Thus function frees rpmsg driver resources for given remote device.
109 *
110 * @param rdev - pointer to device to de-init
111 *
112 */
114 void rpmsg_deinit(struct remote_device *rdev)
115 {
116 if (rdev) {
117 rpmsg_rdev_deinit(rdev);
118 env_deinit();
119 }
120 }
122 /**
123 * This function sends rpmsg "message" to remote device.
124 *
125 * @param rp_chnl - pointer to rpmsg channel
126 * @param src - source address of channel
127 * @param dst - destination address of channel
128 * @param data - data to transmit
129 * @param size - size of data
130 * @param wait - boolean, wait or not for buffer to become
131 * available
132 *
133 * @return - status of function execution
134 *
135 */
137 int rpmsg_send_offchannel_raw(struct rpmsg_channel *rp_chnl, uint32_t src,
138 uint32_t dst, char *data, int size, int wait)
139 {
140 struct remote_device *rdev;
141 struct rpmsg_hdr *rp_hdr;
142 void *buffer;
143 unsigned short idx;
144 int tick_count = 0;
145 unsigned long buff_len;
146 int ret;
148 if (!rp_chnl || !data) {
149 return RPMSG_ERR_PARAM;
150 }
152 /* Get the associated remote device for channel. */
153 rdev = rp_chnl->rdev;
155 /* Validate device state */
156 if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE
157 || rdev->state != RPMSG_DEV_STATE_ACTIVE) {
158 return RPMSG_ERR_DEV_STATE;
159 }
161 if (size > (rpmsg_get_buffer_size(rp_chnl))) {
162 return RPMSG_ERR_BUFF_SIZE;
163 }
165 /* Lock the device to enable exclusive access to virtqueues */
166 env_lock_mutex(rdev->lock);
167 /* Get rpmsg buffer for sending message. */
168 buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx);
169 /* Unlock the device */
170 env_unlock_mutex(rdev->lock);
172 if (!buffer && !wait) {
173 return RPMSG_ERR_NO_BUFF;
174 }
176 while (!buffer) {
177 /*
178 * Wait parameter is true - pool the buffer for
179 * 15 secs as defined by the APIs.
180 */
181 env_sleep_msec(RPMSG_TICKS_PER_INTERVAL);
182 env_lock_mutex(rdev->lock);
183 buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx);
184 env_unlock_mutex(rdev->lock);
185 tick_count += RPMSG_TICKS_PER_INTERVAL;
186 if (!buffer && (tick_count >=
187 (RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL))) {
188 return RPMSG_ERR_NO_BUFF;
189 }
190 }
192 rp_hdr = (struct rpmsg_hdr *)buffer;
194 /* Initialize RPMSG header. */
195 rp_hdr->dst = dst;
196 rp_hdr->src = src;
197 rp_hdr->len = size;
199 /* Copy data to rpmsg buffer. */
200 env_memcpy((void*)RPMSG_LOCATE_DATA(rp_hdr), data, size);
202 env_lock_mutex(rdev->lock);
204 /* Enqueue buffer on virtqueue. */
205 ret = rpmsg_enqueue_buffer(rdev, buffer, buff_len, idx);
206 env_assert(ret == VQUEUE_SUCCESS, "FATAL: RPMSG failed to enqueue buffer.\n");
207 /* Let the other side know that there is a job to process. */
208 virtqueue_kick(rdev->tvq);
210 env_unlock_mutex(rdev->lock);
212 return RPMSG_SUCCESS;
213 }
215 /**
216 * rpmsg_get_buffer_size
217 *
218 * Returns buffer size available for sending messages.
219 *
220 * @param channel - pointer to rpmsg channel
221 *
222 * @return - buffer size
223 *
224 */
225 int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl)
226 {
227 struct remote_device *rdev;
228 int length;
230 /* Get associated remote device for channel. */
231 rdev = rp_chnl->rdev;
233 env_lock_mutex(rdev->lock);
235 if (rdev->role == RPMSG_REMOTE) {
236 /*
237 * If device role is Remote then buffers are provided by us
238 * (RPMSG Master), so just provide the macro.
239 */
240 length = RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr);
241 } else {
242 /*
243 * If other core is Master then buffers are provided by it,
244 * so get the buffer size from the virtqueue.
245 */
246 length =
247 (int)virtqueue_get_desc_size(rdev->tvq) -
248 sizeof(struct rpmsg_hdr);
249 }
251 env_unlock_mutex(rdev->lock);
253 return length;
254 }
256 /**
257 * rpmsg_create_ept
258 *
259 * This function creates rpmsg endpoint for the rpmsg channel.
260 *
261 * @param channel - pointer to rpmsg channel
262 * @param cb - Rx completion call back
263 * @param priv - private data
264 * @param addr - endpoint src address
265 *
266 * @return - pointer to endpoint control block
267 *
268 */
269 struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rp_chnl,
270 rpmsg_rx_cb_t cb, void *priv,
271 uint32_t addr)
272 {
274 struct remote_device *rdev = RPMSG_NULL;
275 struct rpmsg_endpoint *rp_ept = RPMSG_NULL;
277 if (!rp_chnl || !cb) {
278 return RPMSG_NULL;
279 }
281 rdev = rp_chnl->rdev;
283 rp_ept = _create_endpoint(rdev, cb, priv, addr);
285 if (rp_ept) {
286 rp_ept->rp_chnl = rp_chnl;
287 }
289 return rp_ept;
290 }
292 /**
293 * rpmsg_destroy_ept
294 *
295 * This function deletes rpmsg endpoint and performs cleanup.
296 *
297 * @param rp_ept - pointer to endpoint to destroy
298 *
299 */
300 void rpmsg_destroy_ept(struct rpmsg_endpoint *rp_ept)
301 {
303 struct remote_device *rdev;
304 struct rpmsg_channel *rp_chnl;
306 if (!rp_ept)
307 return;
309 rp_chnl = rp_ept->rp_chnl;
310 rdev = rp_chnl->rdev;
312 _destroy_endpoint(rdev, rp_ept);
313 }
315 /**
316 * rpmsg_create_channel
317 *
318 * This function provides facility to create channel dynamically. It sends
319 * Name Service announcement to remote device to let it know about the channel
320 * creation. There must be an active communication among the cores (or atleast
321 * one rpmsg channel must already exist) before using this API to create new
322 * channels.
323 *
324 * @param rdev - pointer to remote device
325 * @param name - channel name
326 *
327 * @return - pointer to new rpmsg channel
328 *
329 */
330 struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev,
331 char *name)
332 {
334 struct rpmsg_channel *rp_chnl;
335 struct rpmsg_endpoint *rp_ept;
337 if (!rdev || !name) {
338 return RPMSG_NULL;
339 }
341 /* Create channel instance */
342 rp_chnl = _rpmsg_create_channel(rdev, name, RPMSG_NS_EPT_ADDR,
343 RPMSG_NS_EPT_ADDR);
344 if (!rp_chnl) {
345 return RPMSG_NULL;
346 }
348 /* Create default endpoint for the channel */
349 rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev,
350 RPMSG_ADDR_ANY);
352 if (!rp_ept) {
353 _rpmsg_delete_channel(rp_chnl);
354 return RPMSG_NULL;
355 }
357 rp_chnl->rp_ept = rp_ept;
358 rp_chnl->src = rp_ept->addr;
359 rp_chnl->state = RPMSG_CHNL_STATE_NS;
361 /* Notify the application of channel creation event */
362 if (rdev->channel_created) {
363 rdev->channel_created(rp_chnl);
364 }
366 /* Send NS announcement to remote processor */
367 rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE);
369 return rp_chnl;
370 }
372 /**
373 * rpmsg_delete_channel
374 *
375 * Deletes the given RPMSG channel. The channel must first be created with the
376 * rpmsg_create_channel API.
377 *
378 * @param rp_chnl - pointer to rpmsg channel to delete
379 *
380 */
381 void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl)
382 {
384 struct remote_device *rdev;
386 if (!rp_chnl) {
387 return;
388 }
390 rdev = rp_chnl->rdev;
392 if (rp_chnl->state > RPMSG_CHNL_STATE_IDLE) {
393 /* Notify the other processor that channel no longer exists */
394 rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY);
395 }
397 /* Notify channel deletion to application */
398 if (rdev->channel_destroyed) {
399 rdev->channel_destroyed(rp_chnl);
400 }
402 rpmsg_destroy_ept(rp_chnl->rp_ept);
403 _rpmsg_delete_channel(rp_chnl);
405 return;
406 }