From a57cc2c988482010061b9e68344fdf1969889763 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 12 Jan 2016 14:06:54 -0800 Subject: initial commit, FreeRTOS_BSP_1.0.0_iMX7D --- .../multicore/open-amp/rpmsg/remote_device.c | 534 ++++++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg.c | 424 +++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg.h | 411 +++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg_core.c | 796 +++++++++++++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg_core.h | 190 +++++ 5 files changed, 2355 insertions(+) create mode 100644 middleware/multicore/open-amp/rpmsg/remote_device.c create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg.c create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg.h create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg_core.c create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg_core.h (limited to 'middleware/multicore/open-amp/rpmsg') 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; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg.c b/middleware/multicore/open-amp/rpmsg/rpmsg.c new file mode 100644 index 0000000..66db1ad --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * 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 + * + * rpmsg.c + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * Main file for the RPMSG driver. This file implements APIs as defined by + * RPMSG documentation(Linux docs) and also provides some utility functions. + * + * RPMSG driver represents each processor/core to which it communicates with + * remote_device control block. + * Each remote device(processor) defines its role in the communication i.e + * whether it is RPMSG Master or Remote. If the device (processor) to which + * driver is talking is RPMSG master then RPMSG driver implicitly behaves as + * Remote and vice versa. + * RPMSG Master is responsible for initiating communications with the Remote + * and shared buffers management. Terms remote device/core/proc are used + * interchangeably for the processor to which RPMSG driver is communicating + * irrespective of the fact whether it is RPMSG Remote or Master. + * + **************************************************************************/ +#include "rpmsg.h" + +/** + * rpmsg_init + * + * Thus function allocates and initializes the rpmsg driver resources for + * given device ID(cpu id). The successful return from this function leaves + * fully enabled IPC link. + * + * @param dev_id - remote device for which driver is to + * be initialized + * @param rdev - pointer to newly created remote device + * @param channel_created - callback function for channel creation + * @param channel_destroyed - callback function for channel deletion + * @param default_cb - default callback for channel I/O + * @param role - role of the other device, Master or Remote + * + * @return - status of function execution + * + */ + +int rpmsg_init(int dev_id, struct remote_device **rdev, + rpmsg_chnl_cb_t channel_created, + rpmsg_chnl_cb_t channel_destroyed, + rpmsg_rx_cb_t default_cb, int role) { + int status; + + PRINTF("init M4 as %s\r\n", role?"REMOTE":"MASTER"); + /* Initialize IPC environment */ + status = env_init(); + if (status == RPMSG_SUCCESS) { + /* Initialize the remote device for given cpu id */ + status = rpmsg_rdev_init(rdev, dev_id, role, channel_created, + channel_destroyed, default_cb); + if (status == RPMSG_SUCCESS) { + /* Kick off IPC with the remote device */ + status = rpmsg_start_ipc(*rdev); + } + } + + /* Deinit system in case of error */ + if (status != RPMSG_SUCCESS) { + rpmsg_deinit(*rdev); + } + + return status; +} + +/** + * rpmsg_deinit + * + * Thus function frees rpmsg driver resources for given remote device. + * + * @param rdev - pointer to device to de-init + * + */ + +void rpmsg_deinit(struct remote_device *rdev) { + if (rdev) { + rpmsg_rdev_deinit(rdev); + env_deinit(); + } +} + +/** + * This function sends rpmsg "message" to remote device. + * + * @param rp_chnl - pointer to rpmsg channel + * @param src - source address of channel + * @param dst - destination address of channel + * @param data - data to transmit + * @param size - size of data + * @param wait - boolean, wait or not for buffer to become + * available + * + * @return - status of function execution + * + */ + +int rpmsg_send_offchannel_raw(struct rpmsg_channel *rp_chnl, unsigned long src, + unsigned long dst, char *data, int size, int wait) { + struct remote_device *rdev; + struct rpmsg_hdr *rp_hdr; + void *buffer; + int status = RPMSG_SUCCESS; + unsigned short idx; + int tick_count = 0; + int buff_len; + + if (!rp_chnl) { + return RPMSG_ERR_PARAM; + } + + /* Get the associated remote device for channel. */ + rdev = rp_chnl->rdev; + + /* Validate device state */ + if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE + || rdev->state != RPMSG_DEV_STATE_ACTIVE) { + return RPMSG_ERR_DEV_STATE; + } + + /* Lock the device to enable exclusive access to virtqueues */ + env_lock_mutex(rdev->lock); + /* Get rpmsg buffer for sending message. */ + buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx); + if (!buffer && !wait) { + status = RPMSG_ERR_NO_MEM; + } + env_unlock_mutex(rdev->lock); + + if (status == RPMSG_SUCCESS) { + + while (!buffer) { + /* + * Wait parameter is true - pool the buffer for + * 15 secs as defined by the APIs. + */ + env_sleep_msec(RPMSG_TICKS_PER_INTERVAL); + env_lock_mutex(rdev->lock); + buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx); + env_unlock_mutex(rdev->lock); + tick_count += RPMSG_TICKS_PER_INTERVAL; + if (tick_count >= (RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL)) { + status = RPMSG_ERR_NO_BUFF; + break; + } + } + + if (status == RPMSG_SUCCESS) { + //FIXME : may be just copy the data size equal to buffer length and Tx it. + if (size > (buff_len - sizeof(struct rpmsg_hdr))) + status = RPMSG_ERR_BUFF_SIZE; + + if (status == RPMSG_SUCCESS) { + rp_hdr = (struct rpmsg_hdr *) buffer; + + /* Initialize RPMSG header. */ + rp_hdr->dst = dst; + rp_hdr->src = src; + rp_hdr->len = size; + + /* Copy data to rpmsg buffer. */ + env_memcpy(rp_hdr->data, data, size); + + env_lock_mutex(rdev->lock); + /* Enqueue buffer on virtqueue. */ + status = rpmsg_enqueue_buffer(rdev, buffer, buff_len, idx); + if (status == RPMSG_SUCCESS) { + /* Let the other side know that there is a job to process. */ + virtqueue_kick(rdev->tvq); + } + env_unlock_mutex(rdev->lock); + } + + } + } + + /* Do cleanup in case of error.*/ + if (status != RPMSG_SUCCESS) { + rpmsg_free_buffer(rdev, buffer); + } + + return status; +} + +/** + * rpmsg_get_buffer_size + * + * Returns buffer size available for sending messages. + * + * @param channel - pointer to rpmsg channel + * + * @return - buffer size + * + */ +int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl) { + struct remote_device *rdev; + int length; + + if (!rp_chnl) { + return RPMSG_ERR_PARAM; + } + + /* Get associated remote device for channel. */ + rdev = rp_chnl->rdev; + + /* Validate device state */ + if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE + || rdev->state != RPMSG_DEV_STATE_ACTIVE) { + return RPMSG_ERR_DEV_STATE; + } + + env_lock_mutex(rdev->lock); + + if (rdev->role == RPMSG_REMOTE) { + /* + * If device role is Remote then buffers are provided by us + * (RPMSG Master), so just provide the macro. + */ + length = RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr); + } else { + /* + * If other core is Master then buffers are provided by it, + * so get the buffer size from the virtqueue. + */ + length = (int) virtqueue_get_desc_size(rdev->tvq) - sizeof(struct rpmsg_hdr); + } + + env_unlock_mutex(rdev->lock); + + return length; +} + +/** + * rpmsg_create_ept + * + * This function creates rpmsg endpoint for the rpmsg channel. + * + * @param channel - pointer to rpmsg channel + * @param cb - Rx completion call back + * @param priv - private data + * @param addr - endpoint src address + * + * @return - pointer to endpoint control block + * + */ +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rp_chnl, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr) { + + /* + * Note : When calling rpmsg_create_ept to a channel, the endpoint + * is put into info field of remote_device, not the channel + * + * + * CHANNEL ---> RDEV ---> CHANNELs + * + * EPT ---> RDEV ---> EPTs + */ + struct remote_device *rdev = RPMSG_NULL; + struct rpmsg_endpoint *rp_ept = RPMSG_NULL; + + if (!rp_chnl || !cb) { + return RPMSG_NULL ; + } + + rdev = rp_chnl->rdev; + + rp_ept = _create_endpoint(rdev, cb, priv, addr); + + if (rp_ept) { + rp_ept->rp_chnl = rp_chnl; + } + + return rp_ept; +} + +/** + * rpmsg_destroy_ept + * + * This function deletes rpmsg endpoint and performs cleanup. + * + * @param rp_ept - pointer to endpoint to destroy + * + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *rp_ept) { + + struct remote_device *rdev; + struct rpmsg_channel *rp_chnl; + + if (!rp_ept) + return; + + rp_chnl = rp_ept->rp_chnl; + rdev = rp_chnl->rdev; + + _destroy_endpoint(rdev, rp_ept); +} + +/** + * rpmsg_create_channel + * + * This function provides facility to create channel dynamically. It sends + * Name Service announcement to remote device to let it know about the channel + * creation. There must be an active communication among the cores (or atleast + * one rpmsg channel must already exist) before using this API to create new + * channels. + * + * @param rdev - pointer to remote device + * @param name - channel name + * + * @return - pointer to new rpmsg channel + * + */ +struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, + char *name) { + + struct rpmsg_channel *rp_chnl; + struct rpmsg_endpoint *rp_ept; + + if (!rdev || !name) { + return RPMSG_NULL ; + } + + /* Create channel instance */ + rp_chnl = _rpmsg_create_channel(rdev, name, RPMSG_NS_EPT_ADDR, + RPMSG_NS_EPT_ADDR); + if (!rp_chnl) { + return RPMSG_NULL ; + } + + /* Create default endpoint for the channel */ + rp_ept = rpmsg_create_ept(rp_chnl , rdev->default_cb, rdev, + RPMSG_ADDR_ANY); + + if (!rp_ept) { + _rpmsg_delete_channel(rp_chnl); + return RPMSG_NULL; + } + + rp_chnl->rp_ept = rp_ept; + rp_chnl->src = rp_ept->addr; + rp_chnl->state = RPMSG_CHNL_STATE_NS; + + /* Notify the application of channel creation event */ + if (rdev->channel_created) { + rdev->channel_created(rp_chnl); + } + + /* Send NS announcement to remote processor */ + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + + return rp_chnl; +} + +/** + * rpmsg_delete_channel + * + * Deletes the given RPMSG channel. The channel must first be created with the + * rpmsg_create_channel API. + * + * @param rp_chnl - pointer to rpmsg channel to delete + * + */ +void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl) { + + struct remote_device *rdev; + + if (!rp_chnl) { + return; + } + + rdev = rp_chnl->rdev; + + if (rp_chnl->state > RPMSG_CHNL_STATE_IDLE) { + /* Notify the other processor that channel no longer exists */ + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY); + } + + /* Notify channel deletion to application */ + if (rdev->channel_destroyed) { + rdev->channel_destroyed(rp_chnl); + } + + rpmsg_destroy_ept(rp_chnl->rp_ept); + _rpmsg_delete_channel(rp_chnl); + + return; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg.h b/middleware/multicore/open-amp/rpmsg/rpmsg.h new file mode 100644 index 0000000..746b792 --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg.h @@ -0,0 +1,411 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name Texas Instruments 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 + * OWNER 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. + */ + +#ifndef _RPMSG_H_ +#define _RPMSG_H_ + +#include "../rpmsg/rpmsg_core.h" + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ +#define RPMSG_NAME_SIZE 32 + +/** + * struct rpmsg_hdr - common header for all rpmsg messages + * @src: source address + * @dst: destination address + * @reserved: reserved for future use + * @len: length of payload (in bytes) + * @flags: message flags + * @data: @len bytes of message payload data + * + * Every message sent(/received) on the rpmsg bus begins with this header. + */ +struct rpmsg_hdr { + unsigned long src; + unsigned long dst; + unsigned long reserved; + unsigned short len; + unsigned short flags; + unsigned char data[0]; +} /*__attribute__((packed))*/; + +/** + * struct rpmsg_ns_msg - dynamic name service announcement message + * @name: name of remote service that is published + * @addr: address of remote service that is published + * @flags: indicates whether service is created or destroyed + * + * This message is sent across to publish a new service, or announce + * about its removal. When we receive these messages, an appropriate + * rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe() + * or ->remove() handler of the appropriate rpmsg driver will be invoked + * (if/as-soon-as one is registered). + */ +struct rpmsg_ns_msg { + char name[RPMSG_NAME_SIZE]; + unsigned long addr; + unsigned long flags; +} /*__attribute__((packed))*/; + +/** + * enum rpmsg_ns_flags - dynamic name service announcement flags + * + * @RPMSG_NS_CREATE: a new remote service was just created + * @RPMSG_NS_DESTROY: a known remote service was just destroyed + */ +enum rpmsg_ns_flags { + RPMSG_NS_CREATE = 0, + RPMSG_NS_DESTROY = 1, +}; + +#define RPMSG_ADDR_ANY 0xFFFFFFFF + + +/** + * rpmsg_channel - devices that belong to the rpmsg bus are called channels + * @name: channel name + * @src: local address + * @dst: destination address + * rdev: rpmsg remote device + * @ept: the rpmsg endpoint of this channel + * @state: channel state + */ +struct rpmsg_channel { + char name[RPMSG_NAME_SIZE]; + unsigned long src; + unsigned long dst; + struct remote_device *rdev; + struct rpmsg_endpoint *rp_ept; + unsigned int state; +}; + +/** + * channel_info - channel info + * @name: channel name + * @src: local address + * @dst: destination address + */ + +struct channel_info { + char name[RPMSG_NAME_SIZE]; + unsigned long src; + unsigned long dest; +}; + +/** + * struct rpmsg_endpoint - binds a local rpmsg address to its user + * @rp_chnl: rpmsg channel device + * @cb: rx callback handler + * @addr: local rpmsg address + * @priv: private data for the driver's use + * + * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as + * it binds an rpmsg address with an rx callback handler. + * + * Simple rpmsg drivers shouldn't use this struct directly, because + * things just work: every rpmsg driver provides an rx callback upon + * registering to the bus, and that callback is then bound to its rpmsg + * address when the driver is probed. When relevant inbound messages arrive + * (i.e. messages which their dst address equals to the src address of + * the rpmsg channel), the driver's handler is invoked to process it. + * + * More complicated drivers though, that do need to allocate additional rpmsg + * addresses, and bind them to different rx callbacks, must explicitly + * create additional endpoints by themselves (see rpmsg_create_ept()). + */ +struct rpmsg_endpoint { + struct rpmsg_channel *rp_chnl; + rpmsg_rx_cb_t cb; + unsigned long addr; + void *priv; +}; + +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rp_chnl, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr); + +void rpmsg_destroy_ept(struct rpmsg_endpoint *rp_ept); + +int +rpmsg_send_offchannel_raw(struct rpmsg_channel *, unsigned long, unsigned long, char *, int, int); + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, unsigned long dst) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, rpdev->src, dst, (char *)data, len, RPMSG_TRUE); +} + +/** + * rpmsg_send() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, rpdev->src, rpdev->dst, (char*)data, len, RPMSG_TRUE); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, unsigned long src, unsigned long dst, + void *data, int len) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, (char *)data, len, RPMSG_TRUE); +} + +/** + * rpmsg_trysend() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len) +{ + + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, rpdev->src, rpdev->dst, (char *)data, len, RPMSG_FALSE); +} + +/** + * rpmsg_trysendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, unsigned long dst) +{ + unsigned long src; + + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + src = rpdev->src; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, (char *)data, len, RPMSG_FALSE); +} + +/** + * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, unsigned long src, unsigned long dst, + void *data, int len) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, (char *)data, len, RPMSG_FALSE); +} + +/** + * rpmsg_init + * + * Thus function allocates and initializes the rpmsg driver resources for given + * device id (cpu id).The successful return from this function leaves + * fully enabled IPC link. + * + * @param dev_id - rpmsg remote device for which driver is to + * be initialized + * @param rdev - pointer to newly created remote device + * @param channel_created - callback function for channel creation + * @param channel_destroyed - callback function for channel deletion + * @default_cb - default callback for channel + * @param role - role of the other device, Master or Remote + * @return - status of function execution + * + */ + +int rpmsg_init(int dev_id, struct remote_device **rdev, + rpmsg_chnl_cb_t channel_created, + rpmsg_chnl_cb_t channel_destroyed, + rpmsg_rx_cb_t default_cb, int role); + +/** + * rpmsg_deinit + * + * Thus function releases the rpmsg driver resources for given remote + * instance. + * + * @param rdev - pointer to device de-init + * + * @return - none + * + */ +void rpmsg_deinit(struct remote_device *rdev); + +/** + * rpmsg_get_buffer_size + * + * Returns buffer size available for sending messages. + * + * @param channel - pointer to rpmsg channel/device + * + * @return - buffer size + * + */ +int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl); + +/** + * rpmsg_create_channel + * + * Creates RPMSG channel with the given name for remote device. + * + * @param rdev - pointer to rpmsg remote device + * @param name - channel name + * + * @return - pointer to new rpmsg channel + * + */ +struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, char *name); + +/** + * rpmsg_delete_channel + * + * Deletes the given RPMSG channel. The channel must first be created with the + * rpmsg_create_channel API. + * + * @param rp_chnl - pointer to rpmsg channel to delete + * + */ +void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl); + +/** + * + * rpmsg_handler + * + * Provide platform specific interrupt handler to application layer + * + */ +void rpmsg_handler(void); + +#endif /* _RPMSG_H_ */ diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_core.c b/middleware/multicore/open-amp/rpmsg/rpmsg_core.c new file mode 100644 index 0000000..c8cb82f --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_core.c @@ -0,0 +1,796 @@ +/* + * 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 + * + * rpmsg_core.c + * + * COMPONENT + * + * OpenAMP + * + * DESCRIPTION + * + * This file provides the core functionality of RPMSG messaging part like + * message parsing ,Rx/Tx callbacks handling , channel creation/deletion + * and address management. + * + * + **************************************************************************/ +#include "rpmsg.h" + +/* Internal functions */ +static void rpmsg_rx_callback(struct virtqueue *vq); +static void rpmsg_tx_callback(struct virtqueue *vq); + +/** + * rpmsg_start_ipc + * + * This function creates communication links(virtqueues) for remote device + * and notifies it to start IPC. + * + * @param rdev - remote device handle + * + * @return - status of function execution + * + */ +int rpmsg_start_ipc(struct remote_device *rdev) { + struct virtio_device *virt_dev; + struct rpmsg_endpoint *ns_ept; + void (*callback[2])(struct virtqueue *vq); + const char *vq_names[2]; + unsigned long dev_features; + int status; + struct virtqueue *vqs[2]; + int i; + + virt_dev = &rdev->virt_dev; + + /* Initialize names and callbacks based on the device role */ + /* + * virtqueue[0] virtqueue[1] + * MASTER "tx_vq", "rpmsg_tx_callback" "rx_vq", "rpmsg_rx_callback" + * + * REMOTE "rx_vq", "rpmsg_rx_callback" "tx_vq", "rpmsg_tx_callback" + */ + if (rdev->role == RPMSG_MASTER) { + vq_names[0] = "tx_vq"; + vq_names[1] = "rx_vq"; + callback[0] = rpmsg_tx_callback; + callback[1] = rpmsg_rx_callback; + } else { + vq_names[0] = "rx_vq"; + vq_names[1] = "tx_vq"; + callback[0] = rpmsg_rx_callback; + callback[1] = rpmsg_tx_callback; + } + + /* Create virtqueues for remote device */ + /* + * the 2 created virtqueues are assigned to rdev's tvq and rvq respectively + * regarding on the MATER/REMOTE role + * + * REMOTE: proc_table.vring_info[0] "tx_vq" "rpmsg_tx_callback" "rdev->tvq" -> "vqs[0]" + * proc_table.vring_info[1] "rx_vq" "rpmsg_rx_callback" "rdev->rvq" -> "vqs[1]" + * MASTER: proc_table.vring_info[0] "rx_vq" "rpmsg_rx_callback" "rdev->rvq" -> "vqs[0]" + * proc_table.vring_info[1] "tx_vq" "rpmsg_tx_callback" "rdev->tvq" -> "vqs[1]" + */ + status = virt_dev->func->create_virtqueues(virt_dev, 0, /*rpmsg_rdev_create_virtqueues*/ + RPMSG_MAX_VQ_PER_RDEV, vq_names, callback, RPMSG_NULL); + if (status != RPMSG_SUCCESS) { + return status; + } + + dev_features = virt_dev->func->get_features(virt_dev); /*rpmsg_rdev_get_feature*/ + + /* + * Create name service announcement endpoint if device supports name + * service announcement feature. + */ + if ((dev_features & (1<support_ns = RPMSG_TRUE; + ns_ept = _create_endpoint(rdev, rpmsg_ns_callback, rdev, /* Is this necessary for a remote? */ + RPMSG_NS_EPT_ADDR); + if (!ns_ept) { + return RPMSG_ERR_NO_MEM; + } + } + + /* Initialize notifications for vring. */ + if (rdev->role == RPMSG_MASTER) { + vqs[0] = rdev->tvq; + vqs[1] = rdev->rvq; + } else { + vqs[0] = rdev->rvq; + vqs[1] = rdev->tvq; + } + for(i = 0; i <= 1; i++) { + status = hil_enable_vring_notifications(i, vqs[i]); + if (status != RPMSG_SUCCESS) { + return status; + } + } + + status = rpmsg_rdev_notify(rdev); + + return status; +} + +/** + * _rpmsg_create_channel + * + * Creates new rpmsg channel with the given parameters. + * + * @param rdev - pointer to remote device which contains the channel + * @param name - name of the device + * @param src - source address for the rpmsg channel + * @param dst - destination address for the rpmsg channel + * + * @return - pointer to new rpmsg channel + * + */ +struct rpmsg_channel *_rpmsg_create_channel(struct remote_device *rdev, + char *name, unsigned long src, unsigned long dst) { + struct rpmsg_channel *rp_chnl; + struct llist *node; + + rp_chnl = env_allocate_memory(sizeof(struct rpmsg_channel)); + if (rp_chnl) { + env_memset(rp_chnl, 0x00, sizeof(struct rpmsg_channel)); + env_strncpy(rp_chnl->name, name, sizeof(rp_chnl->name)); + rp_chnl->src = src; + rp_chnl->dst = dst; + rp_chnl->rdev = rdev; + /* Place channel on channels list */ + node = env_allocate_memory(sizeof(struct llist)); + if (!node) { + env_free_memory(rp_chnl); + return RPMSG_NULL ; + } + node->data = rp_chnl; + env_lock_mutex(rdev->lock); + add_to_list(&rdev->rp_channels , node); + env_unlock_mutex(rdev->lock); + } + + return rp_chnl; +} + +/** + * _rpmsg_delete_channel + * + * Deletes given rpmsg channel. + * + * @param rp_chnl - pointer to rpmsg channel to delete + * + * return - none + */ +void _rpmsg_delete_channel(struct rpmsg_channel * rp_chnl) { + struct llist *node; + if (rp_chnl) { + node = rpmsg_rdev_get_chnl_node_from_id(rp_chnl->rdev, rp_chnl->name); + if (node) { + env_lock_mutex(rp_chnl->rdev->lock); + remove_from_list(&rp_chnl->rdev->rp_channels, node); + env_unlock_mutex(rp_chnl->rdev->lock); + env_free_memory(node); + } + env_free_memory(rp_chnl); + } +} + +/** + * _create_endpoint + * + * This function creates rpmsg endpoint. + * + * @param rdev - pointer to remote device + * @param cb - Rx completion call back + * @param priv - private data + * @param addr - endpoint src address + * + * @return - pointer to endpoint control block + * + */ +struct rpmsg_endpoint *_create_endpoint(struct remote_device *rdev, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr) { + + struct rpmsg_endpoint *rp_ept; + struct llist *node; + int status = RPMSG_SUCCESS; + + rp_ept = env_allocate_memory(sizeof(struct rpmsg_endpoint)); + if (!rp_ept) { + return RPMSG_NULL ; + } + + node = env_allocate_memory(sizeof(struct llist)); + if (!node) { + env_free_memory(rp_ept); + return RPMSG_NULL; + } + + env_lock_mutex(rdev->lock); + + if (addr != RPMSG_ADDR_ANY) { + /* + * Application has requested a particular src address for endpoint, + * first check if address is available. + */ + if (!rpmsg_is_address_set(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr)) { + /* Mark the address as used in the address bitmap. */ + rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr); + + } else { + status = RPMSG_ERR_DEV_ADDR; + } + } else { + addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE); + if (addr < 0) { + status = RPMSG_ERR_DEV_ADDR; + } + } + + /* Do cleanup in case of error and return */ + if (status) { + env_free_memory(node); + env_free_memory(rp_ept); + env_unlock_mutex(rdev->lock); + return RPMSG_NULL; + } + + rp_ept->addr = addr; + rp_ept->cb = cb; + rp_ept->priv = priv; + + node->data = rp_ept; + add_to_list(&rdev->rp_endpoints, node); + + env_unlock_mutex(rdev->lock); + + return rp_ept; +} + +/** + * rpmsg_destroy_ept + * + * This function deletes rpmsg endpoint and performs cleanup. + * + * @param rdev - pointer to remote device + * @param rp_ept - pointer to endpoint to destroy + * + */ +void _destroy_endpoint(struct remote_device *rdev, + struct rpmsg_endpoint *rp_ept) { + struct llist *node; + node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_ept->addr); + if (node) { + env_lock_mutex(rdev->lock); + rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, rp_ept->addr); + remove_from_list(&rdev->rp_endpoints, node); + env_unlock_mutex(rdev->lock); + env_free_memory(node); + } + env_free_memory(rp_ept); +} + +/** + * rpmsg_send_ns_message + * + * Sends name service announcement to remote device + * + * @param rdev - pointer to remote device + * @param rp_chnl - pointer to rpmsg channel + * @param flags - Channel creation/deletion flags + * + */ +void rpmsg_send_ns_message(struct remote_device *rdev, + struct rpmsg_channel *rp_chnl, unsigned long flags) { + + struct rpmsg_hdr *rp_hdr; + struct rpmsg_ns_msg *ns_msg; + unsigned short idx; + int len; + + env_lock_mutex(rdev->lock); + + /* Get Tx buffer. */ + rp_hdr = (struct rpmsg_hdr *) rpmsg_get_tx_buffer(rdev, &len, &idx); + if (!rp_hdr) + return; + + /* Fill out name service data. */ + rp_hdr->dst = RPMSG_NS_EPT_ADDR; + rp_hdr->len = sizeof(struct rpmsg_ns_msg); + ns_msg = (struct rpmsg_ns_msg *) rp_hdr->data; + env_strncpy(ns_msg->name, rp_chnl->name, sizeof(rp_chnl->name)); + ns_msg->flags = flags; + ns_msg->addr = rp_chnl->src; + + /* Place the buffer on virtqueue. */ + rpmsg_enqueue_buffer(rdev, rp_hdr, len, idx); + + /* Notify the other side that it has data to process. */ + virtqueue_kick(rdev->tvq); + + env_unlock_mutex(rdev->lock); +} + +/** + * rpmsg_enqueue_buffers + * + * Places buffer on the virtqueue for consumption by the other side. + * + * @param rdev - pointer to remote core + * @param buffer - buffer pointer + * @param len - buffer length + * @idx - buffer index + * + * @return - status of function execution + * + */ +int rpmsg_enqueue_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx) { + struct llist node; + int status; + + /* Initialize buffer node */ + node.data = buffer; + node.attr = len; + node.next = RPMSG_NULL; + node.prev = RPMSG_NULL; + + if (rdev->role == RPMSG_REMOTE) { + /*MASTER*/ + status = virtqueue_add_buffer(rdev->tvq, &node, 0, 1, buffer); + } else { + /*REMOTE*/ + status = virtqueue_add_consumed_buffer(rdev->tvq, idx, len); + } + + return status; +} + +/** + * rpmsg_return_buffer + * + * Places the used buffer back on the virtqueue. + * + * @param rdev - pointer to remote core + * @param buffer - buffer pointer + * @param len - buffer length + * @param idx - buffer index + * + */ +void rpmsg_return_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx) { + struct llist node; + + /* Initialize buffer node */ + node.data = buffer; + node.attr = len; + node.next = RPMSG_NULL; + node.prev = RPMSG_NULL; + + if (rdev->role == RPMSG_REMOTE) { + /*master*/ + virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); + } else { + /*remote*/ + virtqueue_add_consumed_buffer(rdev->rvq, idx, len); + } +} + +/** + * rpmsg_get_tx_buffer + * + * Provides buffer to transmit messages. + * + * @param rdev - pointer to remote device + * @param len - length of returned buffer + * @param idx - buffer index + * + * return - pointer to buffer. + */ +void *rpmsg_get_tx_buffer(struct remote_device *rdev, int *len, + unsigned short *idx) { + void *data; + + if (rdev->role == RPMSG_REMOTE) { + /* MASTER */ + data = virtqueue_get_buffer(rdev->tvq, (uint32_t *) len); + if (data == RPMSG_NULL) { + /*Here is why Master don't need to pre link memory to vring*/ + data = sh_mem_get_buffer(rdev->mem_pool); + *len = RPMSG_BUFFER_SIZE; + } + } else { + /* REMOTE */ + data = virtqueue_get_available_buffer(rdev->tvq, idx, + (uint32_t *) len); + } + return ((void *) env_map_vatopa(data)); +} + +/** + * rpmsg_get_rx_buffer + * + * Retrieves the received buffer from the virtqueue. + * + * @param rdev - pointer to remote device + * @param len - size of received buffer + * @param idx - index of buffer + * + * @return - pointer to received buffer + * + */ +void *rpmsg_get_rx_buffer(struct remote_device *rdev, unsigned long *len, + unsigned short *idx) { + + void *data; + if (rdev->role == RPMSG_REMOTE) { + /*MASTER*/ + data = virtqueue_get_buffer(rdev->rvq, (uint32_t *)len); + } else { + /*REMOTE*/ + data = virtqueue_get_available_buffer(rdev->rvq, idx, (uint32_t *)len); + } + return ((void *) env_map_vatopa(data)); +} + +/** + * rpmsg_free_buffer + * + * Frees the allocated buffers. + * + * @param rdev - pointer to remote device + * @param buffer - pointer to buffer to free + * + */ +void rpmsg_free_buffer(struct remote_device *rdev, void *buffer) { + if (rdev->role == RPMSG_REMOTE) { + sh_mem_free_buffer(rdev->mem_pool, buffer); + } +} + +/** + * rpmsg_tx_callback + * + * Tx callback function. + * + * @param vq - pointer to virtqueue on which Tx is has been + * completed. + * + */ +static void rpmsg_tx_callback(struct virtqueue *vq) { + struct remote_device *rdev; + struct virtio_device *vdev; + struct rpmsg_channel *rp_chnl; + struct llist *chnl_hd; + + vdev = (struct virtio_device *) vq->vq_dev; + rdev = (struct remote_device *) vdev; + chnl_hd = rdev->rp_channels; + + /* Check if the remote device is master. */ + if (rdev->role == RPMSG_MASTER) { + /* Notification is received from the master. Now the remote(us) can + * performs one of two operations; + * + * a. If name service announcement is supported then it will send NS message. + * else + * b. It will update the channel state to active so that further communication + * can take place. + */ + while (chnl_hd != RPMSG_NULL) { + rp_chnl = (struct rpmsg_channel *) chnl_hd->data; + + if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { + + if (rdev->support_ns) { + rp_chnl->state = RPMSG_CHNL_STATE_NS; + } else { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + } + + if (rp_chnl->state == RPMSG_CHNL_STATE_NS) { + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + } + } + + chnl_hd = chnl_hd->next; + } + } +} + +/** + * rpmsg_rx_callback + * + * Rx callback function. + * + * @param vq - pointer to virtqueue on which messages is received + * + */ +void rpmsg_rx_callback(struct virtqueue *vq) { + struct remote_device *rdev; + struct virtio_device *vdev; + struct rpmsg_channel *rp_chnl; + struct rpmsg_endpoint *rp_ept; + struct rpmsg_hdr *rp_hdr; + struct llist *node; + unsigned long len; + unsigned short idx; + struct llist *chnl_hd; + + vdev = (struct virtio_device *) vq->vq_dev; + rdev = (struct remote_device *) vdev; + + chnl_hd = rdev->rp_channels; + if ((chnl_hd != RPMSG_NULL) && (rdev->role == RPMSG_MASTER)) { + rp_chnl = (struct rpmsg_channel *) chnl_hd->data; + if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { + if (rdev->support_ns) { + rp_chnl->state = RPMSG_CHNL_STATE_NS; + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + } else { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + } + return; + } + } + + env_lock_mutex(rdev->lock); + + /* Process the received data from remote node */ + rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); + + env_unlock_mutex(rdev->lock); + + while(rp_hdr) { + + /* Get the channel node from the remote device channels list. */ + node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_hdr->dst); /*in the "rp_endpoints" list, find the node whose addr equal"rp_hdr->dst"*/ + + if (!node) + /* Fatal error no endpoint for the given dst addr. */ + return; + + rp_ept = (struct rpmsg_endpoint *) node->data; + + rp_chnl = rp_ept->rp_chnl; + + /* + * Linux will not send the null message, so the first message not only + * update the state machine, and the callback is called as well + */ + if ((rp_chnl) && (rp_chnl->state == RPMSG_CHNL_STATE_NS)) { + /* First message from RPMSG Master, update channel + * destination address and state */ + /* + * Only for Remote + */ + rp_chnl->dst = rp_hdr->src; + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + + /* Notify channel creation to application */ + if (rdev->channel_created) { + rdev->channel_created(rp_chnl); /* assigned by rpmsg_rdev_init*/ + } + } + + rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, rp_ept->priv, // for NS message, this will triggle rpmsg_ns_callback /*not the case*/ + rp_hdr->src); // for none NS message, this will triggle APP registered callback "rpmsg_read_cb" + + env_lock_mutex(rdev->lock); + + /* Return used buffers. */ + rpmsg_return_buffer(rdev, rp_hdr, len, idx); + + rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); + env_unlock_mutex(rdev->lock); + } +} + +/** + * rpmsg_ns_callback + * + * This callback handles name service announcement from the remote device + * and creates/deletes rpmsg channels. + * + * @param server_chnl - pointer to server channel control block. + * @param data - pointer to received messages + * @param len - length of received data + * @param priv - any private data + * @param src - source address + * + * @return - none + */ +void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, void *data, int len, + void *priv, unsigned long src) { + struct remote_device *rdev; + struct rpmsg_channel *rp_chnl; + struct rpmsg_ns_msg *ns_msg; + struct llist *node; + + rdev = (struct remote_device *) priv; + + //FIXME: This assumes same name string size for channel name both on master + //and remote. If this is not the case then we will have to parse the + //message contents. + + ns_msg = (struct rpmsg_ns_msg *) data; + ns_msg->name[len - 1] = '\0'; + + if (ns_msg->flags & RPMSG_NS_DESTROY) { + node = rpmsg_rdev_get_chnl_node_from_id(rdev, ns_msg->name); + if (node) { + rp_chnl = (struct rpmsg_channel *) node->data; + if (rdev->channel_destroyed) { + rdev->channel_destroyed(rp_chnl); + } + rpmsg_destroy_ept(rp_chnl->rp_ept); + _rpmsg_delete_channel(rp_chnl); + } + } else { + /*RPMSG_NS_CREATE*/ + rp_chnl = _rpmsg_create_channel(rdev, ns_msg->name, 0x00, ns_msg->addr); + if (rp_chnl) { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + /* Create default endpoint for channel */ + rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, + RPMSG_ADDR_ANY); + if (rp_chnl->rp_ept) { + rp_chnl->src = rp_chnl->rp_ept->addr; + /* + * Echo back the NS message to remote in order to + * complete the connection stage. Remote will know the endpoint + * address from this point onward which will enable it to send + * message without waiting for any application level message from + * master. + */ + rpmsg_send(rp_chnl,data,len); /*Is this necessary? Infinite Echo Back ? */ + if (rdev->channel_created) { + rdev->channel_created(rp_chnl); + } + } + } + } +} + +/** + * rpmsg_get_address + * + * This function provides unique 32 bit address. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * + * return - a unique address + */ +int rpmsg_get_address(unsigned long *bitmap, int size) { + int addr = -1; + int i, tmp32; + + /* Find first available buffer */ + for (i = 0; i < size; i++) { + tmp32 = get_first_zero_bit(bitmap[i]); + + if (tmp32 < 32) { + addr = tmp32 + i + 1; /*This is strange*/ + bitmap[i] |= (1 << tmp32); + break; + } + } + + return addr; +} + +/** + * rpmsg_release_address + * + * Frees the given address. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * @param addr - address to free + * + * return - none + */ +int rpmsg_release_address(unsigned long *bitmap, int size, int addr) { + unsigned int i, j; + unsigned long mask = 1; + + if (addr >= size * 32) + return -1; + + /* Mark the addr as available */ + i = addr / 32; + j = addr % 32; + + mask = mask << j; + bitmap[i] = bitmap[i] & (~mask); + + return RPMSG_SUCCESS; +} + +/** + * rpmsg_is_address_set + * + * Checks whether address is used or free. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * @param addr - address to free + * + * return - TRUE/FALSE + */ +int rpmsg_is_address_set(unsigned long *bitmap, int size, + int addr) { + int i, j; + unsigned long mask = 1; + + if (addr >= size * 32) + return -1; + + /* Mark the id as available */ + i = addr / 32; + j = addr % 32; + mask = mask << j; + + return (bitmap[i] & mask); +} + +/** + * rpmsg_set_address + * + * Marks the address as consumed. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * @param addr - address to free + * + * return - none + */ +int rpmsg_set_address(unsigned long *bitmap, int size, int addr) { + int i, j; + unsigned long mask = 1; + + if (addr >= size * 32) + return -1; + + /* Mark the id as available */ + i = addr / 32; + j = addr % 32; + mask = mask << j; + bitmap[i] |= mask; + + return RPMSG_SUCCESS; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_core.h b/middleware/multicore/open-amp/rpmsg/rpmsg_core.h new file mode 100644 index 0000000..e70fd5c --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_core.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * 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. + */ + + +#ifndef _RPMSG_CORE_H_ +#define _RPMSG_CORE_H_ + +#include "../porting/env/env.h" +#include "../virtio/virtio.h" +#include "../common/hil/hil.h" +#include "../common/shm/sh_mem.h" +#include "../common/llist/llist.h" +#include "rpmsg.h" + +/* Configurable parameters */ +#define RPMSG_BUFFER_SIZE 512 +#define RPMSG_MAX_VQ_PER_RDEV 2 +#define RPMSG_NS_EPT_ADDR 0x35 +#define RPMSG_ADDR_BMP_SIZE 4 + +/* Definitions for device types , null pointer, etc.*/ +#define RPMSG_SUCCESS 0 +#define RPMSG_NULL (void *)0 +#define RPMSG_REMOTE 0 +#define RPMSG_MASTER 1 +#define RPMSG_TRUE 1 +#define RPMSG_FALSE 0 + +/* RPMSG channel states. */ +#define RPMSG_CHNL_STATE_IDLE 0 +#define RPMSG_CHNL_STATE_NS 1 +#define RPMSG_CHNL_STATE_ACTIVE 2 + +/* Remote processor/device states. */ +#define RPMSG_DEV_STATE_IDLE 0 +#define RPMSG_DEV_STATE_ACTIVE 1 + +/* Total tick count for 15secs - 1msec tick. */ +#define RPMSG_TICK_COUNT 15000 + +/* Time to wait - In multiple of 10 msecs. */ +#define RPMSG_TICKS_PER_INTERVAL 10 + +/* Error macros. */ +#define RPMSG_ERRORS_BASE -3000 +#define RPMSG_ERR_NO_MEM (RPMSG_ERRORS_BASE - 1) +#define RPMSG_ERR_NO_BUFF (RPMSG_ERRORS_BASE - 2) +#define RPMSG_ERR_MAX_VQ (RPMSG_ERRORS_BASE - 3) +#define RPMSG_ERR_PARAM (RPMSG_ERRORS_BASE - 4) +#define RPMSG_ERR_DEV_STATE (RPMSG_ERRORS_BASE - 5) +#define RPMSG_ERR_BUFF_SIZE (RPMSG_ERRORS_BASE - 6) +#define RPMSG_ERR_DEV_ID (RPMSG_ERRORS_BASE - 7) +#define RPMSG_ERR_DEV_ADDR (RPMSG_ERRORS_BASE - 8) + +struct rpmsg_channel; +typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, unsigned long); +typedef void (*rpmsg_chnl_cb_t)(struct rpmsg_channel *rp_chl); +/** + * remote_device + * + * This structure is maintained by RPMSG driver to represent remote device/core. + * + * @virtd_dev - virtio device for remote core + * @rvq - Rx virtqueue for virtio device + * @tvq - Tx virtqueue for virtio device + * @proc - reference to remote processor + * @rp_channels - rpmsg channels list for the device + * @rp_endpoints - rpmsg endpoints list for the device + * @mem_pool - shared memory pool + * @bitmap - bitmap for channels addresses + * @channel_created - create channel callback + * @channel_destroyed - delete channel callback + * @default_cb - default callback handler for RX data on channel + * @lock - remote device mutex + * @role - role of the remote device, RPMSG_MASTER/RPMSG_REMOTE + * @state - remote device state, IDLE/ACTIVE + * @support_ns - if device supports name service announcement + * + */ +struct remote_device { + struct virtio_device virt_dev; + struct virtqueue *rvq; + struct virtqueue *tvq; + struct hil_proc *proc; + struct llist *rp_channels; + struct llist *rp_endpoints; + struct sh_mem_pool *mem_pool; + unsigned long bitmap[RPMSG_ADDR_BMP_SIZE]; + rpmsg_chnl_cb_t channel_created; + rpmsg_chnl_cb_t channel_destroyed; + rpmsg_rx_cb_t default_cb; + LOCK *lock; + unsigned int role; + unsigned int state; + int support_ns; +}; + +/* Core functions */ +int rpmsg_start_ipc(struct remote_device *rdev); +struct rpmsg_channel *_rpmsg_create_channel(struct remote_device *rdev, + char *name, unsigned long src, unsigned long dst); +void _rpmsg_delete_channel(struct rpmsg_channel * rp_chnl); +struct rpmsg_endpoint *_create_endpoint(struct remote_device *rdev, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr); +void _destroy_endpoint(struct remote_device *rdev, + struct rpmsg_endpoint *rp_ept); +void rpmsg_send_ns_message(struct remote_device *rdev, + struct rpmsg_channel *rp_chnl, unsigned long flags); +int rpmsg_enqueue_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx); +void rpmsg_return_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx); +void *rpmsg_get_tx_buffer(struct remote_device *rdev, int *len, + unsigned short *idx); +void rpmsg_free_buffer(struct remote_device *rdev, void *buffer); +void rpmsg_free_channel(struct rpmsg_channel* rp_chnl); +void * rpmsg_get_rx_buffer(struct remote_device *rdev, unsigned long *len, + unsigned short *idx); +int rpmsg_get_address(unsigned long *bitmap, int size); +int rpmsg_release_address(unsigned long *bitmap, int size, int addr); +int rpmsg_is_address_set(unsigned long *bitmap, int size, + int addr); +int rpmsg_set_address(unsigned long *bitmap, int size, int addr); +void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, + void *data, int len, void *priv, unsigned long src); + +/* Remote device functions */ +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); +void rpmsg_rdev_deinit(struct remote_device *rdev); +struct llist *rpmsg_rdev_get_chnl_node_from_id(struct remote_device *rdev, + char *rp_chnl_id); +struct llist *rpmsg_rdev_get_chnl_from_addr(struct remote_device *rdev, + unsigned long addr); +struct llist *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev, + unsigned long addr); +int rpmsg_rdev_notify(struct remote_device *rdev); +int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, + const char *names[], vq_callback *callbacks[], + struct virtqueue *vqs[]); +unsigned char rpmsg_rdev_get_status(struct virtio_device *dev); + +void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status); + +uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev); + +void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature); + +uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev, + uint32_t features); +/* + * 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); +void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset, + void *src, int length); +void rpmsg_rdev_reset(struct virtio_device *dev); + +#endif /* _RPMSG_CORE_H_ */ -- cgit v1.2.3