| /* |
| * 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 <openamp/rpmsg.h> |
| #include <metal/alloc.h> |
| #include <metal/utilities.h> |
| |
| #include "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); |
| } |