The qxl-virtio driver implements a QXL driver using VirtIO as transport, thus
enabling the use of QXL/Spice in non-PCI architectures. In the actual QXL
driver, all communication between Host and Guest is done through PCI shared
memory.

Signed-off-by: Erlon R. Cruz <erlon.c...@br.flextronics.com>
Signed-off-by: Fabiano FidĂȘncio <fabiano.fiden...@fit-tecnologia.org.br>
Signed-off-by: Rafael F. Santos <fonsecasantos.raf...@gmail.com>
---
 .gitignore                        |    1 +
 drivers/video/Kconfig             |    6 +
 drivers/video/Makefile            |    3 +
 drivers/video/virtio-qxl-bridge.c |  628 +++++++++++++++++++++++++++++++++++++
 include/linux/Kbuild              |    1 +
 include/linux/virtio_bridge.h     |  159 ++++++++++
 6 files changed, 798 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/virtio-qxl-bridge.c
 create mode 100644 include/linux/virtio_bridge.h

diff --git a/.gitignore b/.gitignore
index 57af07c..ebe7e3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -84,3 +84,4 @@ GTAGS
 *.orig
 *~
 \#*#
+/nbproject/private/
\ No newline at end of file
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f74..466863b 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2469,4 +2469,10 @@ config FB_SH_MOBILE_MERAM
          Up to 4 memory channels can be configured, allowing 4 RGB or
          2 YCbCr framebuffers to be configured.
 
+config VIRTIO_QXL
+       tristate "QXL driver over VirtIO"
+       depends on EXPERIMENTAL && VIRTIO
+       help
+         This driver provides a QXL video device using virtio transport.
+
 endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ee8dafb..a331743 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -170,3 +170,6 @@ obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
 
 #video output switch sysfs driver
 obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+
