From 6d604ec487e664d6a86f29368338aacaecada600 Mon Sep 17 00:00:00 2001 From: liaoao Date: Thu, 4 Jan 2024 14:10:44 +0800 Subject: [PATCH] rpmsg: add physical transport layer support Rpmsg Physical Transport Layer is a new rpmsg transport, it's a common part for the physical communication based rpmsg transport layers such as Rpmsg-SPI and Rpmsg-Uart. It implements three common parts: 1. Implement the NuttX and OpenAMP rpmsg frameworks' ops and the rpmsg name service; 2. The buffer management and provide some APIs to lower layer to use; Signed-off-by: liaoao --- drivers/rpmsg/CMakeLists.txt | 6 + drivers/rpmsg/Kconfig | 6 + drivers/rpmsg/Make.defs | 5 + drivers/rpmsg/rpmsg_port.c | 743 +++++++++++++++++++++++++++++++ drivers/rpmsg/rpmsg_port.h | 305 +++++++++++++ include/nuttx/rpmsg/rpmsg_port.h | 54 +++ 6 files changed, 1119 insertions(+) create mode 100644 drivers/rpmsg/rpmsg_port.c create mode 100644 drivers/rpmsg/rpmsg_port.h create mode 100644 include/nuttx/rpmsg/rpmsg_port.h diff --git a/drivers/rpmsg/CMakeLists.txt b/drivers/rpmsg/CMakeLists.txt index 9d66cd78d5..a049dcc73a 100644 --- a/drivers/rpmsg/CMakeLists.txt +++ b/drivers/rpmsg/CMakeLists.txt @@ -27,6 +27,12 @@ if(CONFIG_RPMSG) list(APPEND SRCS rpmsg_ping.c) endif() + if(CONFIG_RPMSG_PORT) + list(APPEND SRCS rpmsg_port.c) + target_include_directories(drivers + PRIVATE ${NUTTX_DIR}/openamp/open-amp/lib) + endif() + if(CONFIG_RPMSG_VIRTIO) list(APPEND SRCS rpmsg_virtio.c) endif() diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 733e8d987e..6d9bf2b990 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -21,6 +21,12 @@ config RPMSG_PING This is for debugging & profiling, create ping rpmsg channel, user can use it to get send/recv speed & latency. +config RPMSG_PORT + bool + default n + ---help--- + Rpmsg port transport layer used for cross chip communication. + endif # RPMSG config RPMSG_VIRTIO diff --git a/drivers/rpmsg/Make.defs b/drivers/rpmsg/Make.defs index 50b87c5de4..52ddcab5d1 100644 --- a/drivers/rpmsg/Make.defs +++ b/drivers/rpmsg/Make.defs @@ -28,6 +28,11 @@ ifeq ($(CONFIG_RPMSG_PING),y) CSRCS += rpmsg_ping.c endif +ifeq ($(CONFIG_RPMSG_PORT),y) +CSRCS += rpmsg_port.c +CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)openamp$(DELIM)open-amp$(DELIM)lib +endif + ifeq ($(CONFIG_RPMSG_VIRTIO),y) CSRCS += rpmsg_virtio.c CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)openamp$(DELIM)open-amp$(DELIM)lib diff --git a/drivers/rpmsg/rpmsg_port.c b/drivers/rpmsg/rpmsg_port.c new file mode 100644 index 0000000000..2f1355c07d --- /dev/null +++ b/drivers/rpmsg/rpmsg_port.c @@ -0,0 +1,743 @@ +/**************************************************************************** + * drivers/rpmsg/rpmsg_port.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include + +#include "rpmsg_port.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RPMSG_PORT_BUF_TO_NODE(q,b) ((q)->node + ((FAR void *)(b) - (q)->buf) / (q)->len) +#define RPMSG_PORT_NODE_TO_BUF(q,n) ((q)->buf + (((n) - (q)->node)) * (q)->len) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static FAR const char *rpmsg_port_get_cpuname(FAR struct rpmsg_s *rpmsg); +static int rpmsg_port_get_tx_buffer_size(FAR struct rpmsg_s *rpmsg); +static int rpmsg_port_get_rx_buffer_size(FAR struct rpmsg_s *rpmsg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct rpmsg_ops_s g_rpmsg_port_ops = +{ + NULL, + NULL, + NULL, + NULL, + NULL, + rpmsg_port_get_cpuname, + rpmsg_port_get_tx_buffer_size, + rpmsg_port_get_rx_buffer_size, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rpmsg_port_post + ****************************************************************************/ + +static void rpmsg_port_post(FAR sem_t *sem) +{ + int count = 0; + + nxsem_get_value(sem, &count); + while (count++ <= 0) + { + nxsem_post(sem); + } +} + +/**************************************************************************** + * Name: rpmsg_port_add_node + ****************************************************************************/ + +static void rpmsg_port_add_node(FAR struct rpmsg_port_list_s *list, + FAR struct list_node *node) +{ + irqstate_t flags; + + flags = spin_lock_irqsave(&list->lock); + list_add_tail(&list->head, node); + list->num++; + spin_unlock_irqrestore(&list->lock, flags); +} + +/**************************************************************************** + * Name: rpmsg_port_remove_node + ****************************************************************************/ + +static FAR struct list_node * +rpmsg_port_remove_node(FAR struct rpmsg_port_list_s *list) +{ + FAR struct list_node *node; + irqstate_t flags; + + flags = spin_lock_irqsave(&list->lock); + node = list_remove_head(&list->head); + if (node != NULL) + { + list->num--; + } + + spin_unlock_irqrestore(&list->lock, flags); + return node; +} + +/**************************************************************************** + * Name: rpmsg_port_destroy_queue + * + * Description: + * Free memory alloced by rpmsg_port_create_queue. + * + ****************************************************************************/ + +static void rpmsg_port_destroy_queue(FAR struct rpmsg_port_queue_s *queue) +{ + if (queue->alloced) + { + kmm_free(queue->buf); + } + + kmm_free(queue->node); + nxsem_destroy(&queue->free.sem); + nxsem_destroy(&queue->ready.sem); +} + +/**************************************************************************** + * Name: rpmsg_port_create_queue + ****************************************************************************/ + +static int rpmsg_port_create_queue(FAR struct rpmsg_port_queue_s *queue, + uint32_t count, uint32_t len, + FAR void *buf) +{ + FAR struct list_node *node; + + node = kmm_malloc(count * sizeof(struct list_node)); + if (node == NULL) + { + return -ENOMEM; + } + + queue->node = node; + + /* Check if buffer space needed to be malloced internal. */ + + if (buf == NULL) + { + buf = kmm_malloc(count * len); + if (buf == NULL) + { + kmm_free(queue->node); + return -ENOMEM; + } + + queue->alloced = true; + } + + queue->len = len; + queue->buf = buf; + + /* Init free list */ + + spin_lock_init(&queue->free.lock); + nxsem_init(&queue->free.sem, 0, 0); + list_initialize(&queue->free.head); + while (count--) + { + rpmsg_port_add_node(&queue->free, node); + node++; + } + + /* Init ready list */ + + spin_lock_init(&queue->ready.lock); + nxsem_init(&queue->ready.sem, 0, 0); + list_initialize(&queue->ready.head); + + return 0; +} + +/**************************************************************************** + * Name: rpmsg_port_create_queues + ****************************************************************************/ + +static int +rpmsg_port_create_queues(FAR struct rpmsg_port_s *port, + FAR const struct rpmsg_port_config_s *cfg) +{ + int ret; + + ret = rpmsg_port_create_queue(&port->txq, cfg->txnum, + cfg->txlen, cfg->txbuf); + if (ret < 0) + { + return ret; + } + + ret = rpmsg_port_create_queue(&port->rxq, cfg->rxnum, + cfg->rxlen, cfg->rxbuf); + if (ret < 0) + { + rpmsg_port_destroy_queue(&port->txq); + return ret; + } + + return 0; +} + +/**************************************************************************** + * Name: rpmsg_port_destroy_queues + ****************************************************************************/ + +static void rpmsg_port_destroy_queues(FAR struct rpmsg_port_s *port) +{ + rpmsg_port_destroy_queue(&port->txq); + rpmsg_port_destroy_queue(&port->rxq); +} + +/**************************************************************************** + * Name: rpmsg_port_get_tx_payload_buffer + ****************************************************************************/ + +static FAR void * +rpmsg_port_get_tx_payload_buffer(FAR struct rpmsg_device *rdev, + FAR uint32_t *len, int wait) +{ + FAR struct rpmsg_port_s *port = + metal_container_of(rdev, struct rpmsg_port_s, rdev); + FAR struct rpmsg_port_header_s *hdr = + rpmsg_port_queue_get_available_buffer(&port->txq, wait); + + if (hdr == NULL) + { + return NULL; + } + + *len = hdr->len - sizeof(struct rpmsg_port_header_s) - + sizeof(struct rpmsg_hdr); + + return RPMSG_LOCATE_DATA(hdr->buf); +} + +/**************************************************************************** + * Name: rpmsg_port_send_offchannel_nocopy + ****************************************************************************/ + +static int rpmsg_port_send_offchannel_nocopy(FAR struct rpmsg_device *rdev, + uint32_t src, uint32_t dst, + FAR const void *data, int len) +{ + FAR struct rpmsg_port_s *port = + metal_container_of(rdev, struct rpmsg_port_s, rdev); + FAR struct rpmsg_port_header_s *hdr; + FAR struct rpmsg_hdr *rp_hdr; + + rp_hdr = RPMSG_LOCATE_HDR(data); + rp_hdr->dst = dst; + rp_hdr->src = src; + rp_hdr->len = len; + rp_hdr->reserved = 0; + rp_hdr->flags = 0; + + hdr = metal_container_of(rp_hdr, struct rpmsg_port_header_s, buf); + hdr->len = sizeof(struct rpmsg_port_header_s) + + sizeof(struct rpmsg_hdr) + len; + + rpmsg_port_queue_add_buffer(&port->txq, hdr); + port->ops->kick(port); + + return len; +} + +/**************************************************************************** + * Name: rpmsg_port_send_offchannel_raw + ****************************************************************************/ + +static int rpmsg_port_send_offchannel_raw(FAR struct rpmsg_device *rdev, + uint32_t src, uint32_t dst, + FAR const void *data, + int len, int wait) +{ + uint32_t buflen; + FAR void *buf; + + buf = rpmsg_port_get_tx_payload_buffer(rdev, &buflen, wait); + if (buf == NULL) + { + return RPMSG_ERR_NO_BUFF; + } + + RPMSG_ASSERT(len <= buflen, "Send size larger than buffer size\n"); + memcpy(buf, data, len); + + return rpmsg_port_send_offchannel_nocopy(rdev, src, dst, buf, len); +} + +/**************************************************************************** + * Name: rpmsg_port_hold_rx_buffer + ****************************************************************************/ + +static void rpmsg_port_hold_rx_buffer(FAR struct rpmsg_device *rdev, + FAR void *rxbuf) +{ + FAR struct rpmsg_hdr *rp_hdr = RPMSG_LOCATE_HDR(rxbuf); + + atomic_fetch_add(&rp_hdr->reserved, 1 << RPMSG_BUF_HELD_SHIFT); +} + +/**************************************************************************** + * Name: rpmsg_port_release_rx_buffer + ****************************************************************************/ + +static void rpmsg_port_release_rx_buffer(FAR struct rpmsg_device *rdev, + FAR void *rxbuf) +{ + FAR struct rpmsg_port_s *port = + metal_container_of(rdev, struct rpmsg_port_s, rdev); + FAR struct rpmsg_hdr *rp_hdr = RPMSG_LOCATE_HDR(rxbuf); + FAR struct rpmsg_port_header_s *hdr = + metal_container_of(rp_hdr, struct rpmsg_port_header_s, buf); + uint32_t reserved = + atomic_fetch_sub(&rp_hdr->reserved, 1 << RPMSG_BUF_HELD_SHIFT); + + if ((reserved & RPMSG_BUF_HELD_MASK) == (1 << RPMSG_BUF_HELD_SHIFT)) + { + rpmsg_port_queue_return_buffer(&port->rxq, hdr); + } +} + +/**************************************************************************** + * Name: rpmsg_port_release_tx_buffer + ****************************************************************************/ + +static int rpmsg_port_release_tx_buffer(FAR struct rpmsg_device *rdev, + FAR void *txbuf) +{ + FAR struct rpmsg_port_s *port = + metal_container_of(rdev, struct rpmsg_port_s, rdev); + FAR struct rpmsg_hdr *rp_hdr = RPMSG_LOCATE_HDR(txbuf); + FAR struct rpmsg_port_header_s *hdr = + metal_container_of(rp_hdr, struct rpmsg_port_header_s, buf); + + rpmsg_port_queue_return_buffer(&port->txq, hdr); + return RPMSG_SUCCESS; +} + +/**************************************************************************** + * Name: rpmsg_port_rx_callback + ****************************************************************************/ + +static void rpmsg_port_rx_callback(FAR struct rpmsg_port_s *port, + FAR struct rpmsg_port_header_s *hdr) +{ + FAR struct rpmsg_device *rdev = &port->rdev; + FAR struct rpmsg_hdr *rp_hdr = (FAR struct rpmsg_hdr *)hdr->buf; + FAR void *data = RPMSG_LOCATE_DATA(rp_hdr); + FAR struct rpmsg_endpoint *ept; + int status; + + metal_mutex_acquire(&rdev->lock); + ept = rpmsg_get_ept_from_addr(rdev, rp_hdr->dst); + rpmsg_ept_incref(ept); + metal_mutex_release(&rdev->lock); + rpmsg_port_hold_rx_buffer(rdev, data); + + if (ept != NULL) + { + if (ept->dest_addr == RPMSG_ADDR_ANY) + { + ept->dest_addr = rp_hdr->src; + } + + status = ept->cb(ept, data, rp_hdr->len, rp_hdr->src, ept->priv); + if (status < 0) + { + RPMSG_ASSERT(0, "unexpected callback status\n"); + } + } + + rpmsg_port_release_rx_buffer(rdev, data); + metal_mutex_acquire(&rdev->lock); + rpmsg_ept_decref(ept); + metal_mutex_release(&rdev->lock); +} + +/**************************************************************************** + * Name: rpmsg_port_ns_callback + ****************************************************************************/ + +static int rpmsg_port_ns_callback(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, uint32_t src, + FAR void *priv) +{ + FAR struct rpmsg_device *rdev = ept->rdev; + FAR struct rpmsg_ns_msg *msg = data; + FAR const char *name = msg->name; + uint32_t dest = msg->addr; + bool decref = false; + + if (len != sizeof(*msg)) + { + return RPMSG_SUCCESS; + } + + metal_mutex_acquire(&rdev->lock); + ept = rpmsg_get_endpoint(rdev, name, RPMSG_ADDR_ANY, dest); + + if (msg->flags == RPMSG_NS_DESTROY) + { + if (ept != NULL) + { + ept->dest_addr = RPMSG_ADDR_ANY; + if (ept->release_cb != NULL) + { + rpmsg_ept_incref(ept); + decref = true; + } + } + + metal_mutex_release(&rdev->lock); + if (ept != NULL && ept->ns_unbind_cb != NULL) + { + ept->ns_unbind_cb(ept); + } + + if (rdev->ns_unbind_cb != NULL) + { + rdev->ns_unbind_cb(rdev, name, dest); + } + + if (decref) + { + metal_mutex_acquire(&rdev->lock); + rpmsg_ept_decref(ept); + metal_mutex_release(&rdev->lock); + } + } + else if (msg->flags == RPMSG_NS_CREATE) + { + if (ept == NULL) + { + metal_mutex_release(&rdev->lock); + if (rdev->ns_bind_cb != NULL) + { + rdev->ns_bind_cb(rdev, name, dest); + } + } + else if (ept->dest_addr == RPMSG_ADDR_ANY) + { + ept->dest_addr = dest; + metal_mutex_release(&rdev->lock); + if (ept->name[0] && rdev->support_ack) + { + rpmsg_send_ns_message(ept, RPMSG_NS_CREATE_ACK); + } + + /* Notify application that the endpoint has been bound */ + + if (ept->ns_bound_cb != NULL) + { + ept->ns_bound_cb(ept); + } + } + else + { + metal_mutex_release(&rdev->lock); + } + } + else + { + /* RPMSG_NS_CREATE_ACK */ + + if (ept != NULL && ept->dest_addr == RPMSG_ADDR_ANY) + { + ept->dest_addr = dest; + metal_mutex_release(&rdev->lock); + + if (ept->ns_bound_cb != NULL) + { + ept->ns_bound_cb(ept); + } + } + else + { + metal_mutex_release(&rdev->lock); + } + } + + return RPMSG_SUCCESS; +} + +/**************************************************************************** + * Name: rpmsg_port_get_cpuname + ****************************************************************************/ + +static FAR const char *rpmsg_port_get_cpuname(FAR struct rpmsg_s *rpmsg) +{ + FAR struct rpmsg_port_s *port = (FAR struct rpmsg_port_s *)rpmsg; + + return port->cpuname; +} + +/**************************************************************************** + * Name: rpmsg_port_get_tx_buffer_size + ****************************************************************************/ + +static int rpmsg_port_get_tx_buffer_size(FAR struct rpmsg_s *rpmsg) +{ + FAR struct rpmsg_port_s *port = (FAR struct rpmsg_port_s *)rpmsg; + + return port->txq.len - sizeof(struct rpmsg_port_header_s) - + sizeof(struct rpmsg_hdr); +} + +/**************************************************************************** + * Name: rpmsg_port_get_rx_buffer_size + ****************************************************************************/ + +static int rpmsg_port_get_rx_buffer_size(FAR struct rpmsg_s *rpmsg) +{ + FAR struct rpmsg_port_s *port = (FAR struct rpmsg_port_s *)rpmsg; + + return port->rxq.len - sizeof(struct rpmsg_port_header_s) - + sizeof(struct rpmsg_hdr); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rpmsg_port_initialize + ****************************************************************************/ + +int rpmsg_port_initialize(FAR struct rpmsg_port_s *port, + FAR const struct rpmsg_port_config_s *cfg, + FAR const struct rpmsg_port_ops_s *ops) +{ + FAR struct rpmsg_device *rdev; + int ret; + + if (port == NULL || cfg == NULL || ops == NULL) + { + return -EINVAL; + } + + ret = rpmsg_port_create_queues(port, cfg); + if (ret < 0) + { + return ret; + } + + port->ops = ops; + strlcpy(port->cpuname, cfg->remotecpu, RPMSG_NAME_SIZE); + + rdev = &port->rdev; + memset(rdev, 0, sizeof(*rdev)); + metal_mutex_init(&rdev->lock); + rdev->ns_bind_cb = rpmsg_ns_bind; + rdev->ns_unbind_cb = rpmsg_ns_unbind; + rdev->ops.send_offchannel_raw = rpmsg_port_send_offchannel_raw; + rdev->ops.hold_rx_buffer = rpmsg_port_hold_rx_buffer; + rdev->ops.release_rx_buffer = rpmsg_port_release_rx_buffer; + rdev->ops.get_tx_payload_buffer = rpmsg_port_get_tx_payload_buffer; + rdev->ops.send_offchannel_nocopy = rpmsg_port_send_offchannel_nocopy; + rdev->ops.release_tx_buffer = rpmsg_port_release_tx_buffer; + + metal_list_init(&rdev->endpoints); + + rdev->support_ack = true; + rdev->support_ns = true; + + rpmsg_register_endpoint(rdev, &rdev->ns_ept, "NS", RPMSG_NS_EPT_ADDR, + RPMSG_NS_EPT_ADDR, rpmsg_port_ns_callback, NULL); + port->ops->register_callback(port, rpmsg_port_rx_callback); + + return 0; +} + +/**************************************************************************** + * Name: rpmsg_port_uninitialize + ****************************************************************************/ + +void rpmsg_port_uninitialize(FAR struct rpmsg_port_s *port) +{ + FAR struct rpmsg_device *rdev = &port->rdev; + FAR struct metal_list *node; + FAR struct rpmsg_endpoint *ept; + + while (!metal_list_is_empty(&rdev->endpoints)) + { + node = rdev->endpoints.next; + ept = metal_container_of(node, struct rpmsg_endpoint, node); + rpmsg_destroy_ept(ept); + if (ept->ns_unbind_cb) + { + ept->ns_unbind_cb(ept); + } + } + + metal_mutex_deinit(&rdev->lock); + rpmsg_port_destroy_queues(port); +} + +/**************************************************************************** + * Name: rpmsg_port_queue_get_available_buffer + ****************************************************************************/ + +FAR struct rpmsg_port_header_s * +rpmsg_port_queue_get_available_buffer(FAR struct rpmsg_port_queue_s *queue, + bool wait) +{ + FAR struct list_node *node; + FAR struct rpmsg_port_header_s *hdr; + + for (; ; ) + { + node = rpmsg_port_remove_node(&queue->free); + if (node) + { + hdr = RPMSG_PORT_NODE_TO_BUF(queue, node); + hdr->len = queue->len; + return hdr; + } + else if (!wait) + { + return NULL; + } + + nxsem_wait_uninterruptible(&queue->free.sem); + } +} + +/**************************************************************************** + * Name: rpmsg_port_queue_return_buffer + ****************************************************************************/ + +FAR void rpmsg_port_queue_return_buffer(FAR struct rpmsg_port_queue_s *queue, + FAR struct rpmsg_port_header_s *hdr) +{ + FAR struct list_node *node = RPMSG_PORT_BUF_TO_NODE(queue, hdr); + + rpmsg_port_add_node(&queue->free, node); + rpmsg_port_post(&queue->free.sem); +} + +/**************************************************************************** + * Name: rpmsg_port_queue_get_buffer + ****************************************************************************/ + +FAR struct rpmsg_port_header_s * +rpmsg_port_queue_get_buffer(FAR struct rpmsg_port_queue_s *queue, bool wait) +{ + FAR struct list_node *node; + + for (; ; ) + { + node = rpmsg_port_remove_node(&queue->ready); + if (node) + { + return RPMSG_PORT_NODE_TO_BUF(queue, node); + } + else if (!wait) + { + return NULL; + } + + nxsem_wait_uninterruptible(&queue->ready.sem); + } +} + +/**************************************************************************** + * Name: rpmsg_port_queue_add_buffer + ****************************************************************************/ + +void rpmsg_port_queue_add_buffer(FAR struct rpmsg_port_queue_s *queue, + FAR struct rpmsg_port_header_s *hdr) +{ + FAR struct list_node *node = RPMSG_PORT_BUF_TO_NODE(queue, hdr); + + rpmsg_port_add_node(&queue->ready, node); + rpmsg_port_post(&queue->ready.sem); +} + +/**************************************************************************** + * Name: rpmsg_port_queue_navail + ****************************************************************************/ + +uint32_t rpmsg_port_queue_navail(FAR struct rpmsg_port_queue_s *queue) +{ + return atomic_load(&queue->free.num); +} + +/**************************************************************************** + * Name: rpmsg_port_register + ****************************************************************************/ + +int rpmsg_port_register(FAR struct rpmsg_port_s *port) +{ + char name[64]; + int ret; + + snprintf(name, sizeof(name), "/dev/rpmsg/%s", port->cpuname); + ret = rpmsg_register(name, &port->rpmsg, &g_rpmsg_port_ops); + if (ret < 0) + { + return ret; + } + + rpmsg_device_created(&port->rpmsg); + return ret; +} + +/**************************************************************************** + * Name: rpmsg_port_unregister + ****************************************************************************/ + +void rpmsg_port_unregister(FAR struct rpmsg_port_s *port) +{ + char name[64]; + + rpmsg_device_destory(&port->rpmsg); + + snprintf(name, sizeof(name), "/dev/rpmsg/%s", port->cpuname); + rpmsg_unregister(name, &port->rpmsg); +} diff --git a/drivers/rpmsg/rpmsg_port.h b/drivers/rpmsg/rpmsg_port.h new file mode 100644 index 0000000000..e754653388 --- /dev/null +++ b/drivers/rpmsg/rpmsg_port.h @@ -0,0 +1,305 @@ +/**************************************************************************** + * drivers/rpmsg/rpmsg_port.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __DRIVERS_RPMSG_RPMSG_PORT_H +#define __DRIVERS_RPMSG_RPMSG_PORT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This header is for physical layer's use */ + +begin_packed_struct struct rpmsg_port_header_s +{ + uint16_t crc; /* CRC of current port data frame */ + uint16_t cmd; /* Reserved for uart/spi port driver */ + uint32_t avail; /* Available rx buffer of peer side */ + uint32_t len; /* Data frame length */ + uint32_t seq; /* Sequence number of current data frame */ + uint8_t buf[0]; /* Payload buffer */ +} end_packed_struct; + +struct rpmsg_port_list_s +{ + uint32_t num; /* Number of buffers */ + sem_t sem; /* Used to wait for buffer */ + spinlock_t lock; /* List lock */ + struct list_node head; /* List head */ +}; + +struct rpmsg_port_queue_s +{ + /* Indicate buffers current queue managed is dynamic alloced */ + + bool alloced; + + /* Buffer array's base address of current queue managed */ + + FAR void *buf; + + /* Node related to buffer for buffer management */ + + FAR struct list_node *node; + + /* Length of buffers current queue managed */ + + uint32_t len; + + /* Free list of buffers which have not been occupied data yet */ + + struct rpmsg_port_list_s free; + + /* Ready list of buffers which have been occupied data already */ + + struct rpmsg_port_list_s ready; +}; + +struct rpmsg_port_s; + +typedef void (*rpmsg_port_rx_cb_t)(FAR struct rpmsg_port_s *port, + FAR struct rpmsg_port_header_s *hdr); + +struct rpmsg_port_ops_s +{ + /* Kick driver there is buffer to be sent of the tx queue */ + + CODE void (*kick)(FAR struct rpmsg_port_s *port); + + /* Register callback function which should be invoked when there is + * date received to the rx queue by driver + */ + + CODE void (*register_callback)(FAR struct rpmsg_port_s *port, + rpmsg_port_rx_cb_t callback); +}; + +struct rpmsg_port_s +{ + struct rpmsg_s rpmsg; + struct rpmsg_device rdev; /* Rpmsg device object */ + struct rpmsg_port_queue_s txq; /* Port tx queue */ + struct rpmsg_port_queue_s rxq; /* Port rx queue */ + + /* Remote cpu name of this port connected to */ + + char cpuname[RPMSG_NAME_SIZE]; + + /* Ops need implemented by drivers under port layer */ + + const FAR struct rpmsg_port_ops_s *ops; +}; + +#ifndef __ASSEMBLY__ +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: rpmsg_port_queue_get_available_buffer + * + * Description: + * Get buffer from free list of the queue. + * + * Input Parameters: + * queue - The queue to be getten from. + * wait - If wait or not when there is no available buffer of the queue. + * + * Returned Value: + * A struct rpmsg_port_header_s's pointer on success or NULL on failure. + * The len of struct rpmsg_port_header_s indicates the sum of struct + * rpmsg_port_header_s's size and the data size can be used. + * + ****************************************************************************/ + +FAR struct rpmsg_port_header_s * +rpmsg_port_queue_get_available_buffer(FAR struct rpmsg_port_queue_s *queue, + bool wait); + +/**************************************************************************** + * Name: rpmsg_port_queue_return_buffer + * + * Description: + * Return buffer to free list of the queue. + * + * Input Parameters: + * queue - The queue to be returned to. + * hdr - Pointer to the struct rpmsg_port_header_s to be returned. + * + * Returned Value: + * No return value. + * + ****************************************************************************/ + +void rpmsg_port_queue_return_buffer(FAR struct rpmsg_port_queue_s *queue, + FAR struct rpmsg_port_header_s *hdr); + +/**************************************************************************** + * Name: rpmsg_port_queue_get_buffer + * + * Description: + * Get buffer from ready list of the queue. + * + * Input Parameters: + * queue - The queue to be getten from. + * wait - If wait or not when there is no used buffer of the queue. + * + * Returned Value: + * A struct rpmsg_port_header_s's pointer on success or NULL on failure. + * The len of struct rpmsg_port_header_s indicates the sum of struct + * rpmsg_port_header_s's size and the data size has be used. + * + ****************************************************************************/ + +FAR struct rpmsg_port_header_s * +rpmsg_port_queue_get_buffer(FAR struct rpmsg_port_queue_s *queue, + bool wait); + +/**************************************************************************** + * Name: rpmsg_port_queue_add_buffer + * + * Description: + * Add buffer to ready list of the queue. + * + * Input Parameters: + * queue - The queue to be added to. + * hdr - Pointer to the struct rpmsg_port_header_s to be added. The + * length of it must be set before this function invoked. + * + * Returned Value: + * No return value. + * + ****************************************************************************/ + +void rpmsg_port_queue_add_buffer(FAR struct rpmsg_port_queue_s *queue, + FAR struct rpmsg_port_header_s *hdr); + +/**************************************************************************** + * Name: rpmsg_port_queue_navail + * + * Description: + * Get available buffer number of free list of the queue. + * + * Input Parameters: + * queue - The queue is to be calculated. + * + * Returned Value: + * Number of available buffers. + * + ****************************************************************************/ + +uint32_t rpmsg_port_queue_navail(FAR struct rpmsg_port_queue_s *queue); + +/**************************************************************************** + * Name: rpmsg_port_initialize + * + * Description: + * Init port layer by port's configuration. rpmsg port layer creates and + * manages queues used for communication of two cpus. + * + * Input Parameters: + * port - The port to be inited. + * cfg - Port configuration. + * ops - Operation implemented by drivers under port layer. + * + * Returned Value: + * Zero on success or an negative value on failure. + * + ****************************************************************************/ + +int rpmsg_port_initialize(FAR struct rpmsg_port_s *port, + FAR const struct rpmsg_port_config_s *cfg, + FAR const struct rpmsg_port_ops_s *ops); + +/**************************************************************************** + * Name: rpmsg_port_uninitialize + * + * Description: + * uninit rpmsg port. + * + * Input Parameters: + * port - The port to be uninited. + * + * Returned Value: + * No return value. + * + ****************************************************************************/ + +void rpmsg_port_uninitialize(FAR struct rpmsg_port_s *port); + +/**************************************************************************** + * Name: rpmsg_port_register + * + * Description: + * Invoked to create a rpmsg character device when a connection between + * two cpus has established. + * + * Input Parameters: + * port - The port has established a connection. + * + * Returned Value: + * Zero on success or an negative value on failure. + * + ****************************************************************************/ + +int rpmsg_port_register(FAR struct rpmsg_port_s *port); + +/**************************************************************************** + * Name: rpmsg_port_unregister + * + * Description: + * Invoked to unregister the rpmsg character device. + * + * Input Parameters: + * port - The port has established a connection. + * + * Returned Value: + * No return value. + * + ****************************************************************************/ + +void rpmsg_port_unregister(FAR struct rpmsg_port_s *port); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif +#endif /* __DRIVERS_RPMSG_RPMSG_PORT_H */ diff --git a/include/nuttx/rpmsg/rpmsg_port.h b/include/nuttx/rpmsg/rpmsg_port.h new file mode 100644 index 0000000000..33d9ab4ed5 --- /dev/null +++ b/include/nuttx/rpmsg/rpmsg_port.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * include/nuttx/rpmsg/rpmsg_port.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_RPMSG_RPMSG_PORT_H +#define __INCLUDE_NUTTX_RPMSG_RPMSG_PORT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#ifdef CONFIG_RPMSG_PORT + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct rpmsg_port_config_s +{ + FAR const char *remotecpu; + uint32_t txnum; /* Number of tx buffer. */ + uint32_t rxnum; /* Number of rx buffer. */ + uint32_t txlen; /* Length of a single tx buffer. */ + uint32_t rxlen; /* Length of a single rx buffer. */ + + /* Pointer to whole tx/rx buffer, if it was null, transport layer will + * alloc internal. + */ + + FAR void *txbuf; + FAR void *rxbuf; +}; + +#endif /* CONFIG_RPMSG_PORT */ +#endif /* __INCLUDE_NUTTX_RPMSG_RPMSG_PORT_H */