2024-02-06 04:12:15 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* 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>
|
2024-04-24 04:20:52 +02:00
|
|
|
#include <rpmsg/rpmsg_internal.h>
|
2024-02-06 04:12:15 +01:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* 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);
|
2024-06-19 13:54:54 +02:00
|
|
|
static FAR const char *
|
|
|
|
rpmsg_virtio_get_local_cpuname(FAR struct rpmsg_s *rpmsg);
|
2024-02-06 04:12:15 +01:00
|
|
|
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,
|
2024-06-19 13:54:54 +02:00
|
|
|
.get_local_cpuname = rpmsg_virtio_get_local_cpuname,
|
2024-02-06 04:12:15 +01:00
|
|
|
.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
|
|
|
|
|
2024-06-19 13:54:54 +02:00
|
|
|
static FAR const char *
|
|
|
|
rpmsg_virtio_get_local_cpuname(FAR struct rpmsg_s *rpmsg)
|
|
|
|
{
|
|
|
|
FAR struct rpmsg_virtio_priv_s *priv =
|
|
|
|
(FAR struct rpmsg_virtio_priv_s *)rpmsg;
|
|
|
|
|
|
|
|
return RPMSG_VIRTIO_GET_LOCAL_CPUNAME(priv->dev);
|
|
|
|
}
|
|
|
|
|
2024-02-06 04:12:15 +01:00
|
|
|
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;
|
|
|
|
}
|