+# Virtio QLX
+obj-$(CONFIG_VIRTIO_QXL) += virtio-qxl-bridge.o
diff --git a/drivers/video/virtio-qxl-bridge.c 
b/drivers/video/virtio-qxl-bridge.c
new file mode 100644
index 0000000..c799943
--- /dev/null
+++ b/drivers/video/virtio-qxl-bridge.c
@@ -0,0 +1,628 @@
+/*
+ * Virtio QXL Device
+ *
+ *
+ * Authors:
+ *  Erlon R. Cruz <erlon.c...@br.flextronics.com>
+ *  Rafael F. Santos <rafael.san...@fit-tecnologia.org.br>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_bridge.h>
+#include <linux/mm.h>
+#include <linux/semaphore.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <asm/current.h>
+#include <asm/page.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+
+#define DRIVER_STRING "virtio-qxl-bridge"
+#define SG_ELEMENTS 128
+
+#define DEBUG_ERROR 1
+#define DEBUG_INFO 1
+#define DEBUG_VM 0
+#define DEBUG_SG 0
+#define DEBUG_PUSH_AREA 0
+#define DEBUG_PULL_AREA 0
+#define DEBUG_IOCTL 0
+#define DEBUG_IOPWRITE 0
+#define DEBUG_FOPS 0
+
+ #define dprintk(_level, _fmt, ...)                    \
+do {                                                                   \
+       if (_level) {                                           \
+               printk(_fmt, ## __VA_ARGS__);   \
+       }                                                               \
+} while (0)
+
+static inline void printHexa(void *buf, int len)
+{
+       uint8_t *cur, *ubuf;
+       ubuf = (uint8_t *) buf;
+
+       for (cur = ubuf; (cur - ubuf) < len; cur++)
+#ifdef __KERNEL__
+               printk("%02X", *cur);
+#else
+               ErrorF("%02X", *cur);
+#endif
+
+}
+
+dev_t dev;
+int devindex, devno;
+struct semaphore idxsem;
+struct class *virtio_qxl_class;
+
+struct qxl_usrmem_desc {
+       char __user *start;
+       unsigned int len;
+};
+
+struct virtio_qxl_bridge {
+       spinlock_t lock;
+       struct virtio_device *vdev;
+       struct virtqueue *vq;
+       struct scatterlist sg[SG_ELEMENTS];
+       struct scatterlist *vgasg;
+       struct cdev cdev;
+       struct virtioqxl_config config;
+       struct semaphore sem;
+       struct qxl_usrmem_desc mem_desc;
+       struct page **user_pages;
+};
+
+struct vbr_req {
+       struct list_head list;
+       struct vbr_proto_hdr hdr;
+       u8 status;
+};
+
+void put_userpages_vector(struct page **pages, int num_pages, bool dirty)
+{
+       int i;
+
+       for (i = 0; i < num_pages; i++) {
+               if (dirty)
+                       set_page_dirty_lock(pages[i]);
+               put_page(pages[i]);
+       }
+}
+
+static int userpages_fill_sg(struct page **upages, struct scatterlist *sg,
+                            u8 __user *virt, int length, int write)
+{
+       struct page *pg;
+       int sg_entries = 0, pagesidx = 0;
+       int tmp_off, rc;
+       uint8_t *ustart, *uend;
+
+       ustart = virt;
+       uend = ustart + length;
+       tmp_off = ((ulong) ustart & ~PAGE_MASK);
+       /* unaligned */
+       if (tmp_off) {
+               int gap = 0, cplen;
+
+               /* Length from offset to the end of the page */
+               gap = PAGE_SIZE - tmp_off;
+               if (gap > length)
+                       cplen = length;
+               else
+                       cplen = gap;
+
+               down_read(&current->mm->mmap_sem);
+               rc = get_user_pages(current,
+                                   current->mm,
+                                   (unsigned long)ustart & PAGE_MASK,
+                                   1, write, 0, upages + pagesidx++, NULL);
+               up_read(&current->mm->mmap_sem);
+
+               pg = upages[pagesidx - 1];
+               sg_set_page(&sg[sg_entries++], pg, cplen, tmp_off);
+               ustart += cplen;
+               dprintk(DEBUG_SG,
+                       "%s: Unaligned buffer: tt len %d, offset %d, gap %d,"
+                       " unaligned head %d", __func__, length, tmp_off, gap,
+                       cplen);
+       } else {
+               dprintk(DEBUG_SG, "%s: Aligned buffer tt len %d", __func__,
+                       length);
+       }
+
+       /* Now start is aligned rigth? hooope so */
+       while (uend - ustart >= PAGE_SIZE) {
+
+               down_read(&current->mm->mmap_sem);
+               rc = get_user_pages(current,
+                                   current->mm,
+                                   (unsigned long)ustart,
+                                   1, write, 0, upages + pagesidx++, NULL);
+               up_read(&current->mm->mmap_sem);
+
+               pg = upages[pagesidx - 1];
+               sg_set_page(&sg[sg_entries++], pg, PAGE_SIZE, 0);
+               ustart += PAGE_SIZE;
+       }
+
+       if (uend - ustart > 0) {
+
+               down_read(&current->mm->mmap_sem);
+               rc = get_user_pages(current,
+                                   current->mm,
+                                   (unsigned long)ustart,
+                                   1, write, 0, upages + pagesidx++, NULL);
+               up_read(&current->mm->mmap_sem);
+
+               if (rc < 0)
+                       goto fail;
+
+               pg = upages[pagesidx - 1];
+               sg_set_page(&sg[sg_entries++], pg, uend - ustart, 0);
+               dprintk(DEBUG_SG, " unaligned tail %d", (int)(uend - ustart));
+               ustart += uend - ustart;
+       }
+
+       if (ustart != uend)
+               panic("Anomalous behavior when filling SG\n");
+
+       dprintk(DEBUG_SG, " %d SG entries\n", sg_entries);
+       return sg_entries;
+
+fail:
+       put_userpages_vector(upages, pagesidx - 1, false);
+       return 0;
+}
+
+static int send_packet(struct virtio_qxl_bridge *devdata,
+                      int what, char __user *buffer, int len, int flags)
+{
+       int in, out, mapped_entries = 0;
+       unsigned int readlen;
+       struct vbr_req *rq;
+       struct scatterlist *sg;
+
+       in = out = 0;
+       rq = kzalloc(sizeof(*rq), GFP_KERNEL);
+
+       rq->hdr.flags |= flags;
+       rq->hdr.function = what;
+       /* The offset of the buffer based on the start of video memory */
+       rq->hdr.param = buffer - devdata->mem_desc.start;
+       rq->hdr.len = len;
+
+       switch (what) {
+       case VIRTIOQXL_GETCFG:
+               sg = devdata->sg;
+               sg_set_buf(&sg[out++], &rq->hdr, sizeof(rq->hdr));
+               sg_set_buf(&sg[out + in++], buffer, len);
+               break;
+
+       case VIRTIOQXL_IOPORT_WRITE:
+               sg = devdata->sg;
+               sg_set_buf(&sg[out++], &rq->hdr, sizeof(rq->hdr));
+               sg_set_buf(&sg[out++], buffer, len);
+               break;
+
+       case VIRTIOQXL_GET_RAM:
+               sg = devdata->vgasg;
+               sg_set_buf(&sg[out++], &rq->hdr, sizeof(rq->hdr));
+               in += mapped_entries =
+                   userpages_fill_sg(devdata->user_pages, &sg[out], buffer,
+                                     len, 1);
+
+               break;
+
+       case VIRTIOQXL_SET_RAM:
+               sg = devdata->vgasg;
+               sg_set_buf(&sg[out++], &rq->hdr, sizeof(rq->hdr));
+               out += mapped_entries =
+                   userpages_fill_sg(devdata->user_pages, &sg[out], buffer,
+                                     len, 0);
+               break;
+       default:
+               panic("%s: virtio QXL request (%d) not supported\n",
+                     DRIVER_STRING, what);
+               break;
+       }
+
+       sg_set_buf(&sg[out + in++], &rq->status, sizeof(rq->status));
+
+       if (virtqueue_add_buf(devdata->vq, sg, out, in, rq, GFP_KERNEL) < 0) {
+               dprintk(DEBUG_ERROR, "%s: error adding buffer\n",
+                       DRIVER_STRING);
+               return -1;
+       }
+
+       virtqueue_kick(devdata->vq);
+       while (!virtqueue_get_buf(devdata->vq, &readlen))
+               cpu_relax();
+
+       if (mapped_entries)
+               put_userpages_vector(devdata->user_pages, mapped_entries,
+                                    (in > 1));
+
+       kfree(rq);
+       return 0;
+}
+
+static int get_from_host(struct virtio_qxl_bridge *devdata, uint what,
+                        void *bufto, int len)
+{
+       return send_packet(devdata, what, bufto, len, CONFIG_READ);
+}
+
+static int set_on_host(struct virtio_qxl_bridge *devdata, uint what,
+                      void *buffrom, int len)
+{
+       return send_packet(devdata, what, buffrom, len, CONFIG_WRITE);
+}
+
+static int device_open(struct inode *inode, struct file *file)
+{
+       struct virtio_qxl_bridge *virtiodata;
+
+       dprintk(DEBUG_INFO, "%s: entering %s\n", DRIVER_STRING, __func__);
+
+       virtiodata =
+           container_of(inode->i_cdev, struct virtio_qxl_bridge, cdev);
+       file->private_data = virtiodata;
+
+       return 0;
+}
+
+static long device_ioctl(struct file *file, unsigned int cmd, unsigned long 
arg)
+{
+       struct virtio_qxl_bridge *brdev = file->private_data;
+
+       switch (cmd) {
+
+       case QXL_IOCTL_QXL_IO_GETCFG:{
+                       struct virtioqxl_config *cfg;
+                       cfg = &brdev->config;
+                       copy_to_user((void __user *)arg, cfg,
+                                    sizeof(struct virtioqxl_config));
+                       break;
+               }
+
+       case QXL_IOCTL_QXL_IO_SET_RAMSTART:{
+
+                       copy_from_user(&brdev->mem_desc.start,
+                                      (void __user *)arg,
+                                      sizeof(brdev->mem_desc.start));
+                       brdev->mem_desc.len =
+                           brdev->config.vramsize + brdev->config.ramsize +
+                           brdev->config.romsize;
+
+                       dprintk(DEBUG_INFO, "%s: user ram start %p\n",
+                               DRIVER_STRING, brdev->mem_desc.start);
+                       break;
+               }
+
+       case QXL_IOCTL_NOTIFY_CMD:
+       case QXL_IOCTL_NOTIFY_CURSOR:
+       case QXL_IOCTL_UPDATE_AREA:
+       case QXL_IOCTL_UPDATE_IRQ:
+       case QXL_IOCTL_NOTIFY_OOM:
+       case QXL_IOCTL_RESET:
+       case QXL_IOCTL_SET_MODE:
+       case QXL_IOCTL_LOG:
+       case QXL_IOCTL_MEMSLOT_ADD:
+       case QXL_IOCTL_MEMSLOT_DEL:
+       case QXL_IOCTL_DETACH_PRIMARY:
+       case QXL_IOCTL_ATTACH_PRIMARY:
+       case QXL_IOCTL_CREATE_PRIMARY:
+       case QXL_IOCTL_DESTROY_PRIMARY:
+       case QXL_IOCTL_DESTROY_SURFACE_WAIT:
+       case QXL_IOCTL_DESTROY_ALL_SURFACES:
+       case QXL_IOCTL_UPDATE_AREA_ASYNC:
+       case QXL_IOCTL_MEMSLOT_ADD_ASYNC:
+       case QXL_IOCTL_CREATE_PRIMARY_ASYNC:
+       case QXL_IOCTL_DESTROY_PRIMARY_ASYNC:
+       case QXL_IOCTL_DESTROY_SURFACE_ASYNC:
+       case QXL_IOCTL_DESTROY_ALL_SURFACES_ASYNC:
+       case QXL_IOCTL_FLUSH_SURFACES_ASYNC:
+       case QXL_IOCTL_FLUSH_RELEASE:{
+                       struct iowrite_cmd *iocmd =
+                           kmalloc(sizeof(*iocmd), GFP_KERNEL);
+                       iocmd->port = _IOC_NR(cmd);
+                       iocmd->arg = arg;
+                       dprintk(DEBUG_IOPWRITE, " port %d, arg %d\n",
+                               iocmd->port, iocmd->arg);
+                       set_on_host(brdev, VIRTIOQXL_IOPORT_WRITE, iocmd,
+                                   sizeof(*iocmd));
+                       kfree(iocmd);
+                       break;
+               }
+
+       default:
+               dprintk(DEBUG_INFO, "%s: IOCTL not handled %ui\n", __func__,
+                       cmd);
+               return -ENOTTY;
+       }
+
+       return 0;
+}
+
+static ssize_t device_read(struct file *file, char __user *buf, size_t len,
+                          loff_t *f_pos)
+{
+       struct virtio_qxl_bridge *virtiodata = file->private_data;
+
+       if (!virtiodata->mem_desc.start)
+               return -EFAULT;
+
+       if (buf < virtiodata->mem_desc.start ||
+           (buf + len) >
+           (virtiodata->mem_desc.start + virtiodata->mem_desc.len))
+               return -EFAULT;
+
+       if (len <= 0)
+               return 0;
+
+       dprintk(DEBUG_FOPS,
+               "%s: virtio_qxl_bridge: %s: reading %d bytes, "
+               "useraddr = %p, videomem offset = %lu\n",
+               DRIVER_STRING, __func__, (int)len, buf,
+               buf - virtiodata->mem_desc.start);
+
+       return get_from_host(virtiodata, VIRTIOQXL_GET_RAM, buf, len);
+}
+
+static ssize_t device_write(struct file *file, const char __user *buf,
+                           size_t len, loff_t *f_pos)
+{
+       struct virtio_qxl_bridge *virtiodata = file->private_data;
+
+       if (!virtiodata->mem_desc.start)
+               return -EFAULT;
+
+       if (buf < virtiodata->mem_desc.start ||
+           (buf + len) >
+           (virtiodata->mem_desc.start + virtiodata->mem_desc.len))
+               return -EFAULT;
+
+       if (len <= 0)
+               return 0;
+
+       dprintk(DEBUG_FOPS,
+               "%s: virtio_qxl_bridge: %s: writing %d bytes, "
+               "useraddr = %p, videomem offset = %lu\n",
+               DRIVER_STRING, __func__, (int)len, buf,
+               buf - virtiodata->mem_desc.start);
+
+       return set_on_host(virtiodata, VIRTIOQXL_SET_RAM, (char *)buf, len);
+}
+
+int device_release(struct inode *inode, struct file *file)
+{
+       dprintk(DEBUG_FOPS, "%s: %s\n", DRIVER_STRING, __func__);
+       return 0;
+}
+
+const struct file_operations device_fops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = device_ioctl,
+       .read = device_read,
+       .write = device_write,
+       .open = device_open,
+       .release = device_release
+};
+
+static int setup_cdev(struct virtio_qxl_bridge *virtiodata)
+{
+       int minor, err = 0;
+       struct device *device;
+
+       down(&idxsem);
+       minor = devindex;
+       devindex++;
+       up(&idxsem);
+
+       devno = MKDEV(MAJOR(dev), minor);
+
+       dprintk(DEBUG_INFO, "%s: %s: adding char device %d/%d\n",
+               DRIVER_STRING, __func__, MAJOR(dev), minor);
+
+       /* Print driver port string */
+       cdev_init(&virtiodata->cdev, &device_fops);
+       virtiodata->cdev.owner = THIS_MODULE;
+       virtiodata->cdev.ops = &device_fops;
+       err = cdev_add(&virtiodata->cdev, devno, 1);
+       if (err) {
+               dprintk(DEBUG_INFO, "%s: error %d adding char device %d",
+                       DRIVER_STRING, err, devindex);
+               return err;
+       }
+
+       /* Create a sysfs class entry */
+       device =
+           device_create(virtio_qxl_class, NULL, devno, NULL, "virtioqxl%u",
+                         minor);
+       if (IS_ERR(device)) {
+               dprintk(DEBUG_INFO, "%s: error %li creating device %d\n",
+                       DRIVER_STRING, PTR_ERR(device), devindex);
+               err = PTR_ERR(device);
+               cdev_del(&virtiodata->cdev);
+       }
+       return err;
+}
+
+static void release_cdev(struct virtio_qxl_bridge *virtiodata)
+{
+       device_destroy(virtio_qxl_class, devno);
+       cdev_del(&virtiodata->cdev);
+}
+
+static int __devinit qxl_bridge_probe(struct virtio_device *vdev)
+{
+       int memlen, sg_elements, err = 0;
+       struct virtio_qxl_bridge *brdev;
+       struct virtioqxl_config *cfg;
+
+       dprintk(DEBUG_INFO, "%s: probing\n", DRIVER_STRING);
+
+       brdev = kmalloc(sizeof(struct virtio_qxl_bridge), GFP_KERNEL);
+       if (!brdev)
+               return -ENOMEM;
+
+       dprintk(DEBUG_INFO, "%s: allocated %lu bytes as virtio_data\n",
+               DRIVER_STRING, sizeof(struct virtio_qxl_bridge));
+
+       brdev->vq = virtio_find_single_vq(vdev, NULL, "requests");
+       if (IS_ERR(brdev->vq)) {
+               err = PTR_ERR(brdev->vq);
+               dprintk(DEBUG_ERROR, "%s: error finding vq\n", DRIVER_STRING);
+               goto out_find;
+       }
+
+       cfg = &brdev->config;
+       brdev->vdev = vdev;
+       brdev->vdev->priv = brdev;
+
+       spin_lock_init(&brdev->lock);
+       sg_init_table(brdev->sg, SG_ELEMENTS);
+       sema_init(&brdev->sem, 1);
+
+       err = setup_cdev(brdev);
+       if (err < 0)
+               goto out_cdev;
+
+       get_from_host(brdev, VIRTIOQXL_GETCFG, cfg, sizeof(*cfg));
+       memlen = cfg->ramsize + cfg->vramsize + cfg->romsize;
+       dprintk(DEBUG_INFO,
+               "%s: got config information from host: ram size %d, "
+               "vram size %d, rom size %d\n", DRIVER_STRING, cfg->ramsize,
+               cfg->vramsize, cfg->romsize);
+
+       /* Memmory + alignment + head/tail */
+       sg_elements = cfg->vramsize / PAGE_SIZE + 1 + 2;
+       brdev->vgasg =
+           kmalloc(sizeof(struct scatterlist) * sg_elements, GFP_KERNEL);
+       if (!brdev->vgasg) {
+               dprintk(DEBUG_ERROR, "%s: error allocating SG memory\n",
+                       DRIVER_STRING);
+               goto out_driver_mem;
+       }
+       dprintk(DEBUG_INFO, "%s: allocated table for %d SG elements\n",
+               DRIVER_STRING, sg_elements);
+
+       sg_init_table(brdev->vgasg, sg_elements);
+
+       brdev->mem_desc.start = NULL;
+       brdev->user_pages =
+           kmalloc((cfg->vramsize / PAGE_SIZE) * sizeof(struct page),
+                   GFP_KERNEL);
+       if (!brdev->user_pages) {
+               dprintk(DEBUG_ERROR, "%s: error allocating page table memory\n",
+                       DRIVER_STRING);
+               goto out_driver_mem2;
+       }
+
+       return 0;
+
+out_driver_mem2:
+       kfree(brdev->vgasg);
+out_driver_mem:
+       release_cdev(brdev);
+out_cdev:
+       vdev->config->reset(vdev);
+       vdev->config->del_vqs(vdev);
+out_find:
+       kfree(brdev);
+       return err;
+}
+
+static void __devexit qxl_bridge_remove(struct virtio_device *vdev)
+{
+       struct virtio_qxl_bridge *brdata = vdev->priv;
+
+       kfree(brdata->user_pages);
+       kfree(brdata->vgasg);
+       release_cdev(brdata);
+       vdev->config->reset(vdev);
+       vdev->config->del_vqs(vdev);
+       dprintk(DEBUG_INFO, "%s: removing\n", DRIVER_STRING);
+       kfree(brdata);
+}
+
+static const struct virtio_device_id id_table[] = {
+       {6, VIRTIO_DEV_ANY_ID},
+       {0},
+};
+
+static struct virtio_driver __refdata virtio_qxl = {
+       .driver.name = KBUILD_MODNAME,
+       .driver.owner = THIS_MODULE,
+       .id_table = id_table,
+       .probe = qxl_bridge_probe,
+       .remove = __devexit_p(qxl_bridge_remove),
+};
+
+static int __init init(void)
+{
+       int ret = 0;
+       dev = 0;
+       devindex = 0;
+       devno = 0;
+
+       dprintk(DEBUG_INFO, "%s: init\n", DRIVER_STRING);
+
+       ret = alloc_chrdev_region(&dev, 0, 1, "virtio-qxl-bridge");
+       if (ret < 0) {
+               dprintk(DEBUG_INFO, "%s: can't get major %d\n", DRIVER_STRING,
+                       MAJOR(dev));
+               goto out;
+       }
+
+       sema_init(&idxsem, 1);
+
+       virtio_qxl_class = class_create(THIS_MODULE, "virtioqxl");
+       if (IS_ERR(virtio_qxl_class)) {
+               dprintk(DEBUG_INFO, "%s: error creating driver class.\n",
+                       DRIVER_STRING);
+               ret = PTR_ERR(virtio_qxl_class);
+               goto class_out;
+       }
+
+       ret = register_virtio_driver(&virtio_qxl);
+       if (!ret)
+               return ret;
+
+       class_destroy(virtio_qxl_class);
+class_out:
+       unregister_chrdev_region(dev, 1);
+out:
+       return ret;
+}
+
+static void __exit finish(void)
+{
+       unregister_virtio_driver(&virtio_qxl);
+       class_destroy(virtio_qxl_class);
+       unregister_chrdev_region(dev, 1);
+       dprintk(DEBUG_INFO, "%s: ...exit.\n", DRIVER_STRING);
+}
+
+module_init(init);
+module_exit(finish);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("VirtIO QXL bridge");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index fa21760..8b814f6 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -397,6 +397,7 @@ header-y += videodev2.h
 header-y += virtio_9p.h
 header-y += virtio_balloon.h
 header-y += virtio_blk.h
