1 /* 2 * Copyright (c) 2024 Antmicro <www.antmicro.com> 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 #ifndef ZEPHYR_VIRTIO_VIRTQUEUE_H_ 8 #define ZEPHYR_VIRTIO_VIRTQUEUE_H_ 9 #include <stdint.h> 10 #include <stddef.h> 11 #include <zephyr/kernel.h> 12 13 #ifdef __cplusplus 14 extern "C" { 15 #endif 16 17 /* 18 * Based on Virtual I/O Device (VIRTIO) Version 1.3 specification: 19 * https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.pdf 20 */ 21 22 /** 23 * @brief Virtqueue Interface 24 * @defgroup virtqueue_interface Virtqueue Interface 25 * @ingroup virtio_interface 26 * @{ 27 */ 28 29 /** 30 * used in virtq_desc::flags, enables chaining descriptor via virtq_desc::next 31 */ 32 #define VIRTQ_DESC_F_NEXT 1 33 /** 34 * used in virtq_desc::flags, makes descriptor device writeable 35 */ 36 #define VIRTQ_DESC_F_WRITE 2 37 38 /** 39 * @brief virtqueue descriptor 40 * 41 * Describes a single buffer 42 */ 43 struct virtq_desc { 44 /** 45 * physical address of the buffer 46 */ 47 uint64_t addr; 48 /** 49 * length of the buffer 50 */ 51 uint32_t len; 52 /** 53 * buffer flags 54 */ 55 uint16_t flags; 56 /** 57 * chaining next descriptor, valid if flags & VIRTQ_DESC_F_NEXT 58 */ 59 uint16_t next; 60 }; 61 62 /** 63 * @brief virtqueue available ring 64 * 65 * Used to pass descriptors to the virtio device. Driver writeable, device readable 66 */ 67 struct virtq_avail { 68 /** 69 * ring flags, e.g. VIRTQ_AVAIL_F_NO_INTERRUPT, currently unused 70 */ 71 uint16_t flags; 72 /** 73 * head of the ring, by increasing it newly added descriptors are committed 74 */ 75 uint16_t idx; 76 /** 77 * ring with indexes of descriptors 78 */ 79 uint16_t ring[]; 80 }; 81 82 /** 83 * @brief used descriptor chain 84 * 85 * Describes a single descriptor chain returned by the virtio device 86 */ 87 struct virtq_used_elem { 88 /** 89 * index of the head of descriptor chain 90 */ 91 uint32_t id; 92 /** 93 * total amount of bytes written to descriptor chain by the virtio device 94 */ 95 uint32_t len; 96 }; 97 98 /** 99 * @brief virtqueue used ring 100 * 101 * Used to receive descriptors from the virtio device. Driver readable, device writeable 102 */ 103 struct virtq_used { 104 /** 105 * ring flags, e.g. VIRTQ_USED_F_NO_NOTIFY, currently unused 106 */ 107 uint16_t flags; 108 /** 109 * head of the ring 110 */ 111 uint16_t idx; 112 /** 113 * ring of struct virtq_used_elem 114 */ 115 struct virtq_used_elem ring[]; 116 }; 117 118 /** 119 * @brief receive callback function type 120 * 121 * @param opaque argument passed to the callback 122 * @param used_len total amount of bytes written to the descriptor chain by the virtio device 123 */ 124 typedef void (*virtq_receive_callback)(void *opaque, uint32_t used_len); 125 126 /** 127 * @brief callback descriptor 128 * 129 * contains callback function ad its argument, invoked after virtio device return 130 * descriptor chain its associated with 131 */ 132 struct virtq_receive_callback_entry { 133 /** 134 * callback function pointer 135 */ 136 virtq_receive_callback cb; 137 /** 138 * argument passed to the callback function 139 */ 140 void *opaque; 141 }; 142 143 /** 144 * @brief virtqueue 145 * 146 * contains structures required for virtqueue operation 147 */ 148 struct virtq { 149 /** 150 * lock used to synchronize operations on virtqueue 151 */ 152 struct k_spinlock lock; 153 154 /** 155 * size of virtqueue 156 */ 157 uint16_t num; 158 /** 159 * array with descriptors 160 */ 161 struct virtq_desc *desc; 162 /** 163 * available ring 164 */ 165 struct virtq_avail *avail; 166 /** 167 * used ring 168 */ 169 struct virtq_used *used; 170 171 /** 172 * last seen idx in used ring, used to determine first descriptor to process 173 * after receiving virtqueue interrupt 174 */ 175 uint16_t last_used_idx; 176 /** 177 * Stack containing indexes of free descriptors. Because virtio devices are 178 * not required to use received descriptors in order (see 2.7.9) unless 179 * VIRTIO_F_IN_ORDER was offered, we can't use array with descriptors as another 180 * ring buffer, always taking next descriptor. This is an auxilary structure to 181 * easily determine next free descriptor 182 */ 183 struct k_stack free_desc_stack; 184 185 /** 186 * amount of free descriptors in the free_desc_stack 187 */ 188 uint16_t free_desc_n; 189 190 /** 191 * array with callbacks invoked after receiving buffers back from the device 192 */ 193 struct virtq_receive_callback_entry *recv_cbs; 194 }; 195 196 197 /** 198 * @brief creates virtqueue 199 * 200 * @param v virtqueue to be created 201 * @param size size of the virtqueue 202 * @return 0 or error code on failure 203 */ 204 int virtq_create(struct virtq *v, size_t size); 205 206 /** 207 * @brief frees virtqueue 208 * 209 * @param v virtqueue to be freed 210 */ 211 void virtq_free(struct virtq *v); 212 213 /** 214 * @brief single buffer passed to virtq_add_buffer_chain 215 */ 216 struct virtq_buf { 217 /** 218 * virtual address of the buffer 219 */ 220 void *addr; 221 /** 222 * length of the buffer 223 */ 224 uint32_t len; 225 }; 226 227 /** 228 * @brief adds chain of buffers to the virtqueue 229 * 230 * Note that according to spec 2.7.13.3 the device may access the buffers as soon 231 * as the avail->idx is increased, which is done at the end of this function, so 232 * the device may access the buffers without notifying it with virtio_notify_virtqueue 233 * 234 * @param v virtqueue it operates on 235 * @param bufs array of buffers to be added to the virtqueue 236 * @param bufs_size amount of buffers 237 * @param device_readable_count amount of bufferes readable by the device, the first 238 * device_readable_count buffers will be set as device readable 239 * @param cb callback to be invoked after device returns the buffer chain, can be NULL 240 * @param cb_opaque opaque value that will be passed to the cb 241 * @param timeout amount of time it will wait for free descriptors, with K_NO_WAIT it 242 * can be called from isr 243 * @return 0 or error code on failure 244 */ 245 int virtq_add_buffer_chain( 246 struct virtq *v, struct virtq_buf *bufs, uint16_t bufs_size, 247 uint16_t device_readable_count, virtq_receive_callback cb, void *cb_opaque, 248 k_timeout_t timeout 249 ); 250 251 /** 252 * @brief adds free descriptor back 253 * 254 * @param v virtqueue it operates on 255 * @param desc_idx index of returned descriptor 256 */ 257 void virtq_add_free_desc(struct virtq *v, uint16_t desc_idx); 258 259 /** 260 * @brief gets next free descriptor 261 * 262 * @param v virtqueue it operates on 263 * @param desc_idx address where index of descriptor will be stored 264 * @param timeout amount of time it will wait for free descriptor, with K_NO_WAIT it 265 * can be called from isr 266 * @return 0 or error code on failure 267 */ 268 int virtq_get_free_desc(struct virtq *v, uint16_t *desc_idx, k_timeout_t timeout); 269 270 /** 271 * @} 272 */ 273 274 #ifdef __cplusplus 275 } 276 #endif 277 278 #endif /* ZEPHYR_VIRTIO_VIRTQUEUE_H_ */ 279