rpmsg virtio: add rpmsg virtio wrapper support
Rpmsg VirtIO is a virtio transport implementation for Rpmsg, and it's different to the rptun framework. rpmsg_virtio.c implements the rpmsg virtio transport layer by itself to avoid use the remoteproc implementation in OpenAMP to save code size, so it can be treated as a lightweight version of rptun. Therefore, rpmsg_virtio.c only support the communication feature and do not support contoll the life cycle of the remote core. But benefit by it's small footprint, it can be used in the chips with small flash. Signed-off-by: Yongrong Wang <wangyongrong@xiaomi.com> Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
This commit is contained in:
parent
e359c661d8
commit
af0bde8bca
5 changed files with 846 additions and 0 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
634
drivers/rpmsg/rpmsg_virtio.c
Normal file
634
drivers/rpmsg/rpmsg_virtio.c
Normal file
|
@ -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 <nuttx/config.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/kthread.h>
|
||||
#include <nuttx/semaphore.h>
|
||||
#include <nuttx/rpmsg/rpmsg_virtio.h>
|
||||
|
||||
/****************************************************************************
|
||||
* 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;
|
||||
}
|
186
include/nuttx/rpmsg/rpmsg_virtio.h
Normal file
186
include/nuttx/rpmsg/rpmsg_virtio.h
Normal file
|
@ -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 <nuttx/config.h>
|
||||
|
||||
#ifdef CONFIG_RPMSG_VIRTIO
|
||||
|
||||
#include <nuttx/rpmsg/rpmsg.h>
|
||||
#include <openamp/rpmsg_virtio.h>
|
||||
|
||||
/****************************************************************************
|
||||
* 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 */
|
Loading…
Reference in a new issue