diff --git a/drivers/rpmsg/CMakeLists.txt b/drivers/rpmsg/CMakeLists.txt index 02d54ca413..8011872aec 100644 --- a/drivers/rpmsg/CMakeLists.txt +++ b/drivers/rpmsg/CMakeLists.txt @@ -27,5 +27,9 @@ if(CONFIG_RPMSG) list(APPEND SRCS rpmsg_ping.c) endif() + if(CONFIG_RPMSG_VIRTIO) + list(APPEND SRCS rpmsg_virtio.c) + endif() + target_sources(drivers PRIVATE ${SRCS}) endif() diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index c96a7d5692..9a9608c9a2 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -22,3 +22,20 @@ config RPMSG_PING channel, user can use it to get send/recv speed & latency. endif # RPMSG + +config RPMSG_VIRTIO + bool "rpmsg virtio transport support" + default n + select RPMSG + +if RPMSG_VIRTIO + +config RPMSG_VIRTIO_PRIORITY + int "rpmsg virtio rx thread priority" + default 224 + +config RPMSG_VIRTIO_STACKSIZE + int "rpmsg virtio rx thread stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/drivers/rpmsg/Make.defs b/drivers/rpmsg/Make.defs index 64595ca252..677cccf8fc 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_VIRTIO),y) +CSRCS += rpmsg_virtio.c +CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)openamp$(DELIM)open-amp$(DELIM)lib +endif + DEPPATH += --dep-path rpmsg VPATH += :rpmsg diff --git a/drivers/rpmsg/rpmsg_virtio.c b/drivers/rpmsg/rpmsg_virtio.c new file mode 100644 index 0000000000..29d7d04572 --- /dev/null +++ b/drivers/rpmsg/rpmsg_virtio.c @@ -0,0 +1,634 @@ +/**************************************************************************** + * drivers/rpmsg/rpmsg_virtio.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 +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef ALIGN_UP +# define ALIGN_UP(s, a) (((s) + (a) - 1) & ~((a) - 1)) +#endif + +#define RPMSG_VIRTIO_TIMEOUT_MS 20 +#define RPMSG_VIRTIO_NOTIFYID 0 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct rpmsg_virtio_priv_s +{ + struct rpmsg_s rpmsg; + struct rpmsg_virtio_device rvdev; + FAR struct rpmsg_virtio_s *dev; + FAR struct rpmsg_virtio_rsc_s *rsc; + struct virtio_device vdev; + struct rpmsg_virtio_shm_pool pool[2]; + struct virtio_vring_info rvrings[2]; + sem_t semtx; + sem_t semrx; + pid_t tid; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rpmsg_virtio_wait(FAR struct rpmsg_s *rpmsg, FAR sem_t *sem); +static int rpmsg_virtio_post(FAR struct rpmsg_s *rpmsg, FAR sem_t *sem); +static void rpmsg_virtio_dump(FAR struct rpmsg_s *rpmsg); +static FAR const char *rpmsg_virtio_get_cpuname(FAR struct rpmsg_s *rpmsg); +static int rpmsg_virtio_get_tx_buffer_size(FAR struct rpmsg_s *rpmsg); +static int rpmsg_virtio_get_rx_buffer_size_(FAR struct rpmsg_s *rpmsg); + +static int rpmsg_virtio_create_virtqueues_(FAR struct virtio_device *vdev, + unsigned int flags, + unsigned int nvqs, + FAR const char *names[], + vq_callback callbacks[]); +static uint8_t rpmsg_virtio_get_status_(FAR struct virtio_device *dev); +static void rpmsg_virtio_set_status_(FAR struct virtio_device *dev, + uint8_t status); +static uint32_t rpmsg_virtio_get_features_(FAR struct virtio_device *dev); +static void rpmsg_virtio_set_features(FAR struct virtio_device *dev, + uint32_t feature); +static void rpmsg_virtio_notify(FAR struct virtqueue *vq); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct rpmsg_ops_s g_rpmsg_virtio_ops = +{ + .wait = rpmsg_virtio_wait, + .post = rpmsg_virtio_post, + .dump = rpmsg_virtio_dump, + .get_cpuname = rpmsg_virtio_get_cpuname, + .get_tx_buffer_size = rpmsg_virtio_get_tx_buffer_size, + .get_rx_buffer_size = rpmsg_virtio_get_rx_buffer_size_, +}; + +static const struct virtio_dispatch g_rpmsg_virtio_dispatch = +{ + .create_virtqueues = rpmsg_virtio_create_virtqueues_, + .get_status = rpmsg_virtio_get_status_, + .set_status = rpmsg_virtio_set_status_, + .get_features = rpmsg_virtio_get_features_, + .set_features = rpmsg_virtio_set_features, + .notify = rpmsg_virtio_notify, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR struct rpmsg_virtio_priv_s * +rpmsg_virtio_get_priv(FAR struct virtio_device *vdev) +{ + FAR struct rpmsg_virtio_device *rvdev = vdev->priv; + + return metal_container_of(rvdev, struct rpmsg_virtio_priv_s, rvdev); +} + +static int rpmsg_virtio_create_virtqueues_(FAR struct virtio_device *vdev, + unsigned int flags, + unsigned int nvqs, + FAR const char *names[], + vq_callback callbacks[]) +{ + int ret; + int i; + + if (nvqs > vdev->vrings_num) + { + return ERROR_VQUEUE_INVLD_PARAM; + } + + /* Initialize virtqueue for each vring */ + + for (i = 0; i < nvqs; i++) + { + FAR struct virtio_vring_info *vinfo = &vdev->vrings_info[i]; + FAR struct vring_alloc_info *valloc = &vinfo->info; +#ifndef CONFIG_OPENAMP_VIRTIO_DEVICE_ONLY + if (vdev->role == VIRTIO_DEV_DRIVER) + { + size_t offset; + + offset = metal_io_virt_to_offset(vinfo->io, valloc->vaddr); + metal_io_block_set(vinfo->io, offset, 0, + vring_size(valloc->num_descs, valloc->align)); + } +#endif + + ret = virtqueue_create(vdev, i, names[i], valloc, + callbacks[i], vdev->func->notify, + vinfo->vq); + if (ret < 0) + { + return ret; + } + } + + return 0; +} + +static uint8_t rpmsg_virtio_get_status_(FAR struct virtio_device *vdev) +{ + FAR struct rpmsg_virtio_priv_s *priv = rpmsg_virtio_get_priv(vdev); + + return priv->rsc->rpmsg_vdev.status; +} + +static void rpmsg_virtio_set_status_(FAR struct virtio_device *vdev, + uint8_t status) +{ + FAR struct rpmsg_virtio_priv_s *priv = rpmsg_virtio_get_priv(vdev); + + priv->rsc->rpmsg_vdev.status = status; +} + +static uint32_t rpmsg_virtio_get_features_(FAR struct virtio_device *vdev) +{ + FAR struct rpmsg_virtio_priv_s *priv = rpmsg_virtio_get_priv(vdev); + + return priv->rsc->rpmsg_vdev.dfeatures; +} + +static void rpmsg_virtio_set_features(FAR struct virtio_device *vdev, + uint32_t features) +{ + FAR struct rpmsg_virtio_priv_s *priv = rpmsg_virtio_get_priv(vdev); + + priv->rsc->rpmsg_vdev.gfeatures = features; +} + +static void rpmsg_virtio_notify(FAR struct virtqueue *vq) +{ + FAR struct virtio_device *vdev = vq->vq_dev; + FAR struct rpmsg_virtio_priv_s *priv = rpmsg_virtio_get_priv(vdev); + + RPMSG_VIRTIO_NOTIFY(priv->dev, vdev->vrings_info->notifyid); +} + +static bool rpmsg_virtio_is_recursive(FAR struct rpmsg_virtio_priv_s *priv) +{ + return nxsched_gettid() == priv->tid; +} + +static int rpmsg_virtio_wait(FAR struct rpmsg_s *rpmsg, FAR sem_t *sem) +{ + FAR struct rpmsg_virtio_priv_s *priv = + (FAR struct rpmsg_virtio_priv_s *)rpmsg; + int ret; + + if (!rpmsg_virtio_is_recursive(priv)) + { + return nxsem_wait_uninterruptible(sem); + } + + while (1) + { + ret = nxsem_trywait(sem); + if (ret >= 0) + { + break; + } + + nxsem_wait(&priv->semtx); + virtqueue_notification(priv->rvdev.rvq); + } + + return ret; +} + +static void rpmsg_virtio_wakeup_tx(FAR struct rpmsg_virtio_priv_s *priv) +{ + int semcount; + + nxsem_get_value(&priv->semtx, &semcount); + while (semcount++ < 1) + { + nxsem_post(&priv->semtx); + } +} + +static int rpmsg_virtio_post(FAR struct rpmsg_s *rpmsg, FAR sem_t *sem) +{ + FAR struct rpmsg_virtio_priv_s *priv = + (FAR struct rpmsg_virtio_priv_s *)rpmsg; + int semcount; + int ret; + + nxsem_get_value(sem, &semcount); + ret = nxsem_post(sem); + + if (priv && semcount >= 0) + { + rpmsg_virtio_wakeup_tx(priv); + } + + return ret; +} + +#ifdef CONFIG_OPENAMP_DEBUG +static int rpmsg_virtio_buffer_nused(FAR struct rpmsg_virtio_device *rvdev, + bool rx) +{ + FAR struct virtqueue *vq = rx ? rvdev->rvq : rvdev->svq; + uint16_t nused = vq->vq_ring.avail->idx - vq->vq_ring.used->idx; + + if ((rpmsg_virtio_get_role(rvdev) == RPMSG_HOST) ^ rx) + { + return nused; + } + else + { + return vq->vq_nentries - nused; + } +} + +static void rpmsg_virtio_dump_buffer(FAR struct rpmsg_virtio_device *rvdev, + bool rx) +{ + FAR struct virtqueue *vq = rx ? rvdev->rvq : rvdev->svq; + int num; + int i; + + num = rpmsg_virtio_buffer_nused(rvdev, rx); + metal_log(METAL_LOG_EMERGENCY, + " %s buffer, total %d, pending %d\n", + rx ? "RX" : "TX", vq->vq_nentries, num); + + for (i = 0; i < num; i++) + { + FAR void *addr; + int desc_idx; + + if ((rpmsg_virtio_get_role(rvdev) == RPMSG_HOST) ^ rx) + { + desc_idx = (vq->vq_ring.used->idx + i) & (vq->vq_nentries - 1); + desc_idx = vq->vq_ring.avail->ring[desc_idx]; + } + else + { + desc_idx = (vq->vq_ring.avail->idx + i) & (vq->vq_nentries - 1); + desc_idx = vq->vq_ring.used->ring[desc_idx].id; + } + + addr = metal_io_phys_to_virt(vq->shm_io, + vq->vq_ring.desc[desc_idx].addr); + if (addr) + { + FAR struct rpmsg_hdr *hdr = addr; + FAR struct rpmsg_endpoint *ept; + + ept = rpmsg_get_ept_from_addr(&rvdev->rdev, + rx ? hdr->dst : hdr->src); + if (ept) + { + metal_log(METAL_LOG_EMERGENCY, + " %s buffer %p hold by %s\n", + rx ? "RX" : "TX", hdr, ept->name); + } + } + } +} + +static void rpmsg_virtio_dump(FAR struct rpmsg_s *rpmsg) +{ + FAR struct rpmsg_virtio_priv_s *priv = + (FAR struct rpmsg_virtio_priv_s *)rpmsg; + FAR struct rpmsg_virtio_device *rvdev = &priv->rvdev; + FAR struct rpmsg_device *rdev = rpmsg->rdev; + FAR struct rpmsg_endpoint *ept; + FAR struct metal_list *node; + bool needlock = true; + + if (!rvdev->vdev) + { + return; + } + + if (up_interrupt_context() || sched_idletask() || + nxmutex_is_hold(&rdev->lock)) + { + needlock = false; + } + + if (needlock) + { + metal_mutex_acquire(&rdev->lock); + } + + metal_log(METAL_LOG_EMERGENCY, + "Dump rpmsg info between cpu (master: %s)%s <==> %s:\n", + rpmsg_virtio_get_role(rvdev) == RPMSG_HOST ? "yes" : "no", + CONFIG_RPMSG_LOCAL_CPUNAME, rpmsg_get_cpuname(rdev)); + + metal_log(METAL_LOG_EMERGENCY, "rpmsg vq RX:\n"); + virtqueue_dump(rvdev->rvq); + metal_log(METAL_LOG_EMERGENCY, "rpmsg vq TX:\n"); + virtqueue_dump(rvdev->svq); + + metal_log(METAL_LOG_EMERGENCY, " rpmsg ept list:\n"); + + metal_list_for_each(&rdev->endpoints, node) + { + ept = metal_container_of(node, struct rpmsg_endpoint, node); + metal_log(METAL_LOG_EMERGENCY, " ept %s\n", ept->name); + } + + metal_log(METAL_LOG_EMERGENCY, " rpmsg buffer list:\n"); + + rpmsg_virtio_dump_buffer(rvdev, true); + rpmsg_virtio_dump_buffer(rvdev, false); + + if (needlock) + { + metal_mutex_release(&rdev->lock); + } +} +#else +static void rpmsg_virtio_dump(FAR struct rpmsg_s *rpmsg) +{ + /* Nothing */ +} +#endif + +static FAR const char *rpmsg_virtio_get_cpuname(FAR struct rpmsg_s *rpmsg) +{ + FAR struct rpmsg_virtio_priv_s *priv = + (FAR struct rpmsg_virtio_priv_s *)rpmsg; + + return RPMSG_VIRTIO_GET_CPUNAME(priv->dev); +} + +static int rpmsg_virtio_get_tx_buffer_size(FAR struct rpmsg_s *rpmsg) +{ + return rpmsg_virtio_get_buffer_size(rpmsg->rdev); +} + +static int rpmsg_virtio_get_rx_buffer_size_(FAR struct rpmsg_s *rpmsg) +{ + return rpmsg_virtio_get_rx_buffer_size(rpmsg->rdev); +} + +static void rpmsg_virtio_wakeup_rx(FAR struct rpmsg_virtio_priv_s *priv) +{ + int semcount; + + nxsem_get_value(&priv->semrx, &semcount); + if (semcount < 1) + { + nxsem_post(&priv->semrx); + } +} + +static int rpmsg_virtio_callback(FAR void *arg, uint32_t vqid) +{ + FAR struct rpmsg_virtio_priv_s *priv = arg; + FAR struct rpmsg_virtio_device *rvdev = &priv->rvdev; + FAR struct virtio_device *vdev = rvdev->vdev; + FAR struct virtqueue *rvq = rvdev->rvq; + + if (vqid == RPMSG_VIRTIO_NOTIFY_ALL || + vqid == vdev->vrings_info[rvq->vq_queue_index].notifyid) + { + rpmsg_virtio_wakeup_rx(priv); + } + + return OK; +} + +static int rpmsg_virtio_notify_wait(FAR struct rpmsg_device *rdev, + uint32_t id) +{ + FAR struct rpmsg_virtio_priv_s *priv = + metal_container_of(rdev, struct rpmsg_virtio_priv_s, rvdev.rdev); + + if (!rpmsg_virtio_is_recursive(priv)) + { + return -EAGAIN; + } + + /* Wait to wakeup */ + + nxsem_tickwait(&priv->semtx, MSEC2TICK(RPMSG_VIRTIO_TIMEOUT_MS)); + virtqueue_notification(priv->rvdev.rvq); + + return 0; +} + +static int rpmsg_virtio_start(FAR struct rpmsg_virtio_priv_s *priv) +{ + FAR struct virtio_vring_info *rvrings = priv->rvrings; + FAR struct virtio_device *vdev = &priv->vdev; + FAR struct rpmsg_virtio_rsc_s *rsc; + struct rpmsg_virtio_config config; + FAR void *shbuf0; + FAR void *shbuf1; + uint32_t align0; + uint32_t align1; + uint32_t tbsz; + uint32_t v0sz; + uint32_t v1sz; + uint32_t shbufsz0; + uint32_t shbufsz1; + int ret; + + rsc = RPMSG_VIRTIO_GET_RESOURCE(priv->dev); + if (!rsc) + { + return -EINVAL; + } + + priv->rsc = rsc; + + vdev->notifyid = RPMSG_VIRTIO_NOTIFYID; + vdev->vrings_num = rsc->rpmsg_vdev.num_of_vrings; + vdev->role = RPMSG_VIRTIO_IS_MASTER(priv->dev) ? RPMSG_HOST : RPMSG_REMOTE; + vdev->func = &g_rpmsg_virtio_dispatch; + + align0 = rsc->rpmsg_vring0.align; + align1 = rsc->rpmsg_vring1.align; + tbsz = ALIGN_UP(sizeof(struct rpmsg_virtio_rsc_s), MAX(align0, align1)); + v0sz = ALIGN_UP(vring_size(rsc->rpmsg_vring0.num, align0), align0); + v1sz = ALIGN_UP(vring_size(rsc->rpmsg_vring1.num, align1), align1); + + shbuf0 = (FAR char *)rsc + tbsz + v0sz + v1sz; + shbufsz0 = rsc->config.r2h_buf_size * rsc->rpmsg_vring0.num; + shbuf1 = shbuf0 + shbufsz0; + shbufsz1 = rsc->config.h2r_buf_size * rsc->rpmsg_vring1.num; + + rvrings[0].io = metal_io_get_region(); + rvrings[0].info.vaddr = (FAR char *)rsc + tbsz; + rvrings[0].info.num_descs = rsc->rpmsg_vring0.num; + rvrings[0].info.align = rsc->rpmsg_vring0.align; + rvrings[0].vq = virtqueue_allocate(rsc->rpmsg_vring0.num); + if (rvrings[0].vq == NULL) + { + return -ENOMEM; + } + + rvrings[1].io = metal_io_get_region(); + rvrings[1].info.vaddr = (FAR char *)rsc + tbsz + v0sz; + rvrings[1].info.num_descs = rsc->rpmsg_vring1.num; + rvrings[1].info.align = rsc->rpmsg_vring1.align; + rvrings[1].vq = virtqueue_allocate(rsc->rpmsg_vring1.num); + if (rvrings[1].vq == NULL) + { + ret = -ENOMEM; + goto err_vq0; + } + + vdev->vrings_info = &rvrings[0]; + + rpmsg_virtio_init_shm_pool(&priv->pool[0], shbuf0, shbufsz0); + rpmsg_virtio_init_shm_pool(&priv->pool[1], shbuf1, shbufsz1); + + config.h2r_buf_size = rsc->config.h2r_buf_size; + config.r2h_buf_size = rsc->config.r2h_buf_size; + config.split_shpool = true; + + ret = rpmsg_init_vdev_with_config(&priv->rvdev, vdev, rpmsg_ns_bind, + metal_io_get_region(), + priv->pool, &config); + if (ret != 0) + { + rpmsgerr("rpmsg_init_vdev failed %d\n", ret); + ret = -ENOMEM; + goto err_vq1; + } + + priv->rvdev.rdev.ns_unbind_cb = rpmsg_ns_unbind; + priv->rvdev.rdev.notify_wait_cb = rpmsg_virtio_notify_wait; + + RPMSG_VIRTIO_REGISTER_CALLBACK(priv->dev, rpmsg_virtio_callback, priv); + + rpmsg_virtio_wakeup_rx(priv); + + /* Broadcast device_created to all registers */ + + rpmsg_device_created(&priv->rpmsg); + + return 0; + +err_vq1: + virtqueue_free(rvrings[1].vq); +err_vq0: + virtqueue_free(rvrings[0].vq); + return ret; +} + +static int rpmsg_virtio_thread(int argc, FAR char *argv[]) +{ + FAR struct rpmsg_virtio_priv_s *priv = (FAR struct rpmsg_virtio_priv_s *) + ((uintptr_t)strtoul(argv[2], NULL, 0)); + int ret; + + priv->tid = nxsched_gettid(); + + ret = rpmsg_virtio_start(priv); + if (ret < 0) + { + rpmsgerr("rpmsg virtio thread start failed %d\n", ret); + return ret; + } + + while (1) + { + nxsem_wait_uninterruptible(&priv->semrx); + virtqueue_notification(priv->rvdev.rvq); + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int rpmsg_virtio_initialize(FAR struct rpmsg_virtio_s *dev) +{ + FAR struct rpmsg_virtio_priv_s *priv; + FAR char *argv[3]; + char arg1[32]; + char name[32]; + int ret; + + priv = kmm_zalloc(sizeof(struct rpmsg_virtio_priv_s)); + if (priv == NULL) + { + return -ENOMEM; + } + + priv->dev = dev; + nxsem_init(&priv->semrx, 0, 0); + nxsem_init(&priv->semtx, 0, 0); + + snprintf(name, sizeof(name), "/dev/rpmsg/%s", + RPMSG_VIRTIO_GET_CPUNAME(dev)); + ret = rpmsg_register(name, &priv->rpmsg, &g_rpmsg_virtio_ops); + if (ret < 0) + { + goto err_driver; + } + + snprintf(arg1, sizeof(arg1), "%p", priv); + argv[0] = (FAR char *)RPMSG_VIRTIO_GET_CPUNAME(dev); + argv[1] = arg1; + argv[2] = NULL; + + ret = kthread_create("rpmsg_virtio", CONFIG_RPMSG_VIRTIO_PRIORITY, + CONFIG_RPMSG_VIRTIO_STACKSIZE, + rpmsg_virtio_thread, argv); + if (ret < 0) + { + goto err_thread; + } + + return OK; + +err_thread: + rpmsg_unregister(name, &priv->rpmsg); + +err_driver: + nxsem_destroy(&priv->semrx); + nxsem_destroy(&priv->semtx); + kmm_free(priv); + + return ret; +} diff --git a/include/nuttx/rpmsg/rpmsg_virtio.h b/include/nuttx/rpmsg/rpmsg_virtio.h new file mode 100644 index 0000000000..fcb4c29242 --- /dev/null +++ b/include/nuttx/rpmsg/rpmsg_virtio.h @@ -0,0 +1,186 @@ +/**************************************************************************** + * include/nuttx/rpmsg/rpmsg_virtio.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_VIRTIO_H +#define __INCLUDE_NUTTX_RPMSG_RPMSG_VIRTIO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_RPMSG_VIRTIO + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RPMSG_VIRTIO_NOTIFY_ALL UINT32_MAX + +/* Access macros ************************************************************/ + +/**************************************************************************** + * Name: RPMSG_VIRTIO_GET_CPUNAME + * + * Description: + * Get remote cpu name + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Cpu name on success, NULL on failure. + * + ****************************************************************************/ + +#define RPMSG_VIRTIO_GET_CPUNAME(d) \ + ((d)->ops->get_cpuname ? (d)->ops->get_cpuname(d) : "") + +/**************************************************************************** + * Name: RPMSG_VIRTIO_GET_RESOURCE + * + * Description: + * Get rpmsg virtio resource + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Resource pointer on success, NULL on failure + * + ****************************************************************************/ + +#define RPMSG_VIRTIO_GET_RESOURCE(d) \ + ((d)->ops->get_resource ? (d)->ops->get_resource(d) : NULL) + +/**************************************************************************** + * Name: RPMSG_VIRTIO_IS_MASTER + * + * Description: + * Is master or not + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * True master, false remote + * + ****************************************************************************/ + +#define RPMSG_VIRTIO_IS_MASTER(d) \ + ((d)->ops->is_master ? (d)->ops->is_master(d) : false) + +/**************************************************************************** + * Name: RPMSG_VIRTIO_REGISTER_CALLBACK + * + * Description: + * Attach to receive a callback when something is received on RPTUN + * + * Input Parameters: + * dev - Device-specific state data + * callback - The function to be called when something has been received + * arg - A caller provided value to return with the callback + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#define RPMSG_VIRTIO_REGISTER_CALLBACK(d,c,a) \ + ((d)->ops->register_callback ? (d)->ops->register_callback(d,c,a) : -ENOSYS) + +/**************************************************************************** + * Name: RPMSG_VIRTIO_NOTIFY + * + * Description: + * Notify remote core there is a message to get. + * + * Input Parameters: + * dev - Device-specific state data + * vqid - Message to notify + * + * Returned Value: + * OK unless an error occurs. Then a negated errno value is returned + * + ****************************************************************************/ + +#define RPMSG_VIRTIO_NOTIFY(d,v) \ + ((d)->ops->notify ? (d)->ops->notify(d,v) : -ENOSYS) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef CODE int (*rpmsg_virtio_callback_t)(FAR void *arg, uint32_t vqid); + +struct aligned_data(8) rpmsg_virtio_rsc_s +{ + struct resource_table rsc_tbl_hdr; + uint32_t offset[2]; + struct fw_rsc_trace log_trace; + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; + struct fw_rsc_config config; +}; + +struct rpmsg_virtio_s; +struct rpmsg_virtio_ops_s +{ + CODE FAR const char *(*get_cpuname)(FAR struct rpmsg_virtio_s *dev); + CODE FAR struct rpmsg_virtio_rsc_s * + (*get_resource)(FAR struct rpmsg_virtio_s *dev); + CODE int (*is_master)(FAR struct rpmsg_virtio_s *dev); + CODE int (*notify)(FAR struct rpmsg_virtio_s *dev, uint32_t vqid); + CODE int (*register_callback)(FAR struct rpmsg_virtio_s *dev, + rpmsg_virtio_callback_t callback, + FAR void *arg); +}; + +struct rpmsg_virtio_s +{ + FAR const struct rpmsg_virtio_ops_s *ops; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +int rpmsg_virtio_initialize(FAR struct rpmsg_virtio_s *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_RPTUN */ + +#endif /* __INCLUDE_NUTTX_RPMSG_RPMSG_VIRTIO_H */