summaryrefslogtreecommitdiff
path: root/middleware/multicore/open-amp/rpmsg/rpmsg_core.c
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2016-01-12 14:06:54 -0800
committerStefan Agner <stefan.agner@toradex.com>2016-01-12 14:06:54 -0800
commita57cc2c988482010061b9e68344fdf1969889763 (patch)
tree5c050337492ce27c09b47421b123980b5a79f8d9 /middleware/multicore/open-amp/rpmsg/rpmsg_core.c
initial commit, FreeRTOS_BSP_1.0.0_iMX7D
Diffstat (limited to 'middleware/multicore/open-amp/rpmsg/rpmsg_core.c')
-rw-r--r--middleware/multicore/open-amp/rpmsg/rpmsg_core.c796
1 files changed, 796 insertions, 0 deletions
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<<VIRTIO_RPMSG_F_NS))) {
+ rdev->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;
+}