269 lines
8.3 KiB
C
269 lines
8.3 KiB
C
/****************************************************************************
|
|
* fs/v9fs/virtio_9p.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 <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/spinlock.h>
|
|
#include <nuttx/virtio/virtio.h>
|
|
|
|
#include "client.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define VIRTIO_9P_MOUNT_TAG 0
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct virtio_9p_config_s
|
|
{
|
|
uint16_t tag_len;
|
|
char tag[NAME_MAX];
|
|
};
|
|
|
|
struct virtio_9p_priv_s
|
|
{
|
|
FAR struct virtio_device *vdev;
|
|
struct v9fs_transport_s transport;
|
|
struct virtio_driver vdrv;
|
|
spinlock_t lock;
|
|
char tag[0];
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* VirtIO transport api for 9p */
|
|
|
|
static int virtio_9p_create(FAR struct v9fs_transport_s **transport,
|
|
FAR const char *args);
|
|
static void virtio_9p_destroy(FAR struct v9fs_transport_s *transport);
|
|
static int virtio_9p_request(FAR struct v9fs_transport_s *transport,
|
|
FAR struct v9fs_payload_s *payload);
|
|
static void virtio_9p_done(FAR struct virtqueue *vq);
|
|
static int virtio_9p_probe(FAR struct virtio_device *vdev);
|
|
static void virtio_9p_remove(FAR struct virtio_device *vdev);
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
const struct v9fs_transport_ops_s g_virtio_9p_transport_ops =
|
|
{
|
|
virtio_9p_create, /* create */
|
|
virtio_9p_request, /* request */
|
|
virtio_9p_destroy, /* close */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_9p_create
|
|
****************************************************************************/
|
|
|
|
static int virtio_9p_create(FAR struct v9fs_transport_s **transport,
|
|
FAR const char *args)
|
|
{
|
|
FAR struct virtio_9p_priv_s *priv;
|
|
FAR const char *start;
|
|
FAR const char *end;
|
|
size_t length;
|
|
int ret;
|
|
|
|
/* Parse device name */
|
|
|
|
start = strstr(args, "tag=");
|
|
if (start == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
start += 4;
|
|
end = strchr(start, ',');
|
|
length = end ? end - start + 1 : strlen(start) + 1;
|
|
priv = kmm_zalloc(sizeof(struct virtio_9p_priv_s) + length);
|
|
if (priv == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
spin_lock_init(&priv->lock);
|
|
memcpy(priv->tag, start, length);
|
|
priv->vdrv.device = VIRTIO_ID_9P;
|
|
priv->vdrv.probe = virtio_9p_probe;
|
|
priv->vdrv.remove = virtio_9p_remove;
|
|
priv->transport.ops = &g_virtio_9p_transport_ops;
|
|
*transport = &priv->transport;
|
|
ret = virtio_register_driver(&priv->vdrv);
|
|
if (ret < 0)
|
|
{
|
|
kmm_free(priv);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_9p_destroy
|
|
****************************************************************************/
|
|
|
|
static void virtio_9p_destroy(FAR struct v9fs_transport_s *transport)
|
|
{
|
|
FAR struct virtio_9p_priv_s *priv =
|
|
container_of(transport, struct virtio_9p_priv_s, transport);
|
|
virtio_unregister_driver(&priv->vdrv);
|
|
kmm_free(priv);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_9p_request
|
|
****************************************************************************/
|
|
|
|
static int virtio_9p_request(FAR struct v9fs_transport_s *transport,
|
|
FAR struct v9fs_payload_s *payload)
|
|
{
|
|
FAR struct virtio_9p_priv_s *priv =
|
|
container_of(transport, struct virtio_9p_priv_s, transport);
|
|
FAR struct virtqueue *vq = priv->vdev->vrings_info[0].vq;
|
|
struct virtqueue_buf vb[payload->wcount + payload->rcount];
|
|
irqstate_t flags;
|
|
size_t i;
|
|
int ret;
|
|
|
|
for (i = 0; i < payload->wcount; i++)
|
|
{
|
|
vb[i].buf = payload->wiov[i].iov_base;
|
|
vb[i].len = payload->wiov[i].iov_len;
|
|
}
|
|
|
|
for (i = 0; i < payload->rcount; i++)
|
|
{
|
|
vb[payload->wcount + i].buf = payload->riov[i].iov_base;
|
|
vb[payload->wcount + i].len = payload->riov[i].iov_len;
|
|
}
|
|
|
|
flags = spin_lock_irqsave(&priv->lock);
|
|
ret = virtqueue_add_buffer(vq, vb, payload->wcount, payload->rcount,
|
|
payload);
|
|
if (ret < 0)
|
|
{
|
|
vrterr("virtqueue_add_buffer failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
virtqueue_kick(vq);
|
|
out:
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_9p_done
|
|
****************************************************************************/
|
|
|
|
static void virtio_9p_done(FAR struct virtqueue *vq)
|
|
{
|
|
FAR struct virtio_9p_priv_s *priv = vq->vq_dev->priv;
|
|
FAR struct v9fs_payload_s *payload;
|
|
|
|
for (; ; )
|
|
{
|
|
payload = virtqueue_get_buffer_lock(vq, NULL, NULL, &priv->lock);
|
|
if (payload == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
v9fs_transport_done(payload, 0);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_9p_probe
|
|
****************************************************************************/
|
|
|
|
static int virtio_9p_probe(FAR struct virtio_device *vdev)
|
|
{
|
|
FAR struct virtio_9p_priv_s *priv = container_of(vdev->priv,
|
|
struct virtio_9p_priv_s, vdrv);
|
|
struct virtio_9p_config_s config;
|
|
FAR const char *vqname[1];
|
|
vq_callback callback[1];
|
|
int ret;
|
|
|
|
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
|
|
virtio_negotiate_features(vdev, 1 << VIRTIO_9P_MOUNT_TAG);
|
|
virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK);
|
|
|
|
if (!virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG))
|
|
{
|
|
virtio_reset_device(vdev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
virtio_read_config_member(vdev, struct virtio_9p_config_s, tag_len,
|
|
&config.tag_len);
|
|
virtio_read_config(vdev, offsetof(struct virtio_9p_config_s, tag),
|
|
&config.tag, config.tag_len);
|
|
config.tag[config.tag_len] = '\0';
|
|
if (strcmp(config.tag, priv->tag))
|
|
{
|
|
virtio_reset_device(vdev);
|
|
return -ENOENT;
|
|
}
|
|
|
|
vqname[0] = "virtio_9p_vq";
|
|
callback[0] = virtio_9p_done;
|
|
ret = virtio_create_virtqueues(vdev, 0, 1, vqname, callback);
|
|
if (ret < 0)
|
|
{
|
|
vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret);
|
|
virtio_reset_device(vdev);
|
|
return ret;
|
|
}
|
|
|
|
priv->vdev = vdev;
|
|
vdev->priv = priv;
|
|
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
|
|
virtqueue_enable_cb(vdev->vrings_info[0].vq);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: virtio_9p_remove
|
|
****************************************************************************/
|
|
|
|
static void virtio_9p_remove(FAR struct virtio_device *vdev)
|
|
{
|
|
virtio_delete_virtqueues(vdev);
|
|
virtio_reset_device(vdev);
|
|
}
|