blob: ac3c6da874c7a386557789145c704b90c2d4f644 [file] [log] [blame] [edit]
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#pragma once
#include <stdint.h>
#define VQ_DEV_POLL(vq) ((((vq)->a_ring_last_seen + 1) & ((vq)->queue_len - 1)) != (vq)->avail_ring->idx)
#define VQ_DRV_POLL(vq) ((((vq)->u_ring_last_seen + 1) & ((vq)->queue_len - 1)) != (vq)->used_ring->idx)
/* Flags for the buffers in the descriptor table */
typedef enum vq_flags {
VQ_READ = 0,
VQ_WRITE,
VQ_RW
} vq_flags_t;
/* Ring of available buffers */
typedef struct vq_vring_avail {
uint16_t flags; /* Interrupt suppression flag */
uint16_t idx; /* Index of the next free entry in the ring */
uint16_t ring[]; /* The ring of descriptor table entries */
} vq_vring_avail_t;
/* Element of a used ring buffer */
typedef struct vq_vring_used_elem {
uint32_t id; /* Index in descriptor table */
uint32_t len; /* Length of data that was written by the device */
} vq_vring_used_elem_t;
/* Ring of used buffers */
typedef struct vq_vring_used {
uint16_t flags; /* Interrupt suppression flag */
uint16_t idx; /* Index of the next free entry in the ring */
struct vq_vring_used_elem ring[]; /* The ring of descriptor table entries */
} vq_vring_used_t;
/* Entry in the descriptor table */
typedef struct vq_vring_desc {
uint64_t addr; /* Address of the buffer in the shared memory */
uint32_t len; /* Length of the buffer */
uint16_t flags; /* Flag of the buffer */
uint16_t next; /* Index of the next descriptor table entry in the scatter list */
} vq_vring_desc_t;
/* Handle for iterating through a scatter list */
typedef struct virtqueue_ring_object {
uint32_t cur; /* The current index in desc table */
uint32_t first; /* The head of the scatter list in desc table */
} virtqueue_ring_object_t;
/* A device-side virtqueue */
typedef struct virtqueue_device {
void (*notify)(void); /* Notify function to wake-up driver side */
void *cookie; /* User-defined cookie */
unsigned queue_len; /* The number of entries in rings and descriptor table */
unsigned a_ring_last_seen; /* Index of the last seen element in the available ring */
struct vq_vring_avail *avail_ring; /* The available ring */
struct vq_vring_used *used_ring; /* The used ring */
struct vq_vring_desc *desc_table; /* The descriptor table */
} virtqueue_device_t;
/* A driver-side virtqueue */
typedef struct virtqueue_driver {
void (*notify)(void); /* Notify function to wake-up device side */
void *cookie; /* User-defined cookie */
unsigned queue_len; /* The number of entries in rings and descriptor table */
unsigned free_desc_head; /* The head of the free list in the descriptor table */
unsigned u_ring_last_seen; /* Index of the last seen element in the used ring */
struct vq_vring_avail *avail_ring; /* The available ring */
struct vq_vring_used *used_ring; /* The used ring */
struct vq_vring_desc *desc_table; /* The descritor table */
} virtqueue_driver_t;
/* Initialise a driver-side virtqueue.
* @param vq the driver virtqueue
* @param queue_len the length of rings and descriptor table
* @param avail_ring pointer to the shared available ring
* @param used_ring pointer to the shared used ring
* @param desc pointer to the shared descriptor table
* @param notify the notify function to wake up device side
* @param cookie user's cookie
*/
void virtqueue_init_driver(virtqueue_driver_t *vq, unsigned queue_len, vq_vring_avail_t *avail_ring,
vq_vring_used_t *used_ring, vq_vring_desc_t *desc, void (*notify)(void),
void *cookie);
/* Initialise a device-side virtqueue.
* @param vq the device virtqueue
* @param queue_len the length of rings and descriptor table
* @param avail_ring pointer to the shared available ring
* @param used_ring pointer to the shared used ring
* @param desc pointer to the shared descriptor table
* @param notify the notify function to wake up driver side
* @param cookie user's cookie
*/
void virtqueue_init_device(virtqueue_device_t *vq, unsigned queue_len, vq_vring_avail_t *avail_ring,
vq_vring_used_t *used_ring, vq_vring_desc_t *desc, void (*notify)(void),
void *cookie);
/* Initialise the descriptor table (create the free list) */
void virtqueue_init_desc_table(vq_vring_desc_t *table, unsigned queue_len);
/* Initialise the available ring */
void virtqueue_init_avail_ring(vq_vring_avail_t *ring);
/* Initialise the used ring */
void virtqueue_init_used_ring(vq_vring_used_t *ring);
/** Driver side **/
/* Add initial buffer to available ring
* @param vq the driver virtqueue
* @param obj the handle to the ring object. If the handle was just initialized, it will
* create a new entry in the ring. Any following calls with the same handle
* will then chain the buffer in the descriptor table but under the same ring entry.
* @param buf the buffer to add
* @param len the length of the buffer
* @param flag the flag of the buffer
* @return 1 on success, 0 on failure (ring full)
*/
int virtqueue_add_available_buf(virtqueue_driver_t *vq, virtqueue_ring_object_t *obj,
void *buf, unsigned len, vq_flags_t flag);
/* Get buffer from used ring. Dequeue a buffer from the used ring and get an iterator to the scatterlist
* @param vq the driver side virtqueue
* @param robj a pointer to the iterator that will be returned
* @param len a pointer to the length of the buffer that was actually used by the device
* @return 1 on success, 0 on failure (ring empty)
*/
int virtqueue_get_used_buf(virtqueue_driver_t *vq, virtqueue_ring_object_t *robj, uint32_t *len);
/** Device side **/
/* Add buffer to used ring. Takes an ring object (obtained from a get_available_buf call) and passes it
* to the used ring.
* @param vq the device side virtqueue
* @param robj a pointer to the ring object
* @param len the length of the buffer that the device actually used
* @return 1 on success, 0 on failure (ring full)
*/
int virtqueue_add_used_buf(virtqueue_device_t *vq, virtqueue_ring_object_t *robj, uint32_t len);
/* Get buffer from available ring. Returns an iterator to the next available buffer list
* @param vq the device side virtqueue
* @param robj a pointer to the iterator that will be returned
* @return 1 on success, 0 on failure (ring empty)
*/
int virtqueue_get_available_buf(virtqueue_device_t *vq, virtqueue_ring_object_t *robj);
/** Iteration functions **/
/* Initialise a ring object */
void virtqueue_init_ring_object(virtqueue_ring_object_t *obj);
/* Get the size of a scattered list in the available ring from its handle
* @param vq the device side virtqueue
* @param robj a pointer to the ring object/handle
* @return the length of the scatterlist
*/
uint32_t virtqueue_scattered_available_size(virtqueue_device_t *vq, virtqueue_ring_object_t *robj);
/* Iteration function through an available buffer scatterlist. Returns the next buffer in the list.
* @param vq the device side virtqueue
* @param robj the handle/iterator upon which to iterate
* @param buf a pointer to the address of the returned buffer
* @param len a pointer to the length of the returned buffer
* @param flag a pointer to the flag of the returned buffer
* @return 1 on success, 0 on failure (no more buffer available)
*/
int virtqueue_gather_available(virtqueue_device_t *vq, virtqueue_ring_object_t *robj,
void **buf, unsigned *len, vq_flags_t *flag);
/* Iteration function through a used buffer scatterlist. Returns the next buffer in the list.
* @param vq the driver side virtqueue
* @param robj the handle/iterator upon which to iterate
* @param buf a pointer to the address of the returned buffer
* @param len a pointer to the length of the returned buffer
* @param flag a pointer to the flag of the returned buffer
* @return 1 on success, 0 on failure (no more buffer available)
*/
int virtqueue_gather_used(virtqueue_driver_t *vq, virtqueue_ring_object_t *robj,
void **buf, unsigned *len, vq_flags_t *flag);