diff options
Diffstat (limited to 'framework/src/openamp/rpmsg.c')
-rw-r--r-- | framework/src/openamp/rpmsg.c | 269 |
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); +} |