5152b19a97c4de6b70fd8899fe887c919225beab
1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 * Copyright (c) 2015 Xilinx, Inc. All rights reserved.
5 * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
32 /**************************************************************************
33 * FILE NAME
34 *
35 * remote_device.c
36 *
37 * COMPONENT
38 *
39 * OpenAMP Stack
40 *
41 * DESCRIPTION
42 *
43 * This file provides services to manage the remote devices.It also implements
44 * the interface defined by the virtio and provides few other utility functions.
45 *
46 *
47 **************************************************************************/
49 #include <string.h>
50 #include "openamp/rpmsg.h"
52 /* Macro to initialize vring HW info */
53 #define INIT_VRING_ALLOC_INFO(ring_info,vring_hw) \
54 (ring_info).phy_addr = (vring_hw).phy_addr; \
55 (ring_info).align = (vring_hw).align; \
56 (ring_info).num_descs = (vring_hw).num_descs
58 /* Local functions */
59 static int rpmsg_rdev_init_channels(struct remote_device *rdev);
61 /* Ops table for virtio device */
62 virtio_dispatch rpmsg_rdev_config_ops = {
63 rpmsg_rdev_create_virtqueues,
64 rpmsg_rdev_get_status,
65 rpmsg_rdev_set_status,
66 rpmsg_rdev_get_feature,
67 rpmsg_rdev_set_feature,
68 rpmsg_rdev_negotiate_feature,
69 rpmsg_rdev_read_config,
70 rpmsg_rdev_write_config,
71 rpmsg_rdev_reset
72 };
74 /**
75 * rpmsg_rdev_init
76 *
77 * This function creates and initializes the remote device. The remote device
78 * encapsulates virtio device.
79 *
80 * @param rdev - pointer to newly created remote device
81 * @param dev-id - ID of device to create , remote cpu id
82 * @param role - role of the other device, Master or Remote
83 * @param channel_created - callback function for channel creation
84 * @param channel_destroyed - callback function for channel deletion
85 * @param default_cb - default callback for channel
86 *
87 * @return - status of function execution
88 *
89 */
90 int rpmsg_rdev_init(struct remote_device **rdev, int dev_id, int role,
91 rpmsg_chnl_cb_t channel_created,
92 rpmsg_chnl_cb_t channel_destroyed, rpmsg_rx_cb_t default_cb)
93 {
95 struct remote_device *rdev_loc;
96 struct virtio_device *virt_dev;
97 struct hil_proc *proc;
98 struct proc_shm *shm;
99 int status;
101 /* Initialize HIL data structures for given device */
102 proc = hil_create_proc(dev_id);
104 if (!proc) {
105 return RPMSG_ERR_DEV_ID;
106 }
108 /* Create software representation of remote processor. */
109 rdev_loc =
110 (struct remote_device *)
111 env_allocate_memory(sizeof(struct remote_device));
113 if (!rdev_loc) {
114 return RPMSG_ERR_NO_MEM;
115 }
117 memset(rdev_loc, 0x00, sizeof(struct remote_device));
118 metal_mutex_init(&rdev_loc->lock);
120 rdev_loc->proc = proc;
121 rdev_loc->role = role;
122 rdev_loc->channel_created = channel_created;
123 rdev_loc->channel_destroyed = channel_destroyed;
124 rdev_loc->default_cb = default_cb;
126 /* Restrict the ept address - zero address can't be assigned */
127 rdev_loc->bitmap[0] = 1;
129 /* Initialize the virtio device */
130 virt_dev = &rdev_loc->virt_dev;
131 virt_dev->device = proc;
132 virt_dev->func = &rpmsg_rdev_config_ops;
133 if (virt_dev->func->set_features != RPMSG_NULL) {
134 virt_dev->func->set_features(virt_dev, proc->vdev.dfeatures);
135 }
137 if (rdev_loc->role == RPMSG_REMOTE) {
138 /*
139 * Since device is RPMSG Remote so we need to manage the
140 * shared buffers. Create shared memory pool to handle buffers.
141 */
142 shm = hil_get_shm_info(proc);
143 rdev_loc->mem_pool =
144 sh_mem_create_pool(shm->start_addr, shm->size,
145 RPMSG_BUFFER_SIZE);
147 if (!rdev_loc->mem_pool) {
148 return RPMSG_ERR_NO_MEM;
149 }
150 }
152 /* Initialize channels for RPMSG Remote */
153 status = rpmsg_rdev_init_channels(rdev_loc);
155 if (status != RPMSG_SUCCESS) {
156 return status;
157 }
159 *rdev = rdev_loc;
161 return RPMSG_SUCCESS;
162 }
164 /**
165 * rpmsg_rdev_deinit
166 *
167 * This function un-initializes the remote device.
168 *
169 * @param rdev - pointer to remote device to deinit.
170 *
171 * @return - none
172 *
173 */
174 void rpmsg_rdev_deinit(struct remote_device *rdev)
175 {
176 struct llist *rp_chnl_head, *rp_chnl_temp, *node;
177 struct rpmsg_channel *rp_chnl;
179 rp_chnl_head = rdev->rp_channels;
181 while (rp_chnl_head != RPMSG_NULL) {
183 rp_chnl_temp = rp_chnl_head->next;
184 rp_chnl = (struct rpmsg_channel *)rp_chnl_head->data;
186 if (rdev->channel_destroyed) {
187 rdev->channel_destroyed(rp_chnl);
188 }
190 if ((rdev->support_ns) && (rdev->role == RPMSG_MASTER)) {
191 rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY);
192 }
194 /* Delete default endpoint for channel */
195 if (rp_chnl->rp_ept) {
196 rpmsg_destroy_ept(rp_chnl->rp_ept);
197 }
199 _rpmsg_delete_channel(rp_chnl);
200 rp_chnl_head = rp_chnl_temp;
201 }
203 /* Delete name service endpoint */
205 metal_mutex_acquire(&rdev->lock);
206 node = rpmsg_rdev_get_endpoint_from_addr(rdev, RPMSG_NS_EPT_ADDR);
207 metal_mutex_release(&rdev->lock);
208 if (node) {
209 _destroy_endpoint(rdev, (struct rpmsg_endpoint *)node->data);
210 }
212 if (rdev->rvq) {
213 virtqueue_free(rdev->rvq);
214 }
215 if (rdev->tvq) {
216 virtqueue_free(rdev->tvq);
217 }
218 if (rdev->mem_pool) {
219 sh_mem_delete_pool(rdev->mem_pool);
220 }
221 metal_mutex_deinit(&rdev->lock);
222 if (rdev->proc) {
223 hil_delete_proc(rdev->proc);
224 rdev->proc = 0;
225 }
227 env_free_memory(rdev);
228 }
230 /**
231 * rpmsg_rdev_get_chnl_node_from_id
232 *
233 * This function returns channel node based on channel name. It must be called
234 * with mutex locked.
235 *
236 * @param stack - pointer to remote device
237 * @param rp_chnl_id - rpmsg channel name
238 *
239 * @return - channel node
240 *
241 */
242 struct llist *rpmsg_rdev_get_chnl_node_from_id(struct remote_device *rdev,
243 char *rp_chnl_id)
244 {
245 struct rpmsg_channel *rp_chnl;
246 struct llist *rp_chnl_head;
248 rp_chnl_head = rdev->rp_channels;
250 while (rp_chnl_head) {
251 rp_chnl = (struct rpmsg_channel *)rp_chnl_head->data;
252 if (strncmp(rp_chnl->name, rp_chnl_id, sizeof(rp_chnl->name))
253 == 0) {
254 return rp_chnl_head;
255 }
256 rp_chnl_head = rp_chnl_head->next;
257 }
259 return RPMSG_NULL;
260 }
262 /**
263 * rpmsg_rdev_get_endpoint_from_addr
264 *
265 * This function returns endpoint node based on src address. It must be called
266 * with mutex locked.
267 *
268 * @param rdev - pointer remote device control block
269 * @param addr - src address
270 *
271 * @return - endpoint node
272 *
273 */
274 struct llist *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev,
275 unsigned long addr)
276 {
277 struct llist *rp_ept_lut_head;
279 rp_ept_lut_head = rdev->rp_endpoints;
281 while (rp_ept_lut_head) {
282 struct rpmsg_endpoint *rp_ept =
283 (struct rpmsg_endpoint *)rp_ept_lut_head->data;
284 if (rp_ept->addr == addr) {
285 return rp_ept_lut_head;
286 }
287 rp_ept_lut_head = rp_ept_lut_head->next;
288 }
290 return RPMSG_NULL;
291 }
293 /*
294 * rpmsg_rdev_notify
295 *
296 * This function checks whether remote device is up or not. If it is up then
297 * notification is sent based on device role to start IPC.
298 *
299 * @param rdev - pointer to remote device
300 *
301 * @return - status of function execution
302 *
303 */
304 int rpmsg_rdev_notify(struct remote_device *rdev)
305 {
306 int status = RPMSG_SUCCESS;
308 if (rdev->role == RPMSG_REMOTE) {
309 status = hil_get_status(rdev->proc);
311 /*
312 * Let the remote device know that Master is ready for
313 * communication.
314 */
315 if (!status)
316 virtqueue_kick(rdev->rvq);
318 } else {
319 status = hil_set_status(rdev->proc);
320 }
322 if (status == RPMSG_SUCCESS) {
323 rdev->state = RPMSG_DEV_STATE_ACTIVE;
324 }
326 return status;
327 }
329 /**
330 * rpmsg_rdev_init_channels
331 *
332 * This function is only applicable to RPMSG remote. It obtains channel IDs
333 * from the HIL and creates RPMSG channels corresponding to each ID.
334 *
335 * @param rdev - pointer to remote device
336 *
337 * @return - status of function execution
338 *
339 */
340 int rpmsg_rdev_init_channels(struct remote_device *rdev)
341 {
342 struct rpmsg_channel *rp_chnl;
343 struct proc_chnl *chnl_info;
344 int num_chnls, idx;
346 if (rdev->role == RPMSG_MASTER) {
348 chnl_info = hil_get_chnl_info(rdev->proc, &num_chnls);
349 for (idx = 0; idx < num_chnls; idx++) {
351 rp_chnl =
352 _rpmsg_create_channel(rdev, chnl_info[idx].name,
353 0x00, RPMSG_NS_EPT_ADDR);
354 if (!rp_chnl) {
355 return RPMSG_ERR_NO_MEM;
356 }
358 rp_chnl->rp_ept =
359 rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev,
360 RPMSG_ADDR_ANY);
362 if (!rp_chnl->rp_ept) {
363 return RPMSG_ERR_NO_MEM;
364 }
366 rp_chnl->src = rp_chnl->rp_ept->addr;
367 }
368 }
370 return RPMSG_SUCCESS;
371 }
373 /**
374 *------------------------------------------------------------------------
375 * The rest of the file implements the virtio device interface as defined
376 * by the virtio.h file.
377 *------------------------------------------------------------------------
378 */
379 int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs,
380 const char *names[], vq_callback * callbacks[],
381 struct virtqueue *vqs_[])
382 {
383 struct remote_device *rdev;
384 struct vring_alloc_info ring_info;
385 struct virtqueue *vqs[RPMSG_MAX_VQ_PER_RDEV];
386 struct proc_vring *vring_table;
387 void *buffer;
388 struct llist node;
389 int idx, num_vrings, status;
391 (void)flags;
392 (void)vqs_;
394 rdev = (struct remote_device *)dev;
396 /* Get the vring HW info for the given virtio device */
397 vring_table = hil_get_vring_info(&rdev->proc->vdev, &num_vrings);
399 if (num_vrings > nvqs) {
400 return RPMSG_ERR_MAX_VQ;
401 }
403 /* Create virtqueue for each vring. */
404 for (idx = 0; idx < num_vrings; idx++) {
406 INIT_VRING_ALLOC_INFO(ring_info, vring_table[idx]);
408 if (rdev->role == RPMSG_REMOTE) {
409 memset((void *)ring_info.phy_addr, 0x00,
410 vring_size(vring_table[idx].num_descs, vring_table[idx].align));
411 }
413 status =
414 virtqueue_create(dev, idx, (char *)names[idx], &ring_info,
415 callbacks[idx], hil_vring_notify,
416 &vqs[idx]);
418 if (status != RPMSG_SUCCESS) {
419 return status;
420 }
421 }
423 //FIXME - a better way to handle this , tx for master is rx for remote and vice versa.
424 if (rdev->role == RPMSG_MASTER) {
425 rdev->tvq = vqs[0];
426 rdev->rvq = vqs[1];
427 } else {
428 rdev->tvq = vqs[1];
429 rdev->rvq = vqs[0];
430 }
432 if (rdev->role == RPMSG_REMOTE) {
433 for (idx = 0; ((idx < rdev->rvq->vq_nentries)
434 && (idx < rdev->mem_pool->total_buffs / 2));
435 idx++) {
437 /* Initialize TX virtqueue buffers for remote device */
438 buffer = sh_mem_get_buffer(rdev->mem_pool);
440 if (!buffer) {
441 return RPMSG_ERR_NO_BUFF;
442 }
444 node.data = buffer;
445 node.attr = RPMSG_BUFFER_SIZE;
446 node.next = RPMSG_NULL;
448 memset(buffer, 0x00, RPMSG_BUFFER_SIZE);
449 status =
450 virtqueue_add_buffer(rdev->rvq, &node, 0, 1,
451 buffer);
453 if (status != RPMSG_SUCCESS) {
454 return status;
455 }
456 }
457 }
459 return RPMSG_SUCCESS;
460 }
462 unsigned char rpmsg_rdev_get_status(struct virtio_device *dev)
463 {
464 (void)dev;
465 return 0;
466 }
468 void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status)
469 {
470 (void)dev;
471 (void)status;
472 }
474 uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev)
475 {
476 return dev->features;
477 }
479 void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature)
480 {
481 dev->features |= feature;
482 }
484 uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev,
485 uint32_t features)
486 {
487 (void)dev;
488 (void)features;
490 return 0;
491 }
493 /*
494 * Read/write a variable amount from the device specific (ie, network)
495 * configuration region. This region is encoded in the same endian as
496 * the guest.
497 */
498 void rpmsg_rdev_read_config(struct virtio_device *dev, uint32_t offset,
499 void *dst, int length)
500 {
501 (void)dev;
502 (void)offset;
503 (void)dst;
504 (void)length;
506 return;
507 }
509 void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset,
510 void *src, int length)
511 {
512 (void)dev;
513 (void)offset;
514 (void)src;
515 (void)length;
517 return;
518 }
520 void rpmsg_rdev_reset(struct virtio_device *dev)
521 {
522 (void)dev;
524 return;
525 }