| /* | |
| * 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); |