]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - processor-sdk/open-amp.git/blob - lib/rpmsg/remote_device.c
9ddfa93c0edb0718e3d254ef5e68a3de23c1492a
[processor-sdk/open-amp.git] / lib / rpmsg / remote_device.c
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"
51 #include "openamp/remoteproc.h"
52 #include "metal/utilities.h"
53 #include "metal/alloc.h"
54 #include "metal/atomic.h"
55 #include "metal/cpu.h"
57 /* Macro to initialize vring HW info */
58 #define INIT_VRING_ALLOC_INFO(ring_info,vring_hw)                             \
59                          (ring_info).vaddr  = (vring_hw).vaddr;               \
60                          (ring_info).align     = (vring_hw).align;             \
61                          (ring_info).num_descs = (vring_hw).num_descs
63 /* Local functions */
64 static int rpmsg_rdev_init_channels(struct remote_device *rdev);
66 /* Ops table for virtio device */
67 virtio_dispatch rpmsg_rdev_config_ops = {
68         rpmsg_rdev_create_virtqueues,
69         rpmsg_rdev_get_status,
70         rpmsg_rdev_set_status,
71         rpmsg_rdev_get_feature,
72         rpmsg_rdev_set_feature,
73         rpmsg_rdev_negotiate_feature,
74         rpmsg_rdev_read_config,
75         rpmsg_rdev_write_config,
76         rpmsg_rdev_reset
77 };
79 /**
80  * rpmsg_rdev_init
81  *
82  * This function creates and initializes the remote device. The remote device
83  * encapsulates virtio device.
84  *
85  * @param proc              - pointer to hil_proc
86  * @param rdev              - pointer to newly created remote device
87  * @param role              - role of the other device, Master or Remote
88  * @param channel_created   - callback function for channel creation
89  * @param channel_destroyed - callback function for channel deletion
90  * @param default_cb        - default callback for channel
91  *
92  * @return - status of function execution
93  *
94  */
95 int rpmsg_rdev_init(struct hil_proc *proc,
96                     struct remote_device **rdev, int role,
97                     rpmsg_chnl_cb_t channel_created,
98                     rpmsg_chnl_cb_t channel_destroyed, rpmsg_rx_cb_t default_cb)
99 {
101         struct remote_device *rdev_loc;
102         struct virtio_device *virt_dev;
103         struct proc_shm *shm;
104         int status;
106         if (!proc)
107                 return RPMSG_ERR_PARAM;
108         /* Initialize HIL data structures for given device */
109         if (hil_init_proc(proc))
110                 return RPMSG_ERR_DEV_INIT;
112         /* Create software representation of remote processor. */
113         rdev_loc = (struct remote_device *)metal_allocate_memory(sizeof(struct remote_device));
115         if (!rdev_loc) {
116                 return RPMSG_ERR_NO_MEM;
117         }
119         memset(rdev_loc, 0x00, sizeof(struct remote_device));
120         metal_mutex_init(&rdev_loc->lock);
122         rdev_loc->proc = proc;
123         rdev_loc->role = role;
124         rdev_loc->channel_created = channel_created;
125         rdev_loc->channel_destroyed = channel_destroyed;
126         rdev_loc->default_cb = default_cb;
128         /* Restrict the ept address - zero address can't be assigned */
129         rdev_loc->bitmap[0] = 1;
131         /* Initialize the virtio device */
132         virt_dev = &rdev_loc->virt_dev;
133         virt_dev->device = proc;
134         virt_dev->func = &rpmsg_rdev_config_ops;
135         if (virt_dev->func->set_features != RPMSG_NULL) {
136                 virt_dev->func->set_features(virt_dev, proc->vdev.dfeatures);
137         }
139         if (rdev_loc->role == RPMSG_REMOTE) {
140                 /*
141                  * Since device is RPMSG Remote so we need to manage the
142                  * shared buffers. Create shared memory pool to handle buffers.
143                  */
144                 shm = hil_get_shm_info(proc);
145                 rdev_loc->mem_pool =
146                     sh_mem_create_pool(shm->start_addr, shm->size,
147                                        RPMSG_BUFFER_SIZE);
149                 if (!rdev_loc->mem_pool) {
150                         return RPMSG_ERR_NO_MEM;
151                 }
152         }
154         if (!rpmsg_rdev_remote_ready(rdev_loc))
155                 return RPMSG_ERR_DEV_INIT;
157         /* Initialize endpoints list */
158         metal_list_init(&rdev_loc->rp_endpoints);
160         /* Initialize channels for RPMSG Remote */
161         status = rpmsg_rdev_init_channels(rdev_loc);
163         if (status != RPMSG_SUCCESS) {
164                 return status;
165         }
167         *rdev = rdev_loc;
169         return RPMSG_SUCCESS;
172 /**
173  * rpmsg_rdev_deinit
174  *
175  * This function un-initializes the remote device.
176  *
177  * @param rdev - pointer to remote device to deinit.
178  *
179  * @return - none
180  *
181  */
182 void rpmsg_rdev_deinit(struct remote_device *rdev)
184         struct metal_list *node;
185         struct rpmsg_channel *rp_chnl;
186         struct rpmsg_endpoint *rp_ept;
189         while(!metal_list_is_empty(&rdev->rp_channels)) {
190                 node = rdev->rp_channels.next;
191                 rp_chnl = metal_container_of(node, struct rpmsg_channel, node);
193                 if (rdev->channel_destroyed) {
194                         rdev->channel_destroyed(rp_chnl);
195                 }
197                 if ((rdev->support_ns) && (rdev->role == RPMSG_MASTER)) {
198                         rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY);
199                 }
201                 /* Delete default endpoint for channel */
202                 if (rp_chnl->rp_ept) {
203                         rpmsg_destroy_ept(rp_chnl->rp_ept);
204                 }
206                 _rpmsg_delete_channel(rp_chnl);
207         }
209         /* Delete name service endpoint */
210         metal_mutex_acquire(&rdev->lock);
211         rp_ept = rpmsg_rdev_get_endpoint_from_addr(rdev, RPMSG_NS_EPT_ADDR);
212         metal_mutex_release(&rdev->lock);
213         if (rp_ept) {
214                 _destroy_endpoint(rdev, rp_ept);
215         }
217         if (rdev->rvq) {
218                 virtqueue_free(rdev->rvq);
219         }
220         if (rdev->tvq) {
221                 virtqueue_free(rdev->tvq);
222         }
223         if (rdev->mem_pool) {
224                 sh_mem_delete_pool(rdev->mem_pool);
225         }
226         metal_mutex_deinit(&rdev->lock);
227         if (rdev->proc) {
228                 hil_delete_proc(rdev->proc);
229                 rdev->proc = 0;
230         }
232         metal_free_memory(rdev);
235 /**
236  * rpmsg_rdev_get_chnl_from_id
237  *
238  * This function returns channel node based on channel name. It must be called
239  * with mutex locked.
240  *
241  * @param stack      - pointer to remote device
242  * @param rp_chnl_id - rpmsg channel name
243  *
244  * @return - rpmsg channel
245  *
246  */
247 struct rpmsg_channel *rpmsg_rdev_get_chnl_from_id(struct remote_device *rdev,
248                                                char *rp_chnl_id)
250         struct rpmsg_channel *rp_chnl;
251         struct metal_list *node;
253         metal_list_for_each(&rdev->rp_channels, node) {
254                 rp_chnl = metal_container_of(node, struct rpmsg_channel, node);
255                 if (strncmp
256                     (rp_chnl->name, rp_chnl_id, sizeof(rp_chnl->name))
257                     == 0) {
258                         return rp_chnl;
259                 }
260         }
262         return RPMSG_NULL;
265 /**
266  * rpmsg_rdev_get_endpoint_from_addr
267  *
268  * This function returns endpoint node based on src address. It must be called
269  * with mutex locked.
270  *
271  * @param rdev - pointer remote device control block
272  * @param addr - src address
273  *
274  * @return - rpmsg endpoint
275  *
276  */
277 struct rpmsg_endpoint *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev,
278                                                 unsigned long addr)
280         struct rpmsg_endpoint *rp_ept;
281         struct metal_list *node;
283         metal_list_for_each(&rdev->rp_endpoints, node) {
284                 rp_ept = metal_container_of(node,
285                                 struct rpmsg_endpoint, node);
286                 if (rp_ept->addr == addr) {
287                         return rp_ept;
288                 }
289         }
291         return RPMSG_NULL;
294 /*
295  * rpmsg_rdev_notify
296  *
297  * This function checks whether remote device is up or not. If it is up then
298  * notification is sent based on device role to start IPC.
299  *
300  * @param rdev - pointer to remote device
301  *
302  * @return - status of function execution
303  *
304  */
305 int rpmsg_rdev_notify(struct remote_device *rdev)
307         int status = RPMSG_SUCCESS;
309         if (rdev->role == RPMSG_REMOTE) {
310                 status = hil_get_status(rdev->proc);
312                 /*
313                  * Let the remote device know that Master is ready for
314                  * communication.
315                  */
316                 if (!status)
317                         virtqueue_kick(rdev->rvq);
319         } else {
320                 status = hil_set_status(rdev->proc);
321         }
323         if (status == RPMSG_SUCCESS) {
324                 rdev->state = RPMSG_DEV_STATE_ACTIVE;
325         }
327         return status;
330 /**
331  * rpmsg_rdev_init_channels
332  *
333  * This function is only applicable to RPMSG remote. It obtains channel IDs
334  * from the HIL and creates RPMSG channels corresponding to each ID.
335  *
336  * @param rdev - pointer to remote device
337  *
338  * @return  - status of function execution
339  *
340  */
341 int rpmsg_rdev_init_channels(struct remote_device *rdev)
343         struct rpmsg_channel *rp_chnl;
344         struct proc_chnl *chnl_info;
345         int num_chnls, idx;
347         metal_list_init(&rdev->rp_channels);
348         if (rdev->role == RPMSG_MASTER) {
350                 chnl_info = hil_get_chnl_info(rdev->proc, &num_chnls);
351                 for (idx = 0; idx < num_chnls; idx++) {
353                         rp_chnl =
354                             _rpmsg_create_channel(rdev, chnl_info[idx].name,
355                                                   0x00, RPMSG_NS_EPT_ADDR);
356                         if (!rp_chnl) {
357                                 return RPMSG_ERR_NO_MEM;
358                         }
360                         rp_chnl->rp_ept =
361                             rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev,
362                                              RPMSG_ADDR_ANY);
364                         if (!rp_chnl->rp_ept) {
365                                 return RPMSG_ERR_NO_MEM;
366                         }
368                         rp_chnl->src = rp_chnl->rp_ept->addr;
369                 }
370         }
372         return RPMSG_SUCCESS;
375 /**
376  * check if the remote is ready to start RPMsg communication
377  */
378 int rpmsg_rdev_remote_ready(struct remote_device *rdev)
380         struct virtio_device *vdev = &rdev->virt_dev;
381         uint8_t status;
382         if (rdev->role == RPMSG_MASTER) {
383                 while (1) {
384                         /* Busy wait until the remote is ready */
385                         status = vdev->func->get_status(vdev);
386                         if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
387                                 return true;
388                         metal_cpu_yield();
389                 }
390         } else {
391                 return true;
392         }
393         /* Never come here */
394         return false;
397 static void rpmsg_memset_io(struct metal_io_region *io, void *dst, int c, size_t count)
399         if ((io->mem_flags & METAL_IO_MAPPED)) {
400                 metal_memset_io(dst, c, count);
401         } else {
402                 memset(dst, c, count);
403         }
406 /**
407  *------------------------------------------------------------------------
408  * The rest of the file implements the virtio device interface as defined
409  * by the virtio.h file.
410  *------------------------------------------------------------------------
411  */
412 int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs,
413                                  const char *names[], vq_callback * callbacks[],
414                                  struct virtqueue *vqs_[])
416         struct remote_device *rdev;
417         struct vring_alloc_info ring_info;
418         struct virtqueue *vqs[RPMSG_MAX_VQ_PER_RDEV];
419         struct proc_vring *vring_table;
420         void *buffer;
421         struct metal_sg sg;
422         int idx, num_vrings, status;
424         (void)flags;
425         (void)vqs_;
427         rdev = (struct remote_device *)dev;
429         /* Get the vring HW info for the given virtio device */
430         vring_table = hil_get_vring_info(&rdev->proc->vdev, &num_vrings);
432         if (num_vrings > nvqs) {
433                 return RPMSG_ERR_MAX_VQ;
434         }
436         /* Create virtqueue for each vring. */
437         for (idx = 0; idx < num_vrings; idx++) {
439                 INIT_VRING_ALLOC_INFO(ring_info, vring_table[idx]);
441                 if (rdev->role == RPMSG_REMOTE) {
442                         rpmsg_memset_io(vring_table[idx].io, (void *)ring_info.vaddr, 0x00,
443                                vring_size(vring_table[idx].num_descs, vring_table[idx].align));
444                 }
446                 status =
447                     virtqueue_create(dev, idx, (char *)names[idx], &ring_info,
448                                      callbacks[idx], hil_vring_notify,
449                                     rdev->proc->sh_buff.io,
450                                      &vqs[idx]);
452                 if (status != RPMSG_SUCCESS) {
453                         return status;
454                 }
455         }
457         //FIXME - a better way to handle this , tx for master is rx for remote and vice versa.
458         if (rdev->role == RPMSG_MASTER) {
459                 rdev->tvq = vqs[0];
460                 rdev->rvq = vqs[1];
461         } else {
462                 rdev->tvq = vqs[1];
463                 rdev->rvq = vqs[0];
464         }
466         if (rdev->role == RPMSG_REMOTE) {
467                 sg.io = rdev->proc->sh_buff.io;
468                 sg.len = RPMSG_BUFFER_SIZE;
469                 for (idx = 0; ((idx < rdev->rvq->vq_nentries)
470                                && (idx < rdev->mem_pool->total_buffs / 2));
471                      idx++) {
473                         /* Initialize TX virtqueue buffers for remote device */
474                         buffer = sh_mem_get_buffer(rdev->mem_pool);
476                         if (!buffer) {
477                                 return RPMSG_ERR_NO_BUFF;
478                         }
480                         sg.virt = buffer;
482                         rpmsg_memset_io(sg.io, buffer, 0x00, RPMSG_BUFFER_SIZE);
483                         status =
484                             virtqueue_add_buffer(rdev->rvq, &sg, 0, 1,
485                                                  buffer);
487                         if (status != RPMSG_SUCCESS) {
488                                 return status;
489                         }
490                 }
491         }
493         return RPMSG_SUCCESS;
496 unsigned char rpmsg_rdev_get_status(struct virtio_device *dev)
498         struct hil_proc *proc = dev->device;
499         struct proc_vdev *pvdev = &proc->vdev;
500         struct fw_rsc_vdev *vdev_rsc = pvdev->vdev_info;
502         if (!vdev_rsc)
503                 return -1;
505         return vdev_rsc->status;
508 void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status)
510         struct hil_proc *proc = dev->device;
511         struct proc_vdev *pvdev = &proc->vdev;
512         struct fw_rsc_vdev *vdev_rsc = pvdev->vdev_info;
514         if (!vdev_rsc)
515                 return;
517         vdev_rsc->status = status;
519         atomic_thread_fence(memory_order_seq_cst);
522 uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev)
524         return dev->features;
527 void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature)
529         dev->features |= feature;
532 uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev,
533                                       uint32_t features)
535         (void)dev;
536         (void)features;
538         return 0;
541 /*
542  * Read/write a variable amount from the device specific (ie, network)
543  * configuration region. This region is encoded in the same endian as
544  * the guest.
545  */
546 void rpmsg_rdev_read_config(struct virtio_device *dev, uint32_t offset,
547                             void *dst, int length)
549         (void)dev;
550         (void)offset;
551         (void)dst;
552         (void)length;
554         return;
557 void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset,
558                              void *src, int length)
560         (void)dev;
561         (void)offset;
562         (void)src;
563         (void)length;
565         return;
568 void rpmsg_rdev_reset(struct virtio_device *dev)
570         (void)dev;
572         return;