+header-y += virtio_bridge.h
 header-y += virtio_config.h
 header-y += virtio_console.h
 header-y += virtio_ids.h
diff --git a/include/linux/virtio_bridge.h b/include/linux/virtio_bridge.h
new file mode 100644
index 0000000..a5f6472
--- /dev/null
+++ b/include/linux/virtio_bridge.h
@@ -0,0 +1,159 @@
+/*
+ * Virtio QXL
+ *
+ *
+ * Authors:
+ *  Erlon R. Cruz <erlon.c...@br.flextronics.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef VIRTIO_BRIDGE_H
+#define VIRTIO_BRIDGE_H
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+/* Taken from spice-protocol */
+enum {
+       QXL_IO_NOTIFY_CMD,
+       QXL_IO_NOTIFY_CURSOR,
+       QXL_IO_UPDATE_AREA,
+       QXL_IO_UPDATE_IRQ,
+       QXL_IO_NOTIFY_OOM,
+       QXL_IO_RESET,
+       QXL_IO_SET_MODE,        /* qxl-1 */
+       QXL_IO_LOG,
+       /* appended for qxl-2 */
+       QXL_IO_MEMSLOT_ADD,
+       QXL_IO_MEMSLOT_DEL,
+       QXL_IO_DETACH_PRIMARY,
+       QXL_IO_ATTACH_PRIMARY,
+       QXL_IO_CREATE_PRIMARY,
+       QXL_IO_DESTROY_PRIMARY,
+       QXL_IO_DESTROY_SURFACE_WAIT,
+       QXL_IO_DESTROY_ALL_SURFACES,
+       /* appended for qxl-3 */
+       QXL_IO_UPDATE_AREA_ASYNC,
+       QXL_IO_MEMSLOT_ADD_ASYNC,
+       QXL_IO_CREATE_PRIMARY_ASYNC,
+       QXL_IO_DESTROY_PRIMARY_ASYNC,
+       QXL_IO_DESTROY_SURFACE_ASYNC,
+       QXL_IO_DESTROY_ALL_SURFACES_ASYNC,
+       QXL_IO_FLUSH_SURFACES_ASYNC,
+       QXL_IO_FLUSH_RELEASE,
+       QXL_IO_RANGE_SIZE
+};
+
+/* Transport operations between guest kernel and host */
+enum {
+       VIRTIOQXL_GETCFG,
+       VIRTIOQXL_IOPORT_WRITE,
+       VIRTIOQXL_GET_RAM,
+       VIRTIOQXL_SET_RAM
+};
+
+enum {
+       VIRTIOQXL_STATUS_DONE,
+       VIRTIOQXL_STATUS_ERROR,
+       VIRTIOQXL_STATUS_RANGE
+};
+
+/* TODO: Merge this num with guest_host_cmd? */
+/* Read configs from the host device */
+#define CONFIG_READ 0x00000001
+/* Write configs on the host device */
+#define CONFIG_WRITE 0x00000002
+
+#endif
+
+/* Commands betweend xf86 driver and kernel virtio driver */
+enum {
+       QXL_IO_PUSH_AREA = 100,
+       QXL_IO_PULL_AREA,
+       QXL_IO_GETCFG,
+       QXL_IO_SET_RAMSTART
+};
+
+struct qxl_ram_area {
+       __u32 offset;
+       __u32 len;
+};
+
+struct vbr_proto_hdr {
+       __u32 function;
+       __u32 flags;
+       __u32 param;            /* Parameter related var */
+       __u32 len;
+};
+
+struct iowrite_cmd {
+       __u32 port;
+       __u32 arg;
+};
+
+struct virtioqxl_config {
+       __u32 configsize;
+       __u32 ramsize;
+       __u32 vramsize;
+       __u32 romsize;
+       __u32 virtiomem[0];
+};
+
+#define QXLMAGIC 'v'
+
+#define QXL_IOCTL_NOTIFY_CMD _IOW(QXLMAGIC,\
+                                       QXL_IO_NOTIFY_CMD, __u32)
+#define QXL_IOCTL_NOTIFY_CURSOR _IOW(QXLMAGIC, \
+                                       QXL_IO_NOTIFY_CURSOR, __u32)
+#define QXL_IOCTL_UPDATE_AREA _IOW(QXLMAGIC, \
+                                       QXL_IO_UPDATE_AREA, __u32)
+#define QXL_IOCTL_UPDATE_IRQ _IOW(QXLMAGIC, \
+                                       QXL_IO_UPDATE_IRQ, __u32)
+#define QXL_IOCTL_NOTIFY_OOM _IOW(QXLMAGIC, \
+                                       QXL_IO_NOTIFY_OOM, __u32)
+#define QXL_IOCTL_RESET _IOW(QXLMAGIC, \
+                                       QXL_IO_RESET, __u32)
+#define QXL_IOCTL_SET_MODE _IOW(QXLMAGIC, \
+                                       QXL_IO_SET_MODE, __u32)
+#define QXL_IOCTL_LOG _IOW(QXLMAGIC, \
+                                       QXL_IO_LOG, __u32)
+#define QXL_IOCTL_MEMSLOT_ADD _IOW(QXLMAGIC, \
+                                       QXL_IO_MEMSLOT_ADD, __u32)
+#define QXL_IOCTL_MEMSLOT_DEL _IOW(QXLMAGIC, \
+                                       QXL_IO_MEMSLOT_DEL, __u32)
+#define QXL_IOCTL_DETACH_PRIMARY _IOW(QXLMAGIC, \
+                                       QXL_IO_DETACH_PRIMARY, __u32)
+#define QXL_IOCTL_ATTACH_PRIMARY _IOW(QXLMAGIC, \
+                                        QXL_IO_ATTACH_PRIMARY, __u32)
+#define QXL_IOCTL_CREATE_PRIMARY _IOW(QXLMAGIC, \
+                                       QXL_IO_CREATE_PRIMARY, __u32)
+#define QXL_IOCTL_DESTROY_PRIMARY _IOW(QXLMAGIC, \
+                                       QXL_IO_DESTROY_PRIMARY, __u32)
+#define QXL_IOCTL_DESTROY_SURFACE_WAIT _IOW(QXLMAGIC, \
+                                       QXL_IO_DESTROY_SURFACE_WAIT, __u32)
+#define QXL_IOCTL_DESTROY_ALL_SURFACES _IOW(QXLMAGIC, \
+                                       QXL_IO_DESTROY_ALL_SURFACES, __u32)
+#define QXL_IOCTL_UPDATE_AREA_ASYNC _IOW(QXLMAGIC, \
+                                       QXL_IO_UPDATE_AREA_ASYNC, __u32)
+#define QXL_IOCTL_MEMSLOT_ADD_ASYNC _IOW(QXLMAGIC, \
+                                       QXL_IO_MEMSLOT_ADD_ASYNC, __u32)
+#define QXL_IOCTL_CREATE_PRIMARY_ASYNC _IOW(QXLMAGIC, \
+                                       QXL_IO_CREATE_PRIMARY_ASYNC, __u32)
+#define QXL_IOCTL_DESTROY_PRIMARY_ASYNC _IOW(QXLMAGIC, \
+                                       QXL_IO_DESTROY_PRIMARY_ASYNC, __u32)
+#define QXL_IOCTL_DESTROY_SURFACE_ASYNC _IOW(QXLMAGIC, \
+                                       QXL_IO_DESTROY_SURFACE_ASYNC, __u32)
+#define QXL_IOCTL_DESTROY_ALL_SURFACES_ASYNC _IOW(QXLMAGIC, \
+                               QXL_IO_DESTROY_ALL_SURFACES_ASYNC, __u32)
+#define QXL_IOCTL_FLUSH_SURFACES_ASYNC _IOW(QXLMAGIC, \
+                                       QXL_IO_FLUSH_SURFACES_ASYNC, __u32)
+#define QXL_IOCTL_FLUSH_RELEASE _IOW(QXLMAGIC, \
+                                       QXL_IO_FLUSH_RELEASE, __u32)
+#define QXL_IOCTL_QXL_IO_GETCFG _IOW(QXLMAGIC, \
+                                       QXL_IO_GETCFG, struct virtioqxl_config)
+#define QXL_IOCTL_QXL_IO_SET_RAMSTART _IOW(QXLMAGIC, \
+                                       QXL_IO_SET_RAMSTART, __u32)
+#endif /*  VIRTIO_BRIDGE_H */
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to