1 #ifndef VIRTQUEUE_H_ 2 #define VIRTQUEUE_H_ 3 4 /*- 5 * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org> 6 * Copyright (c) 2016 Freescale Semiconductor, Inc. 7 * Copyright 2016-2019 NXP 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice unmodified, this list of conditions, and the following 15 * disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 #include <stdbool.h> 35 #include <stdint.h> 36 #include "rpmsg_default_config.h" 37 typedef uint8_t boolean; 38 39 #include "virtio_ring.h" 40 #include "llist.h" 41 42 /*Error Codes*/ 43 #define VQ_ERROR_BASE (-3000) 44 #define ERROR_VRING_FULL (VQ_ERROR_BASE - 1) 45 #define ERROR_INVLD_DESC_IDX (VQ_ERROR_BASE - 2) 46 #define ERROR_EMPTY_RING (VQ_ERROR_BASE - 3) 47 #define ERROR_NO_MEM (VQ_ERROR_BASE - 4) 48 #define ERROR_VRING_MAX_DESC (VQ_ERROR_BASE - 5) 49 #define ERROR_VRING_ALIGN (VQ_ERROR_BASE - 6) 50 #define ERROR_VRING_NO_BUFF (VQ_ERROR_BASE - 7) 51 #define ERROR_VQUEUE_INVLD_PARAM (VQ_ERROR_BASE - 8) 52 53 #define VQUEUE_SUCCESS (0) 54 #define VQUEUE_DEBUG (false) 55 56 /* This is temporary macro to replace C NULL support. 57 * At the moment all the RTL specific functions are present in env. 58 * */ 59 #define VQ_NULL ((void *)0) 60 61 /* The maximum virtqueue size is 2^15. Use that value as the end of 62 * descriptor chain terminator since it will never be a valid index 63 * in the descriptor table. This is used to verify we are correctly 64 * handling vq_free_cnt. 65 */ 66 #define VQ_RING_DESC_CHAIN_END (32768) 67 #define VIRTQUEUE_FLAG_INDIRECT (0x0001U) 68 #define VIRTQUEUE_FLAG_EVENT_IDX (0x0002U) 69 #define VIRTQUEUE_MAX_NAME_SZ (32) /* mind the alignment */ 70 71 /* Support for indirect buffer descriptors. */ 72 #define VIRTIO_RING_F_INDIRECT_DESC (1 << 28) 73 74 /* Support to suppress interrupt until specific index is reached. */ 75 #define VIRTIO_RING_F_EVENT_IDX (1 << 29) 76 77 /* 78 * Hint on how long the next interrupt should be postponed. This is 79 * only used when the EVENT_IDX feature is negotiated. 80 */ 81 typedef enum 82 { 83 VQ_POSTPONE_SHORT, 84 VQ_POSTPONE_LONG, 85 VQ_POSTPONE_EMPTIED /* Until all available desc are used. */ 86 } vq_postpone_t; 87 88 /* local virtqueue representation, not in shared memory */ 89 struct virtqueue 90 { 91 /* 32bit aligned { */ 92 char vq_name[VIRTQUEUE_MAX_NAME_SZ]; 93 uint32_t vq_flags; 94 int32_t vq_alignment; 95 int32_t vq_ring_size; 96 void *vq_ring_mem; 97 void (*callback_fc)(struct virtqueue *vq); 98 void (*notify_fc)(struct virtqueue *vq); 99 int32_t vq_max_indirect_size; 100 int32_t vq_indirect_mem_size; 101 struct vring vq_ring; 102 /* } 32bit aligned */ 103 104 /* 16bit aligned { */ 105 uint16_t vq_queue_index; 106 uint16_t vq_nentries; 107 uint16_t vq_free_cnt; 108 uint16_t vq_queued_cnt; 109 110 /* 111 * Head of the free chain in the descriptor table. If 112 * there are no free descriptors, this will be set to 113 * VQ_RING_DESC_CHAIN_END. 114 */ 115 uint16_t vq_desc_head_idx; 116 117 /* 118 * Last consumed descriptor in the used table, 119 * trails vq_ring.used->idx. 120 */ 121 uint16_t vq_used_cons_idx; 122 123 /* 124 * Last consumed descriptor in the available table - 125 * used by the consumer side. 126 */ 127 uint16_t vq_available_idx; 128 /* } 16bit aligned */ 129 130 boolean avail_read; /* 8bit wide */ 131 boolean avail_write; /* 8bit wide */ 132 boolean used_read; /* 8bit wide */ 133 boolean used_write; /* 8bit wide */ 134 135 uint16_t padd; /* aligned to 32bits after this: */ 136 137 void *priv; /* private pointer, upper layer instance pointer */ 138 #if defined(RL_USE_ENVIRONMENT_CONTEXT) && (RL_USE_ENVIRONMENT_CONTEXT == 1) 139 void *env; /* private pointer to environment layer internal context */ 140 #endif 141 }; 142 143 /* struct to hold vring specific information */ 144 struct vring_alloc_info 145 { 146 void *phy_addr; 147 uint32_t align; 148 uint16_t num_descs; 149 uint16_t pad; 150 }; 151 152 #if defined(RL_USE_STATIC_API) && (RL_USE_STATIC_API == 1) 153 struct vq_static_context 154 { 155 struct virtqueue vq; 156 }; 157 #endif 158 159 typedef void vq_callback(struct virtqueue *vq); 160 typedef void vq_notify(struct virtqueue *vq); 161 162 #if (VQUEUE_DEBUG == true) 163 #define VQASSERT_BOOL(_vq, _exp, _msg) \ 164 do \ 165 { \ 166 if (!(_exp)) \ 167 { \ 168 env_print("%s: %s - "(_msg), __func__, (_vq)->vq_name); \ 169 while (1) \ 170 { \ 171 }; \ 172 } \ 173 } while (0) 174 #define VQASSERT(_vq, _exp, _msg) VQASSERT_BOOL(_vq, (_exp) != 0, _msg) 175 176 #define VQ_RING_ASSERT_VALID_IDX(_vq, _idx) VQASSERT((_vq), (_idx) < (_vq)->vq_nentries, "invalid ring index") 177 178 #define VQ_PARAM_CHK(condition, status_var, status_err) \ 179 if ((status_var == 0) && (condition)) \ 180 { \ 181 status_var = status_err; \ 182 } 183 184 #define VQUEUE_BUSY(vq, dir) \ 185 if ((vq)->dir == false) \ 186 { \ 187 (vq)->dir = true; \ 188 } \ 189 else \ 190 { \ 191 VQASSERT(vq, (vq)->dir == false, "VirtQueue already in use") \ 192 } 193 194 #define VQUEUE_IDLE(vq, dir) ((vq)->dir = false) 195 196 #else 197 198 #define KASSERT(cond, str) 199 #define VQASSERT(_vq, _exp, _msg) 200 #define VQ_RING_ASSERT_VALID_IDX(_vq, _idx) 201 #define VQ_PARAM_CHK(condition, status_var, status_err) 202 #define VQUEUE_BUSY(vq, dir) 203 #define VQUEUE_IDLE(vq, dir) 204 205 #endif 206 207 #if defined(RL_USE_STATIC_API) && (RL_USE_STATIC_API == 1) 208 int32_t virtqueue_create_static(uint16_t id, 209 const char *name, 210 struct vring_alloc_info *ring, 211 void (*callback_fc)(struct virtqueue *vq), 212 void (*notify_fc)(struct virtqueue *vq), 213 struct virtqueue **v_queue, 214 struct vq_static_context *vq_ctxt); 215 #else 216 int32_t virtqueue_create(uint16_t id, 217 const char *name, 218 struct vring_alloc_info *ring, 219 void (*callback_fc)(struct virtqueue *vq), 220 void (*notify_fc)(struct virtqueue *vq), 221 struct virtqueue **v_queue); 222 #endif 223 224 int32_t virtqueue_add_buffer(struct virtqueue *vq, uint16_t head_idx); 225 226 int32_t virtqueue_fill_used_buffers(struct virtqueue *vq, void *buffer, uint32_t len); 227 228 int32_t virtqueue_fill_avail_buffers(struct virtqueue *vq, void *buffer, uint32_t len); 229 230 void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx); 231 232 void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx, uint32_t *len); 233 234 int32_t virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx, uint32_t len); 235 236 void virtqueue_disable_cb(struct virtqueue *vq); 237 238 int32_t virtqueue_enable_cb(struct virtqueue *vq); 239 240 void virtqueue_kick(struct virtqueue *vq); 241 242 #if defined(RL_USE_STATIC_API) && (RL_USE_STATIC_API == 1) 243 void virtqueue_free_static(struct virtqueue *vq); 244 #else 245 void virtqueue_free(struct virtqueue *vq); 246 #endif 247 248 void virtqueue_dump(struct virtqueue *vq); 249 250 void virtqueue_notification(struct virtqueue *vq); 251 252 uint32_t virtqueue_get_desc_size(struct virtqueue *vq); 253 254 uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx); 255 256 void vq_ring_init(struct virtqueue *vq); 257 258 #endif /* VIRTQUEUE_H_ */ 259