]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - processor-sdk/open-amp.git/blob - lib/rpmsg/remote_device.c
5152b19a97c4de6b70fd8899fe887c919225beab
[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"
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;
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)
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);
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)
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;
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)
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;
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)
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;
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)
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;
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_[])
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;
462 unsigned char rpmsg_rdev_get_status(struct virtio_device *dev)
464         (void)dev;
465         return 0;
468 void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status)
470         (void)dev;
471         (void)status;
474 uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev)
476         return dev->features;
479 void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature)
481         dev->features |= feature;
484 uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev,
485                                       uint32_t features)
487         (void)dev;
488         (void)features;
490         return 0;
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)
501         (void)dev;
502         (void)offset;
503         (void)dst;
504         (void)length;
506         return;
509 void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset,
510                              void *src, int length)
512         (void)dev;
513         (void)offset;
514         (void)src;
515         (void)length;
517         return;
520 void rpmsg_rdev_reset(struct virtio_device *dev)
522         (void)dev;
524         return;