diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2016-01-12 14:06:54 -0800 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2016-01-12 14:06:54 -0800 |
commit | a57cc2c988482010061b9e68344fdf1969889763 (patch) | |
tree | 5c050337492ce27c09b47421b123980b5a79f8d9 /middleware/multicore/open-amp/rpmsg/remote_device.c |
initial commit, FreeRTOS_BSP_1.0.0_iMX7D
Diffstat (limited to 'middleware/multicore/open-amp/rpmsg/remote_device.c')
-rw-r--r-- | middleware/multicore/open-amp/rpmsg/remote_device.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/middleware/multicore/open-amp/rpmsg/remote_device.c b/middleware/multicore/open-amp/rpmsg/remote_device.c new file mode 100644 index 0000000..f07faab --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/remote_device.c @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * remote_device.c + * + * COMPONENT + * + * OpenAMP Stack + * + * DESCRIPTION + * + * This file provides services to manage the remote devices.It also implements + * the interface defined by the virtio and provides few other utility functions. + * + * + **************************************************************************/ + +#include "rpmsg.h" + +/* Macro to initialize vring HW info */ +#define INIT_VRING_ALLOC_INFO(ring_info,vring_hw) \ + (ring_info).phy_addr = (vring_hw).phy_addr; \ + (ring_info).align = (vring_hw).align; \ + (ring_info).num_descs = (vring_hw).num_descs + +/* Local functions */ +static int rpmsg_rdev_init_channels(struct remote_device *rdev); + +/* Ops table for virtio device */ +virtio_dispatch rpmsg_rdev_config_ops = +{ + rpmsg_rdev_create_virtqueues, + rpmsg_rdev_get_status, + rpmsg_rdev_set_status, + rpmsg_rdev_get_feature, + rpmsg_rdev_set_feature, + rpmsg_rdev_negotiate_feature, + rpmsg_rdev_read_config, + rpmsg_rdev_write_config, + rpmsg_rdev_reset +}; + +/** + * rpmsg_rdev_init + * + * This function creates and initializes the remote device. The remote device + * encapsulates virtio device. + * + * @param rdev - pointer to newly created remote device + * @param dev-id - ID of device to create , remote cpu id + * @param role - role of the other device, Master or Remote + * @param channel_created - callback function for channel creation + * @param channel_destroyed - callback function for channel deletion + * @param default_cb - default callback for channel + * + * @return - status of function execution + * + */ +int rpmsg_rdev_init(struct remote_device **rdev, int dev_id, int role, + rpmsg_chnl_cb_t channel_created, + rpmsg_chnl_cb_t channel_destroyed, + rpmsg_rx_cb_t default_cb) { + + struct remote_device *rdev_loc; + struct virtio_device *virt_dev; + struct hil_proc *proc; + struct proc_shm *shm; + int status; + + /* Initialize HIL data structures for given device */ + proc = hil_create_proc(dev_id); + + if (!proc) { + return RPMSG_ERR_DEV_ID; + } + + /* Create software representation of remote processor. */ + rdev_loc = (struct remote_device *) env_allocate_memory( + sizeof(struct remote_device)); + + if (!rdev_loc) { + return RPMSG_ERR_NO_MEM; + } + + env_memset(rdev_loc, 0x00, sizeof(struct remote_device)); + status = env_create_mutex(&rdev_loc->lock, 1); + + if (status != RPMSG_SUCCESS) { + + /* Cleanup required in case of error is performed by caller */ + return status; + } + + rdev_loc->proc = proc; + rdev_loc->role = role; + rdev_loc->channel_created = channel_created; + rdev_loc->channel_destroyed = channel_destroyed; + rdev_loc->default_cb = default_cb; + + /* Initialize the virtio device */ + virt_dev = &rdev_loc->virt_dev; + virt_dev->device = proc; + virt_dev->func = &rpmsg_rdev_config_ops; + if (virt_dev->func->set_features != RPMSG_NULL) { + virt_dev->func->set_features(virt_dev, proc->vdev.dfeatures); /* rpmsg_rdev_set_feature */ + } + + /* + * Linux don't use this way to manage memory pool + */ + if (rdev_loc->role == RPMSG_REMOTE) { + /* + * Since device is RPMSG Remote so we need to manage the + * shared buffers. Create shared memory pool to handle buffers. + */ + shm = hil_get_shm_info(proc); /*proc_table*/ + rdev_loc->mem_pool = sh_mem_create_pool(shm->start_addr, shm->size, + RPMSG_BUFFER_SIZE); + + if (!rdev_loc->mem_pool) { + return RPMSG_ERR_NO_MEM; + } + } + + /* Initialize channels for RPMSG Remote */ + status = rpmsg_rdev_init_channels(rdev_loc); + + if (status != RPMSG_SUCCESS) { + return status; + } + + *rdev = rdev_loc; + + return RPMSG_SUCCESS; +} + +/** + * rpmsg_rdev_deinit + * + * This function un-initializes the remote device. + * + * @param rdev - pointer to remote device to deinit. + * + * @return - none + * + */ +void rpmsg_rdev_deinit(struct remote_device *rdev) { + struct llist *rp_chnl_head, *rp_chnl_temp, *node; + struct rpmsg_channel *rp_chnl; + + rp_chnl_head = rdev->rp_channels; + + while (rp_chnl_head != RPMSG_NULL ) { + + rp_chnl_temp = rp_chnl_head->next; + rp_chnl = (struct rpmsg_channel *) rp_chnl_head->data; + + if (rdev->channel_destroyed) { + rdev->channel_destroyed(rp_chnl); + } + + if ((rdev->support_ns) && (rdev->role == RPMSG_MASTER)) { + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY); + } + + /* Delete default endpoint for channel */ + if (rp_chnl->rp_ept) { + rpmsg_destroy_ept(rp_chnl->rp_ept); + } + + _rpmsg_delete_channel(rp_chnl); + rp_chnl_head = rp_chnl_temp; + } + + /* Delete name service endpoint */ + node = rpmsg_rdev_get_endpoint_from_addr(rdev,RPMSG_NS_EPT_ADDR); + if (node) { + _destroy_endpoint(rdev, (struct rpmsg_endpoint *) node->data); + } + + if (rdev->rvq) { + virtqueue_free(rdev->rvq); + } + if (rdev->tvq) { + virtqueue_free(rdev->tvq); + } + if (rdev->mem_pool) { + sh_mem_delete_pool(rdev->mem_pool); + } + if (rdev->lock) { + env_delete_mutex(rdev->lock); + } + + env_free_memory(rdev); +} + +/** + * rpmsg_rdev_get_chnl_node_from_id + * + * This function returns channel node based on channel name. + * + * @param stack - pointer to remote device + * @param rp_chnl_id - rpmsg channel name + * + * @return - channel node + * + */ +struct llist *rpmsg_rdev_get_chnl_node_from_id(struct remote_device *rdev, + char *rp_chnl_id) { + struct rpmsg_channel *rp_chnl; + struct llist *rp_chnl_head; + + rp_chnl_head = rdev->rp_channels; + + env_lock_mutex(rdev->lock); + while (rp_chnl_head) { + rp_chnl = (struct rpmsg_channel *) rp_chnl_head->data; + if (env_strncmp(rp_chnl->name, rp_chnl_id, sizeof(rp_chnl->name)) + == 0) { + env_unlock_mutex(rdev->lock); + return rp_chnl_head; + } + rp_chnl_head = rp_chnl_head->next; + } + env_unlock_mutex(rdev->lock); + + return RPMSG_NULL ; +} + +/** + * rpmsg_rdev_get_chnl_from_addr + * + * This function returns channel node based on src/dst address. + * + * @param rdev - pointer remote device control block + * @param addr - src/dst address + * + * @return - channel node + * + */ +struct llist *rpmsg_rdev_get_chnl_from_addr(struct remote_device *rdev, + unsigned long addr) { + struct rpmsg_channel *rp_chnl; + struct llist *rp_chnl_head; + + rp_chnl_head = rdev->rp_channels; + + env_lock_mutex(rdev->lock); + while (rp_chnl_head) { + rp_chnl = (struct rpmsg_channel *) rp_chnl_head->data; + if ((rp_chnl->src == addr) || (rp_chnl->dst == addr)) { + env_unlock_mutex(rdev->lock); + return rp_chnl_head; + } + rp_chnl_head = rp_chnl_head->next; + } + env_unlock_mutex(rdev->lock); + + return RPMSG_NULL ; +} + +/** + * rpmsg_rdev_get_endpoint_from_addr + * + * This function returns endpoint node based on src address. + * + * @param rdev - pointer remote device control block + * @param addr - src address + * + * @return - endpoint node + * + */ +struct llist *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev, + unsigned long addr) { + struct llist *rp_ept_lut_head; + + rp_ept_lut_head = rdev->rp_endpoints; + + env_lock_mutex(rdev->lock); + while (rp_ept_lut_head) { + struct rpmsg_endpoint *rp_ept = + (struct rpmsg_endpoint *) rp_ept_lut_head->data; + if (rp_ept->addr == addr) { + env_unlock_mutex(rdev->lock); + return rp_ept_lut_head; + } + rp_ept_lut_head = rp_ept_lut_head->next; + } + env_unlock_mutex(rdev->lock); + + return RPMSG_NULL ; +} +/* + * rpmsg_rdev_notify + * + * This function checks whether remote device is up or not. If it is up then + * notification is sent based on device role to start IPC. + * + * @param rdev - pointer to remote device + * + * @return - status of function execution + * + */ +int rpmsg_rdev_notify(struct remote_device *rdev) { + int status = RPMSG_SUCCESS; + + if (rdev->role == RPMSG_REMOTE) { + status = hil_get_status(rdev->proc); + + /* + * Let the remote device know that Master is ready for + * communication. + */ + if (!status) + virtqueue_kick(rdev->rvq); /*will triggle rpmsg_tx_callback in the REMOTE side*/ + + } else { + status = hil_set_status(rdev->proc); + } + + if (status == RPMSG_SUCCESS) { + rdev->state = RPMSG_DEV_STATE_ACTIVE; + } + + return status; +} +/** + * rpmsg_rdev_init_channels + * + * This function is only applicable to RPMSG remote. It obtains channel IDs + * from the HIL and creates RPMSG channels corresponding to each ID. + * + * @param rdev - pointer to remote device + * + * @return - status of function execution + * + */ +int rpmsg_rdev_init_channels(struct remote_device *rdev) { + struct rpmsg_channel *rp_chnl; + struct proc_chnl *chnl_info; + int num_chnls, idx; + + if (rdev->role == RPMSG_MASTER) { + + chnl_info = hil_get_chnl_info(rdev->proc, &num_chnls); /*proc_table*/ + for (idx = 0; idx < num_chnls; idx++) { + + rp_chnl = _rpmsg_create_channel(rdev, chnl_info[idx].name, 0x00, + RPMSG_NS_EPT_ADDR); /*the channel is put to "rp_channels" field of remote_device data structure*/ + if (!rp_chnl) { + return RPMSG_ERR_NO_MEM; + } + + rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, + RPMSG_ADDR_ANY); /*the endpoint is put ot "rp_endpoints" field of remote_device data structure*/ + + if (!rp_chnl->rp_ept) { + return RPMSG_ERR_NO_MEM; + } + + rp_chnl->src = rp_chnl->rp_ept->addr; /*channel source is the default endpoint address, destination is NS endpoint address*/ + + } + } + + return RPMSG_SUCCESS; +} + +/** + *------------------------------------------------------------------------ + * The rest of the file implements the virtio device interface as defined + * by the virtio.h file. + *------------------------------------------------------------------------ + */ +int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, // invoked by virtio_device->create_virtqueues + const char *names[], vq_callback *callbacks[], + struct virtqueue *vqs_[]) { + struct remote_device *rdev; + struct vring_alloc_info ring_info; + struct virtqueue *vqs[RPMSG_MAX_VQ_PER_RDEV]; + struct proc_vring *vring_table; + void *buffer; + struct llist node; + int idx, num_vrings, status; + + rdev = (struct remote_device*) dev; + + /* Get the vring HW info for the given virtio device */ + vring_table = hil_get_vring_info(&rdev->proc->vdev, + &num_vrings); + + + if (num_vrings > nvqs) { + return RPMSG_ERR_MAX_VQ; + } + + /* Create virtqueue for each vring. */ + for (idx = 0; idx < num_vrings; idx++) { + + INIT_VRING_ALLOC_INFO( ring_info, vring_table[idx]); /*ring_info comes from proc_table*/ + + /* + * - phy_addr + * - align + * - num_descs + */ + + if (rdev->role == RPMSG_REMOTE) { + env_memset((void*) ring_info.phy_addr, 0x00, + vring_size(vring_table[idx].num_descs, + vring_table[idx].align)); + } + + /* + * created virtqueue should be in vqs[0], vqs[1] + */ + status = virtqueue_create(dev, idx, (char *) names[idx], &ring_info, + callbacks[idx], hil_vring_notify, + &vqs[idx]); + + if (status != RPMSG_SUCCESS) { + return status; + } + } + + //FIXME - a better way to handle this , tx for master is rx for remote and vice versa. + /* + * MASTER REMOTE + * vqs[0] RX TX + * vqs[1] TX RX + */ + if (rdev->role == RPMSG_MASTER) { + rdev->tvq = vqs[0]; + rdev->rvq = vqs[1]; + } else { + rdev->tvq = vqs[1]; + rdev->rvq = vqs[0]; + } + + if (rdev->role == RPMSG_REMOTE) { + for (idx = 0; ((idx < rdev->rvq->vq_nentries) + && (idx < rdev->mem_pool->total_buffs / 2)); + idx++) { + + + /* Initialize TX virtqueue buffers for remote device */ + buffer = sh_mem_get_buffer(rdev->mem_pool); + + if (!buffer) { + return RPMSG_ERR_NO_BUFF; + } + + node.data = buffer; + node.attr = RPMSG_BUFFER_SIZE; + node.next = RPMSG_NULL; + + env_memset(buffer, 0x00, RPMSG_BUFFER_SIZE); + status = virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); /* this is where vq_ring.avial.idx is updated by "vq_ring_update_avail"*/ + + if (status != RPMSG_SUCCESS) { + return status; + } + } + } + + return RPMSG_SUCCESS; +} + +unsigned char rpmsg_rdev_get_status(struct virtio_device *dev) { + return 0; +} + +void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status) { + +} + +uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev) { + return dev->features; +} + +void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature) { + dev->features |= feature; /*refer to name service endpoint creation logic in the caller rpmsg_start_ipc*/ +} + +uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev, + uint32_t features) { + return 0; +} +/* + * Read/write a variable amount from the device specific (ie, network) + * configuration region. This region is encoded in the same endian as + * the guest. + */ +void rpmsg_rdev_read_config(struct virtio_device *dev, uint32_t offset, + void *dst, int length) { + return; +} +void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset, + void *src, int length) { + return; +} +void rpmsg_rdev_reset(struct virtio_device *dev) { + return; +} |