text: #ifndef _VIRTIO_RING_H
  #define _VIRTIO_RING_H
  
  #include "types.h" // u64
  #include "memmap.h" // PAGE_SIZE
  
  #define PAGE_SHIFT 12
  #define PAGE_MASK  (PAGE_SIZE-1)
  
  #define virt_to_phys(v) (unsigned long)(v)
  #define phys_to_virt(p) (void*)(p)
  /* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */
  #define smp_rmb() barrier()
  #define smp_wmb() barrier()
  
  /* Status byte for guest to report progress, and synchronize features. */
  /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
  #define VIRTIO_CONFIG_S_ACKNOWLEDGE     1
  /* We have found a driver for the device. */
  #define VIRTIO_CONFIG_S_DRIVER          2
  /* Driver has used its parts of the config, and is happy */
  #define VIRTIO_CONFIG_S_DRIVER_OK       4
  /* We've given up on this device. */
  #define VIRTIO_CONFIG_S_FAILED          0x80
  
  #define MAX_QUEUE_NUM      (128)
  
  #define VRING_DESC_F_NEXT  1
  #define VRING_DESC_F_WRITE 2
  
  #define VRING_AVAIL_F_NO_INTERRUPT 1
  
  #define VRING_USED_F_NO_NOTIFY     1
  
  struct vring_desc
  {
     u64 addr;
     u32 len;
     u16 flags;
     u16 next;
  };
  
  struct vring_avail
  {
     u16 flags;
     u16 idx;
     u16 ring[0];
  };
  
  struct vring_used_elem
  {
     u32 id;
     u32 len;
  };
  
  struct vring_used
  {
     u16 flags;
     u16 idx;
     struct vring_used_elem ring[];
  };
  
  struct vring {
     unsigned int num;
     struct vring_desc *desc;
     struct vring_avail *avail;
     struct vring_used *used;
  };
  
  #define vring_size(num) \
     (((((sizeof(struct vring_desc) * num) + \
        (sizeof(struct vring_avail) + sizeof(u16) * num)) \
           + PAGE_MASK) & ~PAGE_MASK) + \
           (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num))
  
  typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)];
  
  struct vring_virtqueue {
     virtio_queue_t queue;
     struct vring vring;
     u16 free_head;
     u16 last_used_idx;
     u16 vdata[MAX_QUEUE_NUM];
     /* PCI */
     int queue_index;
  };
  
  struct vring_list {
    char *addr;
    unsigned int length;
  };
  
  static inline void vring_init(struct vring *vr,
                           unsigned int num, unsigned char *queue)
  {
     unsigned int i;
     unsigned long pa;
  
     ASSERT32FLAT();
     vr->num = num;
  
     /* physical address of desc must be page aligned */
  
     pa = virt_to_phys(queue);
     pa = (pa + PAGE_MASK) & ~PAGE_MASK;
     vr->desc = phys_to_virt(pa);
  
     vr->avail = (struct vring_avail *)&vr->desc[num];
     /* disable interrupts */
     vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
  
     /* physical address of used must be page aligned */
  
     pa = virt_to_phys(&vr->avail->ring[num]);
     pa = (pa + PAGE_MASK) & ~PAGE_MASK;
     vr->used = phys_to_virt(pa);
  
     for (i = 0; i < num - 1; i++)
             vr->desc[i].next = i + 1;
     vr->desc[i].next = 0;
  }
  
  int vring_more_used(struct vring_virtqueue *vq);
  void vring_detach(struct vring_virtqueue *vq, unsigned int head);
  int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len);
  void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[],
                     unsigned int out, unsigned int in,
                     int index, int num_added);
  void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added);
  
  #endif /* _VIRTIO_RING_H_ */
  