aboutsummaryrefslogtreecommitdiff
path: root/framework/src/openamp/rpmsg.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/openamp/rpmsg.c')
-rw-r--r--framework/src/openamp/rpmsg.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/framework/src/openamp/rpmsg.c b/framework/src/openamp/rpmsg.c
new file mode 100644
index 0000000..66413d4
--- /dev/null
+++ b/framework/src/openamp/rpmsg.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2014, Mentor Graphics Corporation
+ * All rights reserved.
+ * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (c) 2018 Linaro, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <libmetal/utilities.h>
+#include <libmetal/mutex.h>
+#include <openamp/rpmsg_internal.h>
+
+/**
+ * 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
+ */
+static uint32_t rpmsg_get_address(unsigned long *bitmap, int size)
+{
+ unsigned int addr = RPMSG_ADDR_ANY;
+ unsigned int nextbit;
+
+ nextbit = metal_bitmap_next_clear_bit(bitmap, 0, size);
+ if (nextbit < (uint32_t)size) {
+ addr = nextbit;
+ metal_bitmap_set_bit(bitmap, nextbit);
+ }
+
+ 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
+ */
+static void rpmsg_release_address(unsigned long *bitmap, int size,
+ int addr)
+{
+ if (addr < size)
+ metal_bitmap_clear_bit(bitmap, addr);
+}
+
+/**
+ * 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
+ */
+static int rpmsg_is_address_set(unsigned long *bitmap, int size, int addr)
+{
+ if (addr < size)
+ return metal_bitmap_is_bit_set(bitmap, addr);
+ else
+ return RPMSG_ERR_PARAM;
+}
+
+/**
+ * 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
+ */
+static int rpmsg_set_address(unsigned long *bitmap, int size, int addr)
+{
+ if (addr < size) {
+ metal_bitmap_set_bit(bitmap, addr);
+ return RPMSG_SUCCESS;
+ } else {
+ return RPMSG_ERR_PARAM;
+ }
+}
+
+/**
+ * This function sends rpmsg "message" to remote device.
+ *
+ * @param ept - pointer to end point
+ * @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 - size of data sent or negative value for failure.
+ *
+ */
+int rpmsg_send_offchannel_raw(struct rpmsg_endpoint *ept, uint32_t src,
+ uint32_t dst, const void *data, int size,
+ int wait)
+{
+ struct rpmsg_device *rdev;
+
+ if (!ept || !ept->rdev || !data || dst == RPMSG_ADDR_ANY)
+ return RPMSG_ERR_PARAM;
+
+ rdev = ept->rdev;
+
+ if (rdev->ops.send_offchannel_raw)
+ return rdev->ops.send_offchannel_raw(rdev, src, dst, data,
+ size, wait);
+
+ return RPMSG_ERR_PARAM;
+}
+
+int rpmsg_send_ns_message(struct rpmsg_endpoint *ept, unsigned long flags)
+{
+ struct rpmsg_ns_msg ns_msg;
+ int ret;
+
+ ns_msg.flags = flags;
+ ns_msg.addr = ept->addr;
+ strncpy(ns_msg.name, ept->name, sizeof(ns_msg.name));
+ ret = rpmsg_send_offchannel_raw(ept, ept->addr,
+ RPMSG_NS_EPT_ADDR,
+ &ns_msg, sizeof(ns_msg), true);
+ if (ret < 0)
+ return ret;
+ else
+ return RPMSG_SUCCESS;
+}
+
+struct rpmsg_endpoint *rpmsg_get_endpoint(struct rpmsg_device *rdev,
+ const char *name, uint32_t addr,
+ uint32_t dest_addr)
+{
+ struct metal_list *node;
+ struct rpmsg_endpoint *ept;
+
+ metal_list_for_each(&rdev->endpoints, node) {
+ int name_match = 0;
+
+ ept = metal_container_of(node, struct rpmsg_endpoint, node);
+ /* try to get by local address only */
+ if (addr != RPMSG_ADDR_ANY && ept->addr == addr)
+ return ept;
+ /* try to find match on local end remote address */
+ if (addr == ept->addr && dest_addr == ept->dest_addr)
+ return ept;
+ /* else use name service and destination address */
+ if (name)
+ name_match = !strncmp(ept->name, name,
+ sizeof(ept->name));
+ if (!name || !name_match)
+ continue;
+ /* destination address is known, equal to ept remote address*/
+ if (dest_addr != RPMSG_ADDR_ANY && ept->dest_addr == dest_addr)
+ return ept;
+ /* ept is registered but not associated to remote ept*/
+ if (addr == RPMSG_ADDR_ANY && ept->dest_addr == RPMSG_ADDR_ANY)
+ return ept;
+ }
+ return NULL;
+}
+
+static void rpmsg_unregister_endpoint(struct rpmsg_endpoint *ept)
+{
+ struct rpmsg_device *rdev;
+
+ if (!ept)
+ return;
+
+ rdev = ept->rdev;
+
+ if (ept->addr != RPMSG_ADDR_ANY)
+ rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
+ ept->addr);
+ metal_list_del(&ept->node);
+}
+
+int rpmsg_register_endpoint(struct rpmsg_device *rdev,
+ struct rpmsg_endpoint *ept)
+{
+ ept->rdev = rdev;
+
+ metal_list_add_tail(&rdev->endpoints, &ept->node);
+ return RPMSG_SUCCESS;
+}
+
+int rpmsg_create_ept(struct rpmsg_endpoint *ept, struct rpmsg_device *rdev,
+ const char *name, uint32_t src, uint32_t dest,
+ rpmsg_ept_cb cb, rpmsg_ns_unbind_cb unbind_cb)
+{
+ int status;
+ uint32_t addr = src;
+
+ if (!ept)
+ return RPMSG_ERR_PARAM;
+
+ metal_mutex_acquire(&rdev->lock);
+ if (src != RPMSG_ADDR_ANY) {
+ status = rpmsg_is_address_set(rdev->bitmap,
+ RPMSG_ADDR_BMP_SIZE, src);
+ if (!status) {
+ /* Mark the address as used in the address bitmap. */
+ rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
+ src);
+ } else if (status > 0) {
+ status = RPMSG_SUCCESS;
+ goto ret_status;
+ } else {
+ goto ret_status;
+ }
+ } else {
+ addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE);
+ }
+
+ rpmsg_init_ept(ept, name, addr, dest, cb, unbind_cb);
+
+ status = rpmsg_register_endpoint(rdev, ept);
+ if (status < 0)
+ rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr);
+
+ if (!status && ept->dest_addr == RPMSG_ADDR_ANY) {
+ /* Send NS announcement to remote processor */
+ metal_mutex_release(&rdev->lock);
+ status = rpmsg_send_ns_message(ept, RPMSG_NS_CREATE);
+ metal_mutex_acquire(&rdev->lock);
+ if (status)
+ rpmsg_unregister_endpoint(ept);
+ }
+
+ret_status:
+ metal_mutex_release(&rdev->lock);
+ return status;
+}
+
+/**
+ * rpmsg_destroy_ept
+ *
+ * This function deletes rpmsg endpoint and performs cleanup.
+ *
+ * @param ept - pointer to endpoint to destroy
+ *
+ */
+void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
+{
+ struct rpmsg_device *rdev;
+
+ if (!ept)
+ return;
+
+ rdev = ept->rdev;
+ if (ept->addr != RPMSG_NS_EPT_ADDR)
+ (void)rpmsg_send_ns_message(ept, RPMSG_NS_DESTROY);
+ metal_mutex_acquire(&rdev->lock);
+ rpmsg_unregister_endpoint(ept);
+ metal_mutex_release(&rdev->lock);
+}