Xen 
 
Home About Xen.org Xen Xen Summit Wiki Mailing List Bug Tracker Xen Downloads
 
   
 

xen-devel

[Xen-devel] [PATCH] PV framebuffer

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH] PV framebuffer
From: Markus Armbruster <armbru@xxxxxxxxxx>
Date: Thu, 30 Nov 2006 19:29:44 +0100
Delivery-date: Thu, 30 Nov 2006 10:30:10 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux)
This is Anthony Liguori's virtual framebuffer forward ported and
extensively hacked based on feedback from xen-devel.

Its architecture is comparable to the common split device driver
architecture: xenfb and xenkbd modules serve as frontend in domU, and
the user space vncfb or sdlfb process serves as backend in dom0.

Signed-off-by: Markus Armbruster <armbru@xxxxxxxxxx>
Signed-off-by: Anthony Liguori <aliguori@xxxxxxxxxx>
---
Summary of changes since last submission:

* Update to hg tip 12621:d1b0a5adaeab

* Rename/move files to better match existing code: xenfb/ and xenkbd/
  to fbfront/, xenfb.h to fbif.h, xenkbd.h to kbdif.h.

* Patched tty_io.c is identical to non-xen version; remove it from the
  sparse tree instead.

 linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c   |    5 
 linux-2.6-xen-sparse/arch/ia64/kernel/setup.c       |    4 
 linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c |    7 
 linux-2.6-xen-sparse/drivers/char/tty_io.c          | 3264 --------------------
 linux-2.6-xen-sparse/drivers/xen/Kconfig            |   23 
 linux-2.6-xen-sparse/drivers/xen/Makefile           |    2 
 linux-2.6-xen-sparse/drivers/xen/console/console.c  |   10 
 linux-2.6-xen-sparse/drivers/xen/fbfront/Makefile   |    2 
 linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c    |  682 ++++
 linux-2.6-xen-sparse/drivers/xen/fbfront/xenkbd.c   |  300 +
 linux-2.6-xen-sparse/mm/memory.c                    |    1 
 tools/Makefile                                      |    1 
 tools/python/xen/xend/XendDevices.py                |    4 
 tools/python/xen/xend/XendDomainInfo.py             |   19 
 tools/python/xen/xend/image.py                      |   74 
 tools/python/xen/xend/server/vfbif.py               |   29 
 tools/python/xen/xm/create.py                       |   34 
 tools/xenfb/Makefile                                |   35 
 tools/xenfb/sdlfb.c                                 |  334 ++
 tools/xenfb/vncfb.c                                 |  393 ++
 tools/xenfb/xenfb.c                                 |  691 ++++
 tools/xenfb/xenfb.h                                 |   34 
 xen/include/public/io/fbif.h                        |  116 
 xen/include/public/io/kbdif.h                       |  108 
 24 files changed, 2895 insertions(+), 3277 deletions(-)

diff -r 2773c39df9a6 linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c
--- a/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c Wed Nov 29 12:16:19 
2006 +0000
+++ b/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c Thu Nov 30 15:13:53 
2006 +0100
@@ -1850,8 +1850,9 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #endif
        } else {
-               extern int console_use_vt;
-               console_use_vt = 0;
+#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
+               conswitchp = &dummy_con;
+#endif
        }
 }
 
diff -r 2773c39df9a6 linux-2.6-xen-sparse/arch/ia64/kernel/setup.c
--- a/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c     Wed Nov 29 12:16:19 
2006 +0000
+++ b/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c     Thu Nov 30 15:13:53 
2006 +0100
@@ -550,9 +550,9 @@ setup_arch (char **cmdline_p)
                       xen_start_info->nr_pages, xen_start_info->flags);
 
                if (!is_initial_xendomain()) {
-                       extern int console_use_vt;
+#if !defined(CONFIG_VT) || !defined(CONFIG_DUMMY_CONSOLE)
                        conswitchp = NULL;
-                       console_use_vt = 0;
+#endif
                }
        }
 #endif
diff -r 2773c39df9a6 linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c
--- a/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c       Wed Nov 29 
12:16:19 2006 +0000
+++ b/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c       Thu Nov 30 
15:13:53 2006 +0100
@@ -970,9 +970,10 @@ void __init setup_arch(char **cmdline_p)
 #endif
 #endif
                } else {
-                       extern int console_use_vt;
-                       console_use_vt = 0;
-               }
+#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
+                    conswitchp = &dummy_con;
+#endif
+                }
        }
 #else  /* CONFIG_XEN */
 
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/xen/Kconfig
--- a/linux-2.6-xen-sparse/drivers/xen/Kconfig  Wed Nov 29 12:16:19 2006 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/Kconfig  Thu Nov 30 15:13:53 2006 +0100
@@ -172,6 +172,29 @@ config XEN_NETDEV_FRONTEND
          dedicated device-driver domain, or your master control domain
          (domain 0), then you almost certainly want to say Y here.
 
+config XEN_FRAMEBUFFER
+       tristate "Framebuffer-device frontend driver"
+       depends on XEN && FB
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       default y
+       help
+         The framebuffer-device frontend drivers allows the kernel to create a
+         virtual framebuffer.  This framebuffer can be viewed in another
+         domain.  Unless this domain has access to a real video card, you
+         probably want to say Y here.
+
+config XEN_KEYBOARD
+       tristate "Keyboard-device frontend driver"
+       depends on XEN && XEN_FRAMEBUFFER && INPUT
+       default y
+       help
+         The keyboard-device frontend driver allows the kernel to create a
+         virtual keyboard.  This keyboard can then be driven by another
+         domain.  If you've said Y to CONFIG_XEN_FRAMEBUFFER, you probably
+         want to say Y here.
+
 config XEN_SCRUB_PAGES
        bool "Scrub memory before freeing it to Xen"
        default y
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/xen/Makefile
--- a/linux-2.6-xen-sparse/drivers/xen/Makefile Wed Nov 29 12:16:19 2006 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Thu Nov 30 15:13:53 2006 +0100
@@ -15,3 +15,5 @@ obj-$(CONFIG_XEN_NETDEV_FRONTEND)     += net
 obj-$(CONFIG_XEN_NETDEV_FRONTEND)      += netfront/
 obj-$(CONFIG_XEN_PCIDEV_BACKEND)       += pciback/
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND)      += pcifront/
+obj-$(CONFIG_XEN_FRAMEBUFFER)          += fbfront/
+obj-$(CONFIG_XEN_KEYBOARD)             += fbfront/
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/xen/console/console.c
--- a/linux-2.6-xen-sparse/drivers/xen/console/console.c        Wed Nov 29 
12:16:19 2006 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/console/console.c        Thu Nov 30 
15:13:53 2006 +0100
@@ -57,6 +57,7 @@
 #include <xen/interface/event_channel.h>
 #include <asm/hypervisor.h>
 #include <xen/evtchn.h>
+#include <xen/xenbus.h>
 #include <xen/xencons.h>
 
 /*
@@ -698,6 +699,15 @@ static int __init xencons_init(void)
        printk("Xen virtual console successfully installed as %s%d\n",
               DRV(xencons_driver)->name, xc_num);
 
+        /* Check about framebuffer messing up the console */
+        if (!is_initial_xendomain() &&
+           !xenbus_exists(XBT_NIL, "device", "vfb")) {
+               /* FIXME: this is ugly */
+               unregister_console(&kcons_info);
+               kcons_info.flags |= CON_CONSDEV;
+               register_console(&kcons_info);
+       }
+
        return 0;
 }
 
diff -r 2773c39df9a6 linux-2.6-xen-sparse/mm/memory.c
--- a/linux-2.6-xen-sparse/mm/memory.c  Wed Nov 29 12:16:19 2006 +0000
+++ b/linux-2.6-xen-sparse/mm/memory.c  Thu Nov 30 15:13:53 2006 +0100
@@ -882,6 +882,7 @@ unsigned long zap_page_range(struct vm_a
                tlb_finish_mmu(tlb, address, end);
        return end;
 }
+EXPORT_SYMBOL(zap_page_range);
 
 /*
  * Do a quick page-table lookup for a single page.
diff -r 2773c39df9a6 tools/Makefile
--- a/tools/Makefile    Wed Nov 29 12:16:19 2006 +0000
+++ b/tools/Makefile    Wed Nov 29 13:56:23 2006 +0100
@@ -19,6 +19,7 @@ SUBDIRS-y += libaio
 SUBDIRS-y += libaio
 SUBDIRS-y += blktap
 SUBDIRS-y += libfsimage
+SUBDIRS-y += xenfb
 SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen
 
 # These don't cross-compile
diff -r 2773c39df9a6 tools/python/xen/xend/XendDevices.py
--- a/tools/python/xen/xend/XendDevices.py      Wed Nov 29 12:16:19 2006 +0000
+++ b/tools/python/xen/xend/XendDevices.py      Thu Nov 23 18:54:35 2006 +0100
@@ -19,7 +19,7 @@
 # A collection of DevControllers 
 #
 
-from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
+from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, 
vfbif
 from xen.xend.server.BlktapController import BlktapController
 
 class XendDevices:
@@ -41,6 +41,8 @@ class XendDevices:
         'irq': irqif.IRQController,
         'usb': usbif.UsbifController,
         'tap': BlktapController,
+        'vfb': vfbif.VfbifController,
+        'vkbd': vfbif.VkbdifController,
     }
 
     #@classmethod
diff -r 2773c39df9a6 tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py   Wed Nov 29 12:16:19 2006 +0000
+++ b/tools/python/xen/xend/XendDomainInfo.py   Wed Nov 29 13:56:25 2006 +0100
@@ -458,7 +458,7 @@ class XendDomainInfo:
             try:
                 self._constructDomain()
                 self._storeVmDetails()
-                self._createDevices()
+                self._restoreDomain()
                 self._createChannels()
                 self._storeDomDetails()
                 self._endRestore()
@@ -1330,6 +1330,23 @@ class XendDomainInfo:
                 self.image.cleanupBootloading()
             raise VmError(str(exn))
 
+
+    def _restoreDomain(self):
+        log.debug('XendDomainInfo.restoreDomain: %s %s',
+                  self.domid,
+                  self.info['cpu_weight'])
+
+        if not self.infoIsSet('image'):
+            raise VmError('Missing image in configuration')
+
+        try:
+            self.image = image.create(self,
+                                      self.info['image'],
+                                      self.info['device'])
+
+            self._createDevices()
+        except RuntimeError, exn:
+            raise VmError(str(exn))
 
     def cleanupDomain(self):
         """Cleanup domain resources; release devices.  Idempotent.  Nothrow
diff -r 2773c39df9a6 tools/python/xen/xend/image.py
--- a/tools/python/xen/xend/image.py    Wed Nov 29 12:16:19 2006 +0000
+++ b/tools/python/xen/xend/image.py    Wed Nov 29 13:56:25 2006 +0100
@@ -23,6 +23,7 @@ import signal
 import signal
 
 import xen.lowlevel.xc
+import xen.util.auxbin
 from xen.xend import sxp
 from xen.xend.XendError import VmError, XendError
 from xen.xend.XendLogging import log
@@ -209,6 +210,79 @@ class LinuxImageHandler(ImageHandler):
                               cmdline        = self.cmdline,
                               ramdisk        = self.ramdisk,
                               features       = self.vm.getFeatures())
+
+    def configure(self, imageConfig, deviceConfig):
+        ImageHandler.configure(self, imageConfig, deviceConfig)
+
+        self.pid = 0
+        log.info("configuring linux guest")
+
+        # set up the graphics bits.
+        # FIXME: this is much like what we do for HVM, should it be 
+        # for all image types now?
+        self.display = sxp.child_value(imageConfig, 'display')
+        self.xauthority = sxp.child_value(imageConfig, 'xauthority')
+        self.vncconsole = sxp.child_value(imageConfig, 'vncconsole')
+        vncpasswd = sxp.child_value(imageConfig, 'vncpasswd')
+        self.vncpasswd = vncpasswd
+
+        self.vnc = sxp.child_value(imageConfig, 'vnc')
+        self.sdl = sxp.child_value(imageConfig, 'sdl')
+        if self.vnc:
+            self.vncdisplay = int(sxp.child_value(imageConfig, 'vncdisplay',
+                                                  self.vm.getDomid()))
+            self.vncunused = sxp.child_value(imageConfig, 'vncunused')
+            self.vnclisten = sxp.child_value(imageConfig, 'vnclisten')
+            if not(self.vnclisten):
+                self.vnclisten = 
xen.xend.XendRoot.instance().get_vnclisten_address()
+
+    def createDeviceModel(self):
+        if self.pid:
+            return
+        # Execute device model (for us, it's just the fb frontend)
+        if not self.vnc and not self.sdl:
+            return
+
+        if self.vnc:
+            args = [xen.util.auxbin.pathTo("xen-vncfb")]
+            if self.vncunused:
+                args += ['--unused']
+            elif self.vncdisplay:
+                args += [ "--vncport", "%d" %(5900 + self.vncdisplay,) ]
+            if self.vnclisten:
+                args += [ "--listen", self.vnclisten ]
+
+            # password check
+            if self.vncpasswd is None:
+                # get password from xend-config(if password omitted, None)
+                self.vncpasswd = 
xen.xend.XendRoot.instance().get_vncpasswd_default()
+
+                if self.vncpasswd is None:
+                    raise VmError('vncpasswd is not setup in the guest config 
or xend-config.')
+            if self.vncpasswd != '':
+                self.vm.storeVm("vncpasswd", self.vncpasswd)
+                log.info("vncpassword set to '%s'", self.vncpasswd)
+
+        elif self.sdl:
+            args = [xen.util.auxbin.pathTo("xen-sdlfb")]
+        args = args + [ "--domid", "%d" % self.vm.getDomid(),
+                        "--title", self.vm.info['name'] ]
+
+        env = dict(os.environ)
+        if self.display:
+            env['DISPLAY'] = self.display
+        if self.xauthority:
+            env['XAUTHORITY'] = self.xauthority
+        log.info("spawning video: %s", args)
+        self.pid = os.spawnve(os.P_NOWAIT, args[0], args, env)
+        log.info("device model pid: %d", self.pid)
+
+    def destroy(self):
+        if not self.pid:
+            return
+        os.kill(self.pid, signal.SIGKILL)
+        os.waitpid(self.pid, 0)
+        self.pid = 0
 
 class PPC_LinuxImageHandler(LinuxImageHandler):
 
diff -r 2773c39df9a6 tools/python/xen/xm/create.py
--- a/tools/python/xen/xm/create.py     Wed Nov 29 12:16:19 2006 +0000
+++ b/tools/python/xen/xm/create.py     Wed Nov 29 13:57:17 2006 +0100
@@ -284,6 +284,14 @@ gopts.var('usbport', val='PATH',
           use="""Add a physical USB port to a domain, as specified by the path
           to that port.  This option may be repeated to add more than one 
port.""")
 
+gopts.var('vfb', val="no|yes'",
+          fn=set_bool, default=0,
+          use="Make the domain a framebuffer backend.")
+
+gopts.var('vkbd', val="no|yes'",
+          fn=set_bool, default=0,
+          use="Make the domain a keyboard backend.")
+
 gopts.var('vif', 
val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
           fn=append_value, default=[],
           use="""Add a network interface with the given MAC address and bridge.
@@ -512,8 +520,10 @@ def configure_image(vals):
         config_image.append(['args', vals.extra])
 
     if vals.builder == 'hvm':
-        configure_hvm(config_image, vals)
-        
+        configure_hvm(config_image, vals) 
+
+    configure_graphics(config_image, vals)        
+       
     return config_image
     
 def configure_disks(config_devs, vals):
@@ -564,6 +574,13 @@ def configure_usb(config_devs, vals):
         config_usb = ['usbport', ['path', path]]
         config_devs.append(['device', config_usb])
 
+def configure_vfbs(config_devs, vals):
+    if vals.vfb:
+        config_devs.append(['device', ['vfb', []]])
+
+def configure_vkbds(config_devs, vals):
+    if vals.vkbd:
+        config_devs.append(['device', ['vkbd', []]])
 
 def configure_security(config, vals):
     """Create the config for ACM security labels.
@@ -661,13 +678,20 @@ def configure_vifs(config_devs, vals):
         config_devs.append(['device', config_vif])
 
 
+def configure_graphics(config_image, vals):
+    """Create the config for graphic consoles.
+    """
+    args = [ 'vnc', 'vncdisplay', 'vncconsole', 'vncunused',
+             'sdl', 'display', 'xauthority', 'vnclisten', 'vncpasswd']
+    for a in args:
+        if (vals.__dict__[a]):
+            config_image.append([a, vals.__dict__[a]])
+
 def configure_hvm(config_image, vals):
     """Create the config for HVM devices.
     """
     args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
              'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
-             'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
-             'sdl', 'display', 'xauthority',
              'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
     for a in args:
         if a in vals.__dict__ and vals.__dict__[a] is not None:
@@ -742,6 +766,8 @@ def make_config(vals):
     configure_vifs(config_devs, vals)
     configure_usb(config_devs, vals)
     configure_vtpm(config_devs, vals)
+    configure_vfbs(config_devs, vals)
+    configure_vkbds(config_devs, vals)
     configure_security(config, vals)
     config += config_devs
 
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/xen/fbfront/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/Makefile Thu Nov 30 15:13:53 
2006 +0100
@@ -0,0 +1,2 @@
+obj-$(CONFIG_XEN_FRAMEBUFFER)  := xenfb.o
+obj-$(CONFIG_XEN_KEYBOARD)     += xenkbd.o
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c  Thu Nov 30 15:13:53 
2006 +0100
@@ -0,0 +1,682 @@
+/*
+ * linux/drivers/video/xenfb.c -- Xen para-virtual frame buffer device
+ *
+ * Copyright (C) 2005-2006 Anthony Liguori <aliguori@xxxxxxxxxx>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@xxxxxxxxxx>
+ *
+ *  Based on linux/drivers/video/q40fb.c
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/*
+ * TODO:
+ *
+ * Switch to grant tables when they become capable of dealing with the
+ * frame buffer.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <asm/hypervisor.h>
+#include <xen/evtchn.h>
+#include <xen/interface/io/fbif.h>
+#include <xen/xenbus.h>
+#include <linux/kthread.h>
+
+struct xenfb_mapping
+{
+       struct list_head        link;
+       struct vm_area_struct   *vma;
+       atomic_t                map_refs;
+       int                     faults;
+       struct xenfb_info       *info;
+};
+
+struct xenfb_info
+{
+       struct task_struct      *kthread;
+       wait_queue_head_t       wq;
+
+       unsigned char           *fb;
+       struct fb_info          *fb_info;
+       struct timer_list       refresh;
+       int                     dirty;
+       int                     x1, y1, x2, y2; /* dirty rectangle,
+                                                  protected by mm_lock */
+       spinlock_t              mm_lock;
+       int                     nr_pages;
+       struct page             **pages;
+       struct list_head        mappings; /* protected by mm_lock */
+
+       unsigned                evtchn;
+       int                     irq;
+       struct xenfb_page       *page;
+       unsigned long           *mfns;
+       int                     update_wanted; /* XENFB_TYPE_UPDATE wanted */
+
+       struct xenbus_device    *xbdev;
+};
+
+static int xenfb_fps = 20;
+static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH 
/ 8;
+
+static int xenfb_remove(struct xenbus_device *);
+static void xenfb_init_shared_page(struct xenfb_info *);
+static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
+static void xenfb_disconnect_backend(struct xenfb_info *);
+
+static void xenfb_do_update(struct xenfb_info *info,
+                           int x, int y, int w, int h)
+{
+       union xenfb_out_event event;
+       __u32 prod;
+
+       event.type = XENFB_TYPE_UPDATE;
+       event.update.x = x;
+       event.update.y = y;
+       event.update.width = w;
+       event.update.height = h;
+
+       prod = info->page->out_prod;
+       /* caller ensures !xenfb_queue_full() */
+       mb();                   /* ensure ring space available */
+       XENFB_OUT_RING_REF(info->page, prod) = event;
+       wmb();                  /* ensure ring contents visible */
+       info->page->out_prod = prod + 1;
+
+       notify_remote_via_evtchn(info->evtchn);
+}
+
+static int xenfb_queue_full(struct xenfb_info *info)
+{
+       __u32 cons, prod;
+
+       prod = info->page->out_prod;
+       cons = info->page->out_cons;
+       return prod - cons == XENFB_OUT_RING_LEN;
+}
+
+static void xenfb_update_screen(struct xenfb_info *info)
+{
+       unsigned long flags;
+       int y1, y2, x1, x2;
+       struct xenfb_mapping *map;
+
+       if (!info->update_wanted)
+               return;
+       if (xenfb_queue_full(info))
+               return;
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+
+       y1 = info->y1;
+       y2 = info->y2;
+       x1 = info->x1;
+       x2 = info->x2;
+       info->x1 = info->y1 = INT_MAX;
+       info->x2 = info->y2 = 0;
+
+       list_for_each_entry(map, &info->mappings, link) {
+               if (!map->faults)
+                       continue;
+               zap_page_range(map->vma, map->vma->vm_start,
+                              map->vma->vm_end - map->vma->vm_start, NULL);
+               map->faults = 0;
+       }
+
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+
+       xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1);
+}
+
+static int xenfb_thread(void *data)
+{
+       struct xenfb_info *info = data;
+
+       while (!kthread_should_stop()) {
+               if (info->dirty) {
+                       info->dirty = 0;
+                       xenfb_update_screen(info);
+               }
+               wait_event_interruptible(info->wq,
+                       kthread_should_stop() || info->dirty);
+               try_to_freeze();
+       }
+       return 0;
+}
+
+static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *info)
+{
+       u32 v;
+
+       if (regno > info->cmap.len)
+               return 1;
+
+       red   >>= (16 - info->var.red.length);
+       green >>= (16 - info->var.green.length);
+       blue  >>= (16 - info->var.blue.length);
+
+       v = (red << info->var.red.offset) |
+           (green << info->var.green.offset) |
+           (blue << info->var.blue.offset);
+
+       /* FIXME is this sane?  check against xxxfb_setcolreg()!  */
+       switch (info->var.bits_per_pixel) {
+       case 16:
+       case 24:
+       case 32:
+               ((u32 *)info->pseudo_palette)[regno] = v;
+               break;
+       }
+       
+       return 0;
+}
+
+static void xenfb_timer(unsigned long data)
+{
+       struct xenfb_info *info = (struct xenfb_info *)data;
+       info->dirty = 1;
+       wake_up(&info->wq);
+}
+
+static void __xenfb_refresh(struct xenfb_info *info,
+                           int x1, int y1, int w, int h)
+{
+       int y2, x2;
+
+       y2 = y1 + h;
+       x2 = x1 + w;
+
+       if (info->y1 > y1)
+               info->y1 = y1;
+       if (info->y2 < y2)
+               info->y2 = y2;
+       if (info->x1 > x1)
+               info->x1 = x1;
+       if (info->x2 < x2)
+               info->x2 = x2;
+
+       if (timer_pending(&info->refresh))
+               return;
+
+       mod_timer(&info->refresh, jiffies + HZ/xenfb_fps);
+}
+
+static void xenfb_refresh(struct xenfb_info *info,
+                         int x1, int y1, int w, int h)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+       __xenfb_refresh(info, x1, y1, w, h);
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+}
+
+static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+       struct xenfb_info *info = p->par;
+
+       cfb_fillrect(p, rect);
+       xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height);
+}
+
+static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image)
+{
+       struct xenfb_info *info = p->par;
+
+       cfb_imageblit(p, image);
+       xenfb_refresh(info, image->dx, image->dy, image->width, image->height);
+}
+
+static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+       struct xenfb_info *info = p->par;
+
+       cfb_copyarea(p, area);
+       xenfb_refresh(info, area->dx, area->dy, area->width, area->height);
+}
+
+static void xenfb_vm_open(struct vm_area_struct *vma)
+{
+       struct xenfb_mapping *map = vma->vm_private_data;
+       atomic_inc(&map->map_refs);
+}
+
+static void xenfb_vm_close(struct vm_area_struct *vma)
+{
+       struct xenfb_mapping *map = vma->vm_private_data;
+       struct xenfb_info *info = map->info;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+       if (atomic_dec_and_test(&map->map_refs)) {
+               list_del(&map->link);
+               kfree(map);
+       }
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+}
+
+static struct page *xenfb_vm_nopage(struct vm_area_struct *vma,
+                                   unsigned long vaddr, int *type)
+{
+       struct xenfb_mapping *map = vma->vm_private_data;
+       struct xenfb_info *info = map->info;
+       int pgnr = (vaddr - vma->vm_start) >> PAGE_SHIFT;
+       unsigned long flags;
+       struct page *page;
+       int y1, y2;
+
+       if (pgnr >= info->nr_pages)
+               return NOPAGE_SIGBUS;
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+       page = info->pages[pgnr];
+       get_page(page);
+       map->faults++;
+
+       y1 = pgnr * PAGE_SIZE / info->fb_info->fix.line_length;
+       y2 = (pgnr * PAGE_SIZE + PAGE_SIZE - 1) / 
info->fb_info->fix.line_length;
+       if (y2 > info->fb_info->var.yres)
+               y2 = info->fb_info->var.yres;
+       __xenfb_refresh(info, 0, y1, info->fb_info->var.xres, y2 - y1);
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+
+       if (type)
+               *type = VM_FAULT_MINOR;
+
+       return page;
+}
+
+static struct vm_operations_struct xenfb_vm_ops = {
+       .open   = xenfb_vm_open,
+       .close  = xenfb_vm_close,
+       .nopage = xenfb_vm_nopage,
+};
+
+static int xenfb_mmap(struct fb_info *fb_info, struct vm_area_struct *vma)
+{
+       struct xenfb_info *info = fb_info->par;
+       unsigned long flags;
+       struct xenfb_mapping *map;
+       int map_pages;
+
+       if (!(vma->vm_flags & VM_WRITE))
+               return -EINVAL;
+       if (!(vma->vm_flags & VM_SHARED))
+               return -EINVAL;
+       if (vma->vm_pgoff != 0)
+               return -EINVAL;
+
+       map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT;
+       if (map_pages > info->nr_pages)
+               return -EINVAL;
+
+       map = kzalloc(sizeof(*map), GFP_KERNEL);
+       if (map == NULL)
+               return -ENOMEM;
+
+       map->vma = vma;
+       map->faults = 0;
+       map->info = info;
+       atomic_set(&map->map_refs, 1);
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+       list_add(&map->link, &info->mappings);
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+
+       vma->vm_ops = &xenfb_vm_ops;
+       vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED);
+       vma->vm_private_data = map;
+
+       return 0;
+}
+
+static struct fb_ops xenfb_fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_setcolreg   = xenfb_setcolreg,
+       .fb_fillrect    = xenfb_fillrect,
+       .fb_copyarea    = xenfb_copyarea,
+       .fb_imageblit   = xenfb_imageblit,
+       .fb_mmap        = xenfb_mmap,
+};
+
+static irqreturn_t xenfb_event_handler(int rq, void *dev_id,
+                                      struct pt_regs *regs)
+{
+       /*
+        * No in events recognized, simply ignore them all.
+        * If you need to recognize some, see xenbkd's input_handler()
+        * for how to do that.
+        */
+       struct xenfb_info *info = dev_id;
+       struct xenfb_page *page = info->page;
+
+       if (page->in_cons != page->in_prod) {
+               info->page->in_cons = info->page->in_prod;
+               notify_remote_via_evtchn(info->evtchn);
+       }
+       return IRQ_HANDLED;
+}
+
+static unsigned long vmalloc_to_mfn(void *address)
+{
+       return pfn_to_mfn(vmalloc_to_pfn(address));
+}
+
+static int __devinit xenfb_probe(struct xenbus_device *dev,
+                                const struct xenbus_device_id *id)
+{
+       struct xenfb_info *info;
+       struct fb_info *fb_info;
+       int ret;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+               return -ENOMEM;
+       }
+       dev->dev.driver_data = info;
+       info->xbdev = dev;
+       info->irq = -1;
+       info->x1 = info->y1 = INT_MAX;
+       spin_lock_init(&info->mm_lock);
+       init_waitqueue_head(&info->wq);
+       init_timer(&info->refresh);
+       info->refresh.function = xenfb_timer;
+       info->refresh.data = (unsigned long)info;
+       INIT_LIST_HEAD(&info->mappings);
+
+       info->fb = vmalloc(xenfb_mem_len);
+       if (info->fb == NULL)
+               goto error_nomem;
+       memset(info->fb, 0, xenfb_mem_len);
+
+       info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+       info->pages = kmalloc(sizeof(struct page *) * info->nr_pages,
+                             GFP_KERNEL);
+       if (info->pages == NULL)
+               goto error_nomem;
+
+       info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
+       if (!info->mfns)
+               goto error_nomem;
+
+       /* set up shared page */
+       info->page = (void *)__get_free_page(GFP_KERNEL);
+       if (!info->page)
+               goto error_nomem;
+
+       xenfb_init_shared_page(info);
+
+       fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
+                               /* see fishy hackery below */
+       if (fb_info == NULL)
+               goto error_nomem;
+
+       /* FIXME fishy hackery */
+       fb_info->pseudo_palette = fb_info->par;
+       fb_info->par = info;
+       /* /FIXME */
+       fb_info->screen_base = info->fb;
+
+       fb_info->fbops = &xenfb_fb_ops;
+       fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
+       fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
+       fb_info->var.bits_per_pixel = info->page->depth;
+
+       fb_info->var.red = (struct fb_bitfield){16, 8, 0};
+       fb_info->var.green = (struct fb_bitfield){8, 8, 0};
+       fb_info->var.blue = (struct fb_bitfield){0, 8, 0};
+
+       fb_info->var.activate = FB_ACTIVATE_NOW;
+       fb_info->var.height = -1;
+       fb_info->var.width = -1;
+       fb_info->var.vmode = FB_VMODE_NONINTERLACED;
+
+       fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
+       fb_info->fix.line_length = info->page->line_length;
+       fb_info->fix.smem_start = 0;
+       fb_info->fix.smem_len = xenfb_mem_len;
+       strcpy(fb_info->fix.id, "xen");
+       fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
+       fb_info->fix.accel = FB_ACCEL_NONE;
+
+       fb_info->flags = FBINFO_FLAG_DEFAULT;
+
+       ret = fb_alloc_cmap(&fb_info->cmap, 256, 0);
+       if (ret < 0) {
+               framebuffer_release(fb_info);
+               xenbus_dev_fatal(dev, ret, "fb_alloc_cmap");
+               goto error;
+       }
+
+       ret = register_framebuffer(fb_info);
+       if (ret) {
+               fb_dealloc_cmap(&info->fb_info->cmap);
+               framebuffer_release(fb_info);
+               xenbus_dev_fatal(dev, ret, "register_framebuffer");
+               goto error;
+       }
+       info->fb_info = fb_info;
+
+       /* FIXME should this be delayed until backend XenbusStateConnected? */
+       info->kthread = kthread_run(xenfb_thread, info, "xenfb thread");
+       if (IS_ERR(info->kthread)) {
+               ret = PTR_ERR(info->kthread);
+               info->kthread = NULL;
+               xenbus_dev_fatal(dev, ret, "register_framebuffer");
+               goto error;
+       }
+
+       ret = xenfb_connect_backend(dev, info);
+       if (ret < 0)
+               goto error;
+
+       return 0;
+
+ error_nomem:
+       ret = -ENOMEM;
+       xenbus_dev_fatal(dev, ret, "allocating device memory");
+ error:
+       xenfb_remove(dev);
+       return ret;
+}
+
+static int xenfb_resume(struct xenbus_device *dev)
+{
+       struct xenfb_info *info = dev->dev.driver_data;
+
+       xenfb_disconnect_backend(info);
+       xenfb_init_shared_page(info);
+       return xenfb_connect_backend(dev, info);
+}
+
+static int xenfb_remove(struct xenbus_device *dev)
+{
+       struct xenfb_info *info = dev->dev.driver_data;
+
+       del_timer(&info->refresh);
+       if (info->kthread)
+               kthread_stop(info->kthread);
+       xenfb_disconnect_backend(info);
+       if (info->fb_info) {
+               unregister_framebuffer(info->fb_info);
+               fb_dealloc_cmap(&info->fb_info->cmap);
+               framebuffer_release(info->fb_info);
+       }
+       free_page((unsigned long)info->page);
+       vfree(info->mfns);
+       kfree(info->pages);
+       vfree(info->fb);
+       kfree(info);
+
+       return 0;
+}
+
+static void xenfb_init_shared_page(struct xenfb_info *info)
+{
+       int i;
+
+       for (i = 0; i < info->nr_pages; i++)
+               info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE);
+
+       for (i = 0; i < info->nr_pages; i++)
+               info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
+
+       info->page->pd[0] = vmalloc_to_mfn(info->mfns);
+       info->page->pd[1] = 0;
+       info->page->width = XENFB_WIDTH;
+       info->page->height = XENFB_HEIGHT;
+       info->page->depth = XENFB_DEPTH;
+       info->page->line_length = (info->page->depth / 8) * info->page->width;
+       info->page->mem_length = xenfb_mem_len;
+       info->page->in_cons = info->page->in_prod = 0;
+       info->page->out_cons = info->page->out_prod = 0;
+}
+
+static int xenfb_connect_backend(struct xenbus_device *dev,
+                                struct xenfb_info *info)
+{
+       int ret;
+       struct xenbus_transaction xbt;
+
+       ret = xenbus_alloc_evtchn(dev, &info->evtchn);
+       if (ret)
+               return ret;
+       ret = bind_evtchn_to_irqhandler(info->evtchn, xenfb_event_handler,
+                                       0, "xenfb", info);
+       if (ret < 0) {
+               xenbus_free_evtchn(dev, info->evtchn);
+               xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
+               return ret;
+       }
+       info->irq = ret;
+
+ again:
+       ret = xenbus_transaction_start(&xbt);
+       if (ret) {
+               xenbus_dev_fatal(dev, ret, "starting transaction");
+               return ret;
+       }
+       ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
+                           virt_to_mfn(info->page));
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+                           info->evtchn);
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1");
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_transaction_end(xbt, 0);
+       if (ret) {
+               if (ret == -EAGAIN)
+                       goto again;
+               xenbus_dev_fatal(dev, ret, "completing transaction");
+               return ret;
+       }
+
+       xenbus_switch_state(dev, XenbusStateInitialised);
+       return 0;
+
+ error_xenbus:
+       xenbus_transaction_end(xbt, 1);
+       xenbus_dev_fatal(dev, ret, "writing xenstore");
+       return ret;
+}
+
+static void xenfb_disconnect_backend(struct xenfb_info *info)
+{
+       if (info->irq >= 0)
+               unbind_from_irqhandler(info->irq, info);
+       info->irq = -1;
+}
+
+static void xenfb_backend_changed(struct xenbus_device *dev,
+                                 enum xenbus_state backend_state)
+{
+       struct xenfb_info *info = dev->dev.driver_data;
+       int val;
+
+       switch (backend_state) {
+       case XenbusStateInitialising:
+       case XenbusStateInitialised:
+       case XenbusStateUnknown:
+       case XenbusStateClosed:
+               break;
+
+       case XenbusStateInitWait:
+       InitWait:
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateConnected:
+               /*
+                * Work around xenbus race condition: If backend goes
+                * through InitWait to Connected fast enough, we can
+                * get Connected twice here.
+                */
+               if (dev->state != XenbusStateConnected)
+                       goto InitWait; /* no InitWait seen yet, fudge it */
+
+               if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                                "request-update", "%d", &val) < 0)
+                       val = 0;
+               if (val)
+                       info->update_wanted = 1;
+               break;
+
+       case XenbusStateClosing:
+               // FIXME is this safe in any dev->state?
+               xenbus_frontend_closed(dev);
+               break;
+       }
+}
+
+static struct xenbus_device_id xenfb_ids[] = {
+       { "vfb" },
+       { "" }
+};
+
+static struct xenbus_driver xenfb = {
+       .name = "vfb",
+       .owner = THIS_MODULE,
+       .ids = xenfb_ids,
+       .probe = xenfb_probe,
+       .remove = xenfb_remove,
+       .resume = xenfb_resume,
+       .otherend_changed = xenfb_backend_changed,
+};
+
+static int __init xenfb_init(void)
+{
+       if (!is_running_on_xen())
+               return -ENODEV;
+
+       /* Nothing to do if running in dom0. */
+       if (is_initial_xendomain())
+               return -ENODEV;
+
+       return xenbus_register_frontend(&xenfb);
+}
+
+static void __exit xenfb_cleanup(void)
+{
+       return xenbus_unregister_driver(&xenfb);
+}
+
+module_init(xenfb_init);
+module_exit(xenfb_cleanup);
+
+MODULE_LICENSE("GPL");
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/xen/fbfront/xenkbd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenkbd.c Thu Nov 30 15:13:53 
2006 +0100
@@ -0,0 +1,300 @@
+/*
+ * linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device
+ *
+ * Copyright (C) 2005 Anthony Liguori <aliguori@xxxxxxxxxx>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@xxxxxxxxxx>
+ *
+ *  Based on linux/drivers/input/mouse/sermouse.c
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/*
+ * TODO:
+ *
+ * Switch to grant tables together with xenfb.c.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <asm/hypervisor.h>
+#include <xen/evtchn.h>
+#include <xen/interface/io/fbif.h>
+#include <xen/interface/io/kbdif.h>
+#include <xen/xenbus.h>
+
+struct xenkbd_info
+{
+       struct input_dev *dev;
+       struct xenkbd_page *page;
+       unsigned evtchn;
+       int irq;
+       struct xenbus_device *xbdev;
+};
+
+static int xenkbd_remove(struct xenbus_device *);
+static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info 
*);
+static void xenkbd_disconnect_backend(struct xenkbd_info *);
+
+/*
+ * Note: if you need to send out events, see xenfb_do_update() for how
+ * to do that.
+ */
+
+static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs)
+{
+       struct xenkbd_info *info = dev_id;
+       struct xenkbd_page *page = info->page;
+       __u32 cons, prod;
+
+       prod = page->in_prod;
+       if (prod == page->out_cons)
+               return IRQ_HANDLED;
+       rmb();                  /* ensure we see ring contents up to prod */
+       for (cons = page->in_cons; cons != prod; cons++) {
+               union xenkbd_in_event *event;
+               event = &XENKBD_IN_RING_REF(page, cons);
+
+               switch (event->type) {
+               case XENKBD_TYPE_MOTION:
+                       input_report_rel(info->dev, REL_X, event->motion.rel_x);
+                       input_report_rel(info->dev, REL_Y, event->motion.rel_y);
+                       break;
+               case XENKBD_TYPE_KEY:
+                       input_report_key(info->dev, event->key.keycode, 
event->key.pressed);
+                       break;
+               case XENKBD_TYPE_POS:
+                       input_report_abs(info->dev, ABS_X, event->pos.abs_x);
+                       input_report_abs(info->dev, ABS_Y, event->pos.abs_y);
+                       break;
+               }
+       }
+       input_sync(info->dev);
+       mb();                   /* ensure we got ring contents */
+       page->in_cons = cons;
+       notify_remote_via_evtchn(info->evtchn);
+
+       return IRQ_HANDLED;
+}
+
+int __devinit xenkbd_probe(struct xenbus_device *dev,
+                          const struct xenbus_device_id *id)
+{
+       int ret, i;
+       struct xenkbd_info *info;
+       struct input_dev *input_dev;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+               return -ENOMEM;
+       }
+       dev->dev.driver_data = info;
+       info->xbdev = dev;
+
+       info->page = (void *)__get_free_page(GFP_KERNEL);
+       if (!info->page)
+               goto error_nomem;
+       info->page->in_cons = info->page->in_prod = 0;
+       info->page->out_cons = info->page->out_prod = 0;
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               goto error_nomem;
+
+       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
+       input_dev->keybit[LONG(BTN_MOUSE)]
+               = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+       /* TODO additional buttons */
+       input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+
+       /* FIXME not sure this is quite right */
+       for (i = 0; i < 256; i++)
+               set_bit(i, input_dev->keybit);
+
+       input_dev->name = "Xen Virtual Keyboard/Mouse";
+
+       input_set_abs_params(input_dev, ABS_X, 0, XENFB_WIDTH, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+
+       ret = input_register_device(input_dev);
+       if (ret) {
+               input_free_device(input_dev);
+               xenbus_dev_fatal(dev, ret, "input_register_device");
+               goto error;
+       }
+       info->dev = input_dev;
+
+       ret = xenkbd_connect_backend(dev, info);
+       if (ret < 0)
+               goto error;
+
+       return 0;
+
+ error_nomem:
+       ret = -ENOMEM;
+       xenbus_dev_fatal(dev, ret, "allocating device memory");
+ error:
+       xenkbd_remove(dev);
+       return ret;
+}
+
+static int xenkbd_resume(struct xenbus_device *dev)
+{
+       struct xenkbd_info *info = dev->dev.driver_data;
+
+       xenkbd_disconnect_backend(info);
+       return xenkbd_connect_backend(dev, info);
+}
+
+static int xenkbd_remove(struct xenbus_device *dev)
+{
+       struct xenkbd_info *info = dev->dev.driver_data;
+
+       xenkbd_disconnect_backend(info);
+       input_unregister_device(info->dev);
+       free_page((unsigned long)info->page);
+       kfree(info);
+       return 0;
+}
+
+static int xenkbd_connect_backend(struct xenbus_device *dev,
+                                 struct xenkbd_info *info)
+{
+       int ret;
+       struct xenbus_transaction xbt;
+
+       ret = xenbus_alloc_evtchn(dev, &info->evtchn);
+       if (ret)
+               return ret;
+       ret = bind_evtchn_to_irqhandler(info->evtchn, input_handler, 0,
+                                       "xenkbd", info);
+       if (ret < 0) {
+               xenbus_free_evtchn(dev, info->evtchn);
+               xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
+               return ret;
+       }
+       info->irq = ret;
+
+ again:
+       ret = xenbus_transaction_start(&xbt);
+       if (ret) {
+               xenbus_dev_fatal(dev, ret, "starting transaction");
+               return ret;
+       }
+       ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
+                           virt_to_mfn(info->page));
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+                           info->evtchn);
+       if (ret)
+               goto error_xenbus;
+       ret = xenbus_transaction_end(xbt, 0);
+       if (ret) {
+               if (ret == -EAGAIN)
+                       goto again;
+               xenbus_dev_fatal(dev, ret, "completing transaction");
+               return ret;
+       }
+
+       xenbus_switch_state(dev, XenbusStateInitialised);
+       return 0;
+
+ error_xenbus:
+       xenbus_transaction_end(xbt, 1);
+       xenbus_dev_fatal(dev, ret, "writing xenstore");
+       return ret;
+}
+
+static void xenkbd_disconnect_backend(struct xenkbd_info *info)
+{
+       if (info->irq >= 0)
+               unbind_from_irqhandler(info->irq, info);
+       info->irq = -1;
+}
+
+static void xenkbd_backend_changed(struct xenbus_device *dev,
+                                  enum xenbus_state backend_state)
+{
+       struct xenkbd_info *info = dev->dev.driver_data;
+       int ret, val;
+
+       switch (backend_state) {
+       case XenbusStateInitialising:
+       case XenbusStateInitialised:
+       case XenbusStateUnknown:
+       case XenbusStateClosed:
+               break;
+
+       case XenbusStateInitWait:
+       InitWait:
+               ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                                  "feature-abs-pointer", "%d", &val);
+               if (ret < 0)
+                       val = 0;
+               if (val) {
+                       ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
+                                           "request-abs-pointer", "1");
+                       if (ret)
+                               ; /* FIXME */
+               }
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateConnected:
+               /*
+                * Work around xenbus race condition: If backend goes
+                * through InitWait to Connected fast enough, we can
+                * get Connected twice here.
+                */
+               if (dev->state != XenbusStateConnected)
+                       goto InitWait; /* no InitWait seen yet, fudge it */
+               break;
+
+       case XenbusStateClosing:
+               xenbus_frontend_closed(dev);
+               break;
+       }
+}
+
+static struct xenbus_device_id xenkbd_ids[] = {
+       { "vkbd" },
+       { "" }
+};
+
+static struct xenbus_driver xenkbd = {
+       .name = "vkbd",
+       .owner = THIS_MODULE,
+       .ids = xenkbd_ids,
+       .probe = xenkbd_probe,
+       .remove = xenkbd_remove,
+       .resume = xenkbd_resume,
+       .otherend_changed = xenkbd_backend_changed,
+};
+
+static int __init xenkbd_init(void)
+{
+       if (!is_running_on_xen())
+               return -ENODEV;
+
+       /* Nothing to do if running in dom0. */
+       if (is_initial_xendomain())
+               return -ENODEV;
+
+       return xenbus_register_frontend(&xenkbd);
+}
+
+static void __exit xenkbd_cleanup(void)
+{
+       return xenbus_unregister_driver(&xenkbd);
+}
+
+module_init(xenkbd_init);
+module_exit(xenkbd_cleanup);
+
+MODULE_LICENSE("GPL");
diff -r 2773c39df9a6 tools/python/xen/xend/server/vfbif.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/xend/server/vfbif.py     Thu Nov 23 09:18:03 2006 +0100
@@ -0,0 +1,29 @@
+from xen.xend.server.DevController import DevController
+
+class VfbifController(DevController):
+    """Virtual frame buffer controller. Handles all vfb devices for a domain.
+    """
+
+    def __init__(self, vm):
+        DevController.__init__(self, vm)
+
+    def getDeviceDetails(self, config):
+        """@see DevController.getDeviceDetails"""
+        devid = 0
+        back = {}
+        front = {}
+        return (devid, back, front)
+
+class VkbdifController(DevController):
+    """Virtual keyboard controller. Handles all vkbd devices for a domain.
+    """
+
+    def __init__(self, vm):
+        DevController.__init__(self, vm)
+
+    def getDeviceDetails(self, config):
+        """@see DevController.getDeviceDetails"""
+        devid = 0
+        back = {}
+        front = {}
+        return (devid, back, front)
diff -r 2773c39df9a6 tools/xenfb/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/xenfb/Makefile      Thu Nov 23 11:29:49 2006 +0100
@@ -0,0 +1,35 @@
+XEN_ROOT=../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) 
-I$(XEN_ROOT)/linux-2.6-xen-sparse/include
+LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE)
+
+INSTALL         = install
+INSTALL_PROG    = $(INSTALL) -m0755
+INSTALL_DIR     = $(INSTALL) -d -m0755
+
+.PHONY: all
+all: build
+
+.PHONY: build
+build: mk-symlinks
+       $(MAKE) vncfb sdlfb
+
+install: all
+       $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin
+       $(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb
+       $(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb
+
+sdlfb: sdlfb.o xenfb.o
+
+sdlfb.o: CFLAGS += $(shell sdl-config --cflags)
+sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore
+
+clean:
+       $(RM) *.o *~ vncfb sdlfb
+
+vncfb: vncfb.o xenfb.o
+vncfb.o: CFLAGS += $(shell libvncserver-config --cflags)
+vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore
+
+sdlfb.o xenfb.o vncfb.o: xenfb.h
diff -r 2773c39df9a6 tools/xenfb/sdlfb.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/xenfb/sdlfb.c       Thu Nov 23 11:09:50 2006 +0100
@@ -0,0 +1,334 @@
+#include <SDL.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <stdlib.h>
+#include <linux/input.h>
+#include <getopt.h>
+#include <string.h>
+#include "xenfb.h"
+
+struct SDLFBData
+{
+       SDL_Surface *dst;
+       SDL_Surface *src;
+};
+
+/*
+ * Map from scancode to Linux input layer keycode.  Scancodes are
+ * hardware-specific.  This map assumes a standard AT or PS/2
+ * keyboard.
+ *
+ * Why use scancodes?  We can't use key symbols, because they don't
+ * identify keys --- they're what keys are mapped to.  The standard
+ * German keymap, for instance, maps both KEY_COMMA and KEY_102ND to
+ * SDLK_LESS.
+ */
+static int keymap[256] = {
+       [9] = KEY_ESC,
+       [10] = KEY_1,
+       [11] = KEY_2,
+       [12] = KEY_3,
+       [13] = KEY_4,
+       [14] = KEY_5,
+       [15] = KEY_6,
+       [16] = KEY_7,
+       [17] = KEY_8,
+       [18] = KEY_9,
+       [19] = KEY_0,
+       [20] = KEY_MINUS,
+       [21] = KEY_EQUAL,
+       [22] = KEY_BACKSPACE,
+       [23] = KEY_TAB,
+       [24] = KEY_Q,
+       [25] = KEY_W,
+       [26] = KEY_E,
+       [27] = KEY_R,
+       [28] = KEY_T,
+       [29] = KEY_Y,
+       [30] = KEY_U,
+       [31] = KEY_I,
+       [32] = KEY_O,
+       [33] = KEY_P,
+       [34] = KEY_LEFTBRACE,
+       [35] = KEY_RIGHTBRACE,
+       [36] = KEY_ENTER,
+       [37] = KEY_LEFTCTRL,
+       [38] = KEY_A,
+       [39] = KEY_S,
+       [40] = KEY_D,
+       [41] = KEY_F,
+       [42] = KEY_G,
+       [43] = KEY_H,
+       [44] = KEY_J,
+       [45] = KEY_K,
+       [46] = KEY_L,
+       [47] = KEY_SEMICOLON,
+       [48] = KEY_APOSTROPHE,
+       [49] = KEY_GRAVE,
+       [50] = KEY_LEFTSHIFT,
+       [51] = KEY_BACKSLASH,
+       [52] = KEY_Z,
+       [53] = KEY_X,
+       [54] = KEY_C,
+       [55] = KEY_V,
+       [56] = KEY_B,
+       [57] = KEY_N,
+       [58] = KEY_M,
+       [59] = KEY_COMMA,
+       [60] = KEY_DOT,
+       [61] = KEY_SLASH,
+       [62] = KEY_RIGHTSHIFT,
+       [63] = KEY_KPASTERISK,
+       [64] = KEY_LEFTALT,
+       [65] = KEY_SPACE,
+       [66] = KEY_CAPSLOCK,
+       [67] = KEY_F1,
+       [68] = KEY_F2,
+       [69] = KEY_F3,
+       [70] = KEY_F4,
+       [71] = KEY_F5,
+       [72] = KEY_F6,
+       [73] = KEY_F7,
+       [74] = KEY_F8,
+       [75] = KEY_F9,
+       [76] = KEY_F10,
+       [77] = KEY_NUMLOCK,
+       [78] = KEY_SCROLLLOCK,
+       [79] = KEY_KP7,
+       [80] = KEY_KP8,
+       [81] = KEY_KP9,
+       [82] = KEY_KPMINUS,
+       [83] = KEY_KP4,
+       [84] = KEY_KP5,
+       [85] = KEY_KP6,
+       [86] = KEY_KPPLUS,
+       [87] = KEY_KP1,
+       [88] = KEY_KP2,
+       [89] = KEY_KP3,
+       [90] = KEY_KP0,
+       [91] = KEY_KPDOT,
+       [94] = KEY_102ND,       /* FIXME is this correct? */
+       [95] = KEY_F11,
+       [96] = KEY_F12,
+       [108] = KEY_KPENTER,
+       [109] = KEY_RIGHTCTRL,
+       [112] = KEY_KPSLASH,
+       [111] = KEY_SYSRQ,
+       [113] = KEY_RIGHTALT,
+       [97] = KEY_HOME,
+       [98] = KEY_UP,
+       [99] = KEY_PAGEUP,
+       [100] = KEY_LEFT,
+       [102] = KEY_RIGHT,
+       [103] = KEY_END,
+       [104] = KEY_DOWN,
+       [105] = KEY_PAGEDOWN,
+       [106] = KEY_INSERT,
+       [107] = KEY_DELETE,
+       [110] = KEY_PAUSE,
+       [115] = KEY_LEFTMETA,
+       [116] = KEY_RIGHTMETA,
+       [117] = KEY_MENU,
+};
+
+static int btnmap[] = {
+       [SDL_BUTTON_LEFT] = BTN_LEFT,
+       [SDL_BUTTON_MIDDLE] = BTN_MIDDLE,
+       [SDL_BUTTON_RIGHT] = BTN_RIGHT,
+       /* FIXME not 100% sure about these: */
+       [SDL_BUTTON_WHEELUP] = BTN_FORWARD,
+       [SDL_BUTTON_WHEELDOWN] BTN_BACK
+};
+
+static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int 
height)
+{
+       struct SDLFBData *data = xenfb->user_data;
+       SDL_Rect r = { x, y, width, height };
+       SDL_BlitSurface(data->src, &r, data->dst, &r);
+       SDL_UpdateRect(data->dst, x, y, width, height);
+}
+
+static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event)
+{
+       int x, y, ret;
+
+       switch (event->type) {
+       case SDL_KEYDOWN:
+       case SDL_KEYUP:
+               if (keymap[event->key.keysym.scancode] == 0)
+                       break;
+               ret = xenfb_send_key(xenfb,
+                                    event->type == SDL_KEYDOWN,
+                                    keymap[event->key.keysym.scancode]);
+               if (ret < 0)
+                       fprintf(stderr, "Key %d %s lost (%s)\n",
+                               keymap[event->key.keysym.scancode],
+                               event->type == SDL_KEYDOWN ? "down" : "up",
+                               strerror(errno));
+               break;
+       case SDL_MOUSEMOTION:
+               if (xenfb->abs_pointer_wanted) {
+                       SDL_GetMouseState(&x, &y);
+                       ret = xenfb_send_position(xenfb, x, y);
+               } else {
+                       SDL_GetRelativeMouseState(&x, &y);
+                       ret = xenfb_send_motion(xenfb, x, y);
+               }
+               if (ret < 0)
+                       fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
+                               x, y, strerror(errno));
+               break;
+       case SDL_MOUSEBUTTONDOWN:
+       case SDL_MOUSEBUTTONUP:
+               if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap))
+                       break;
+               if (btnmap[event->button.button] == 0)
+                       break;
+               ret = xenfb_send_key(xenfb,
+                                    event->type == SDL_MOUSEBUTTONDOWN,
+                                    btnmap[event->button.button]);
+               if (ret < 0)
+                       fprintf(stderr, "Button %d %s lost (%s)\n",
+                               btnmap[event->button.button] - BTN_MOUSE,
+                               event->type == SDL_MOUSEBUTTONDOWN ? "down" : 
"up",
+                               strerror(errno));
+               break;
+       case SDL_QUIT:
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct option options[] = {
+       { "domid", 1, NULL, 'd' },
+       { "title", 1, NULL, 't' },
+};
+
+int main(int argc, char **argv)
+{
+       struct xenfb *xenfb;
+       int domid = -1;
+        char * title = NULL;
+       fd_set readfds;
+       int nfds;
+       struct SDLFBData data;
+       SDL_Rect r;
+       struct timeval tv;
+       SDL_Event event;
+       int do_quit = 0;
+       int opt;
+       char *endp;
+
+       while ((opt = getopt_long(argc, argv, "d:t:", options,
+                                 NULL)) != -1) {
+               switch (opt) {
+                case 'd':
+                       domid = strtol(optarg, &endp, 10);
+                       if (endp == optarg || *endp) {
+                               fprintf(stderr, "Invalid domain id 
specified\n");
+                               exit(1);
+                       }
+                       break;
+                case 't':
+                       title = strdup(optarg);
+                       break;
+                }
+        }
+        if (optind != argc) {
+               fprintf(stderr, "Invalid options!\n");
+               exit(1);
+        }
+        if (domid <= 0) {
+               fprintf(stderr, "Domain ID must be specified!\n");
+               exit(1);
+        }
+
+       xenfb = xenfb_new();
+       if (xenfb == NULL) {
+               fprintf(stderr, "Could not create framebuffer (%s)\n",
+                       strerror(errno));
+               exit(1);
+        }
+
+       if (xenfb_attach_dom(xenfb, domid) < 0) {
+               fprintf(stderr, "Could not connect to domain (%s)\n",
+                       strerror(errno));
+               exit(1);
+        }
+
+       if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+               fprintf(stderr, "Could not initialize SDL\n");
+               exit(1);
+       }
+
+       data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth,
+                                   SDL_SWSURFACE);
+       if (!data.dst) {
+               fprintf(stderr, "SDL_SetVideoMode failed\n");
+               exit(1);
+       }
+
+       data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels,
+                                           xenfb->width, xenfb->height,
+                                           xenfb->depth, xenfb->row_stride,
+                                           0xFF0000, 0xFF00, 0xFF, 0);
+
+       if (!data.src) {
+               fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n");
+               exit(1);
+       }
+
+        if (title == NULL)
+               title = strdup("xen-sdlfb");
+        SDL_WM_SetCaption(title, title);
+
+       r.x = r.y = 0;
+       r.w = xenfb->width;
+       r.h = xenfb->height;
+       SDL_BlitSurface(data.src, &r, data.dst, &r);
+       SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height);
+
+       xenfb->update = sdl_update;
+       xenfb->user_data = &data;
+
+       SDL_ShowCursor(0);
+
+       /*
+        * We need to wait for fds becoming ready or SDL events to
+        * arrive.  We time out the select after 10ms to poll for SDL
+        * events.  Clunky, but works.  Could avoid the clunkiness
+        * with a separate thread.
+        */
+       for (;;) {
+               FD_ZERO(&readfds);
+               nfds = xenfb_select_fds(xenfb, &readfds);
+               tv = (struct timeval){0, 10000};
+
+               if (select(nfds, &readfds, NULL, NULL, &tv) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       fprintf(stderr,
+                               "Can't select() on event channel (%s)\n",
+                               strerror(errno));
+                       break;
+               }
+
+               while (SDL_PollEvent(&event)) {
+                       if (!sdl_on_event(xenfb, &event))
+                               do_quit = 1;
+               }
+
+                if (do_quit)
+                       break;
+
+               xenfb_poll(xenfb, &readfds);
+       }
+
+       xenfb_delete(xenfb);
+
+       SDL_Quit();
+
+       return 0;
+}
diff -r 2773c39df9a6 tools/xenfb/vncfb.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/xenfb/vncfb.c       Thu Nov 23 11:32:35 2006 +0100
@@ -0,0 +1,393 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <rfb/rfb.h>
+#include <rfb/keysym.h>
+#include <linux/input.h>
+#include <xs.h>
+#include "xenfb.h"
+
+static int xk2linux[0x10000] = {
+       [XK_a] = KEY_A,
+       [XK_b] = KEY_B,
+       [XK_c] = KEY_C,
+       [XK_d] = KEY_D,
+       [XK_e] = KEY_E,
+       [XK_f] = KEY_F,
+       [XK_g] = KEY_G,
+       [XK_h] = KEY_H,
+       [XK_i] = KEY_I,
+       [XK_j] = KEY_J,
+       [XK_k] = KEY_K,
+       [XK_l] = KEY_L,
+       [XK_m] = KEY_M,
+       [XK_n] = KEY_N,
+       [XK_o] = KEY_O,
+       [XK_p] = KEY_P,
+       [XK_q] = KEY_Q,
+       [XK_r] = KEY_R,
+       [XK_s] = KEY_S,
+       [XK_t] = KEY_T,
+       [XK_u] = KEY_U,
+       [XK_v] = KEY_V,
+       [XK_w] = KEY_W,
+       [XK_x] = KEY_X,
+       [XK_y] = KEY_Y,
+       [XK_z] = KEY_Z,
+       [XK_A] = KEY_A,
+       [XK_B] = KEY_B,
+       [XK_C] = KEY_C,
+       [XK_D] = KEY_D,
+       [XK_E] = KEY_E,
+       [XK_F] = KEY_F,
+       [XK_G] = KEY_G,
+       [XK_H] = KEY_H,
+       [XK_I] = KEY_I,
+       [XK_J] = KEY_J,
+       [XK_K] = KEY_K,
+       [XK_L] = KEY_L,
+       [XK_M] = KEY_M,
+       [XK_N] = KEY_N,
+       [XK_O] = KEY_O,
+       [XK_P] = KEY_P,
+       [XK_Q] = KEY_Q,
+       [XK_R] = KEY_R,
+       [XK_S] = KEY_S,
+       [XK_T] = KEY_T,
+       [XK_U] = KEY_U,
+       [XK_V] = KEY_V,
+       [XK_W] = KEY_W,
+       [XK_X] = KEY_X,
+       [XK_Y] = KEY_Y,
+       [XK_Z] = KEY_Z,
+       [XK_0] = KEY_0,
+       [XK_1] = KEY_1,
+       [XK_2] = KEY_2,
+       [XK_3] = KEY_3,
+       [XK_4] = KEY_4,
+       [XK_5] = KEY_5,
+       [XK_6] = KEY_6,
+       [XK_7] = KEY_7,
+       [XK_8] = KEY_8,
+       [XK_9] = KEY_9,
+       [XK_Return] = KEY_ENTER,
+       [XK_BackSpace] = KEY_BACKSPACE,
+       [XK_Tab] = KEY_TAB,
+       [XK_Pause] = KEY_PAUSE,
+       [XK_Delete] = KEY_DELETE,
+       [XK_slash] = KEY_SLASH,
+       [XK_minus] = KEY_MINUS,
+       [XK_equal] = KEY_EQUAL,
+       [XK_Escape] = KEY_ESC,
+       [XK_braceleft] = KEY_LEFTBRACE,
+       [XK_braceright] = KEY_RIGHTBRACE,
+       [XK_bracketleft] = KEY_LEFTMETA,
+       [XK_bracketright] = KEY_RIGHTMETA,
+       [XK_Control_L] = KEY_LEFTCTRL,
+       [XK_Control_R] = KEY_RIGHTCTRL,
+       [XK_Shift_L] = KEY_LEFTSHIFT,
+       [XK_Shift_R] = KEY_RIGHTSHIFT,
+       [XK_Alt_L] = KEY_LEFTALT,
+       [XK_Alt_R] = KEY_RIGHTALT,
+       [XK_semicolon] = KEY_SEMICOLON, 
+       [XK_apostrophe] = KEY_APOSTROPHE,
+       [XK_grave] = KEY_GRAVE,
+       [XK_backslash] = KEY_BACKSLASH,
+       [XK_comma] = KEY_COMMA,
+       [XK_period] = KEY_DOT,
+       [XK_space] = KEY_SPACE,
+       [XK_Caps_Lock] = KEY_CAPSLOCK,
+       [XK_Num_Lock] = KEY_NUMLOCK,
+       [XK_Scroll_Lock] = KEY_SCROLLLOCK,
+       [XK_Sys_Req] = KEY_SYSRQ,
+       [XK_Linefeed] = KEY_LINEFEED,
+       [XK_Home] = KEY_HOME,
+       [XK_Pause] = KEY_PAUSE,
+       [XK_F1] = KEY_F1,
+       [XK_F2] = KEY_F2,
+       [XK_F3] = KEY_F3,
+       [XK_F4] = KEY_F4,
+       [XK_F5] = KEY_F5,
+       [XK_F6] = KEY_F6,
+       [XK_F7] = KEY_F7,
+       [XK_F8] = KEY_F8,
+       [XK_F9] = KEY_F9,
+       [XK_F10] = KEY_F10,
+       [XK_F11] = KEY_F11,
+       [XK_F12] = KEY_F12,
+       [XK_Up] = KEY_UP,
+       [XK_Page_Up] = KEY_PAGEUP,
+       [XK_Left] = KEY_LEFT,
+       [XK_Right] = KEY_RIGHT,
+       [XK_End] = KEY_END,
+       [XK_Down] = KEY_DOWN,
+       [XK_Page_Down] = KEY_PAGEDOWN,
+       [XK_Insert] = KEY_INSERT, 
+       [XK_colon] = KEY_SEMICOLON,
+       [XK_quotedbl] = KEY_APOSTROPHE,
+       [XK_less] = KEY_COMMA,
+       [XK_greater] = KEY_DOT,
+       [XK_question] = KEY_SLASH,
+       [XK_bar] = KEY_BACKSLASH,
+       [XK_asciitilde] = KEY_GRAVE,
+       [XK_exclam] = KEY_1,
+       [XK_at] = KEY_2,
+       [XK_numbersign] = KEY_3,
+       [XK_dollar] = KEY_4,
+       [XK_percent] = KEY_5,
+       [XK_asciicircum] = KEY_6,
+       [XK_ampersand] = KEY_7,
+       [XK_asterisk] = KEY_8,
+       [XK_parenleft] = KEY_9,
+       [XK_parenright] = KEY_0,
+       [XK_underscore] = KEY_MINUS,
+       [XK_plus] = KEY_EQUAL,
+};
+
+static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl)
+{
+       /*
+        * We need to map to the key's Linux input layer keycode.
+        * Unfortunately, we don't get the key here, only the
+        * rfbKeySym, which is what the key is mapped to.  Mapping
+        * back to the key is impossible in general, even when you
+        * know the keymap.  For instance, the standard German keymap
+        * maps both KEY_COMMA and KEY_102ND to XK_less.  We simply
+        * assume standard US layout.  This sucks.
+        */
+       rfbScreenInfoPtr server = cl->screen;
+       struct xenfb *xenfb = server->screenData;
+       if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux))
+               return;
+       if (xk2linux[keycode] == 0)
+               return;
+       if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0)
+               fprintf(stderr, "Key %d %s lost (%s)\n",
+                       xk2linux[keycode], down ? "down" : "up",
+                       strerror(errno));
+}
+
+static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl)
+{
+       /* initial pointer state: at (0,0), buttons up */
+       static int last_x, last_y, last_button;
+       rfbScreenInfoPtr server = cl->screen;
+       struct xenfb *xenfb = server->screenData;
+       int i, last_down, down, ret;
+
+       for (i = 0; i < 8; i++) {
+               last_down = last_button & (1 << i);
+               down = buttonMask & (1 << i);
+               if (down == last_down)
+                       continue;
+               /* FIXME this assumes buttons are numbered the same; verify 
they are */
+               if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0)
+                       fprintf(stderr, "Button %d %s lost (%s)\n",
+                               i, down ? "down" : "up", strerror(errno));
+       }
+
+       if (x != last_x || y != last_y) {
+               if (xenfb->abs_pointer_wanted) 
+                       ret = xenfb_send_position(xenfb, x, y);
+               else
+                       ret = xenfb_send_motion(xenfb, x - last_x, y - last_y);
+               if (ret < 0)
+                       fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
+                               x, y, strerror(errno));
+       }
+
+       last_button = buttonMask;
+       last_x = x;
+       last_y = y;
+}
+
+static void xenstore_write_vncport(int port, int domid)
+{
+       char *buf = NULL, *path;
+       char portstr[10];
+       struct xs_handle *xsh = NULL;
+
+       xsh = xs_daemon_open();
+       if (xsh == NULL)
+               return;
+
+       path = xs_get_domain_path(xsh, domid);
+       if (path == NULL) {
+               fprintf(stderr, "Can't get domain path (%s)\n",
+                       strerror(errno));
+               goto out;
+       }
+
+       if (asprintf(&buf, "%s/console/vnc-port", path) == -1) {
+               fprintf(stderr, "Can't make vncport path\n");
+               goto out;
+       }
+
+       if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
+               fprintf(stderr, "Can't make vncport value\n");
+               goto out;
+       }
+
+       if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr)))
+               fprintf(stderr, "Can't set vncport (%s)\n",
+                       strerror(errno));
+
+ out:
+       free(buf);
+}
+
+
+static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h)
+{
+       rfbScreenInfoPtr server = xenfb->user_data;
+       rfbMarkRectAsModified(server, x, y, x + w, y + h);
+}
+
+static struct option options[] = {
+       { "domid", 1, NULL, 'd' },
+       { "vncport", 1, NULL, 'p' },
+       { "title", 1, NULL, 't' },
+       { "unused", 0, NULL, 'u' },
+       { "listen", 1, NULL, 'l' },
+};
+
+int main(int argc, char **argv)
+{
+       rfbScreenInfoPtr server;
+       char *fake_argv[7] = { "vncfb", "-rfbport", "5901", 
+                               "-desktop", "xen-vncfb", 
+                               "-listen", "127.0.0.1" };
+       int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]);
+       int domid = -1, port = -1;
+       char *title = NULL;
+       char *listen = NULL;
+       bool unused = false;
+       int opt;
+       struct xenfb *xenfb;
+       fd_set readfds;
+       int nfds;
+       char portstr[10];
+       char *endp;
+
+       while ((opt = getopt_long(argc, argv, "d:p:t:u", options,
+                                 NULL)) != -1) {
+               switch (opt) {
+                case 'd':
+                       errno = 0;
+                       domid = strtol(optarg, &endp, 10);
+                       if (endp == optarg || *endp || errno) {
+                               fprintf(stderr, "Invalid domain id 
specified\n");
+                               exit(1);
+                       }
+                       break;
+                case 'p':
+                       errno = 0;
+                       port = strtol(optarg, &endp, 10);
+                       if (endp == optarg || *endp || errno) {
+                               fprintf(stderr, "Invalid port specified\n");
+                               exit(1);
+                       }
+                       break;
+                case 't':
+                       title = strdup(optarg);
+                       break;
+                case 'u':
+                       unused = true;
+                       break;
+                case 'l':
+                       listen = strdup(optarg);
+                       break;
+                }
+        }
+        if (optind != argc) {
+               fprintf(stderr, "Invalid options!\n");
+               exit(1);
+        }
+        if (domid <= 0) {
+               fprintf(stderr, "Domain ID must be specified!\n");
+               exit(1);
+        }
+            
+        if (port <= 0)
+               port = 5900 + domid;
+       if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
+               fprintf(stderr, "Invalid port specified\n");
+               exit(1);
+        }
+            
+       fake_argv[2] = portstr;
+
+        if (title != NULL)
+               fake_argv[4] = title;
+
+        if (listen != NULL)
+               fake_argv[6] = listen;
+
+       signal(SIGPIPE, SIG_IGN);
+
+       xenfb = xenfb_new();
+       if (xenfb == NULL) {
+               fprintf(stderr, "Could not create framebuffer (%s)\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       if (xenfb_attach_dom(xenfb, domid) < 0) {
+               fprintf(stderr, "Could not connect to domain (%s)\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       server = rfbGetScreen(&fake_argc, fake_argv, 
+                             xenfb->width, xenfb->height,
+                             8, 3, xenfb->depth / 8);
+       if (server == NULL) {
+               fprintf(stderr, "Could not create VNC server\n");
+               exit(1);
+       }
+
+       xenfb->user_data = server;
+       xenfb->update = vnc_update;
+
+        if (unused)
+               server->autoPort = true;
+
+       server->serverFormat.redShift = 16;
+       server->serverFormat.greenShift = 8;
+       server->serverFormat.blueShift = 0;
+       server->kbdAddEvent = on_kbd_event;
+       server->ptrAddEvent = on_ptr_event;
+       server->frameBuffer = xenfb->pixels;
+       server->screenData = xenfb;
+       server->cursor = NULL;
+       rfbInitServer(server);
+
+       rfbRunEventLoop(server, -1, true);
+
+        xenstore_write_vncport(server->port, domid);
+
+       for (;;) {
+               FD_ZERO(&readfds);
+               nfds = xenfb_select_fds(xenfb, &readfds);
+
+               if (select(nfds, &readfds, NULL, NULL, NULL) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       fprintf(stderr,
+                               "Can't select() on event channel (%s)\n",
+                               strerror(errno));
+                       break;
+               }
+
+               xenfb_poll(xenfb, &readfds);
+       }
+
+       rfbScreenCleanup(server);
+       xenfb_delete(xenfb);
+
+       return 0;
+}
diff -r 2773c39df9a6 tools/xenfb/xenfb.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/xenfb/xenfb.c       Thu Nov 30 09:48:37 2006 +0100
@@ -0,0 +1,691 @@
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <xenctrl.h>
+#include <xen/io/xenbus.h>
+#include <xen/io/fbif.h>
+#include <xen/io/kbdif.h>
+#include <sys/select.h>
+#include <stdbool.h>
+#include <xen/linux/evtchn.h>
+#include <xen/event_channel.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <xs.h>
+
+#include "xenfb.h"
+
+// FIXME defend against malicious frontend?
+
+struct xenfb_device {
+       const char *devicetype;
+       char nodename[64];      /* backend xenstore dir */
+       char otherend[64];      /* frontend xenstore dir */
+       int otherend_id;        /* frontend domid */
+       enum xenbus_state state; /* backend state */
+       void *page;             /* shared page */
+       evtchn_port_t port;
+       struct xenfb_private *xenfb;
+};
+
+struct xenfb_private {
+       struct xenfb pub;
+       int evt_xch;            /* event channel driver handle */
+       int xc;                 /* hypervisor interface handle */
+       struct xs_handle *xsh;  /* xs daemon handle */
+       struct xenfb_device fb, kbd;
+       size_t fb_len;          /* size of framebuffer */
+};
+
+static void xenfb_detach_dom(struct xenfb_private *);
+
+static char *xenfb_path_in_dom(struct xs_handle *xsh,
+                              char *buf, size_t size,
+                              unsigned domid, const char *fmt, ...)
+{
+       va_list ap;
+       char *domp = xs_get_domain_path(xsh, domid);
+       int n;
+
+        if (domp == NULL)
+               return NULL;
+
+       n = snprintf(buf, size, "%s/", domp);
+       free(domp);
+       if (n >= size)
+               return NULL;
+
+       va_start(ap, fmt);
+       n += vsnprintf(buf + n, size - n, fmt, ap);
+       va_end(ap);
+       if (n >= size)
+               return NULL;
+
+       return buf;
+}
+
+static int xenfb_xs_scanf1(struct xs_handle *xsh,
+                          const char *dir, const char *node,
+                          const char *fmt, void *dest)
+{
+       char buf[1024];
+       char *p;
+       int ret;
+
+       if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) {
+               errno = ENOENT;
+               return -1;
+        }
+       p = xs_read(xsh, XBT_NULL, buf, NULL);
+       if (!p) {
+               errno = ENOENT;
+               return -1;
+        }
+       ret = sscanf(p, fmt, dest);
+       free(p);
+       if (ret != 1) {
+               errno = EDOM;
+               return -1;
+        }
+       return ret;
+}
+
+static int xenfb_xs_printf(struct xs_handle *xsh,
+                          const char *dir, const char *node, char *fmt, ...)
+{
+       va_list ap;
+       char key[1024];
+       char val[1024];
+       int n;
+
+       if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) {
+               errno = ENOENT;
+               return -1;
+        }
+
+       va_start(ap, fmt);
+       n = vsnprintf(val, sizeof(val), fmt, ap);
+       va_end(ap);
+       if (n >= sizeof(val)) {
+               errno = ENOSPC; /* close enough */
+               return -1;
+       }
+
+       if (!xs_write(xsh, XBT_NULL, key, val, n))
+               return -1;
+       return 0;
+}
+
+static void xenfb_device_init(struct xenfb_device *dev,
+                             const char *type,
+                             struct xenfb_private *xenfb)
+{
+       dev->devicetype = type;
+       dev->otherend_id = -1;
+       dev->port = -1;
+       dev->xenfb = xenfb;
+}
+
+int xenfb_device_set_domain(struct xenfb_device *dev, int domid)
+{
+       struct xenfb_private *xenfb = dev->xenfb;
+
+       dev->otherend_id = domid;
+
+       if (!xenfb_path_in_dom(xenfb->xsh,
+                              dev->otherend, sizeof(dev->otherend),
+                              domid, "device/%s/0", dev->devicetype)) {
+               errno = ENOENT;
+               return -1;
+       }
+       if (!xenfb_path_in_dom(xenfb->xsh,
+                              dev->nodename, sizeof(dev->nodename),
+                              0, "backend/%s/%d/0", dev->devicetype, domid)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       return 0;
+}
+
+struct xenfb *xenfb_new(void)
+{
+       struct xenfb_private *xenfb = malloc(sizeof(*xenfb));
+       int serrno;
+
+       if (xenfb == NULL)
+               return NULL;
+
+       memset(xenfb, 0, sizeof(*xenfb));
+       xenfb->evt_xch = xenfb->xc = -1;
+       xenfb_device_init(&xenfb->fb, "vfb", xenfb);
+       xenfb_device_init(&xenfb->kbd, "vkbd", xenfb);
+
+       xenfb->evt_xch = xc_evtchn_open();
+       if (xenfb->evt_xch == -1)
+               goto fail;
+
+       xenfb->xc = xc_interface_open();
+       if (xenfb->xc == -1)
+               goto fail;
+
+       xenfb->xsh = xs_daemon_open();
+       if (!xenfb->xsh)
+               goto fail;
+
+       return &xenfb->pub;
+
+ fail:
+       serrno = errno;
+       xenfb_delete(&xenfb->pub);
+       errno = serrno;
+       return NULL;
+}
+
+void xenfb_delete(struct xenfb *xenfb_pub)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+
+       xenfb_detach_dom(xenfb);
+       if (xenfb->xc >= 0)
+               xc_interface_close(xenfb->xc);
+       if (xenfb->evt_xch >= 0)
+               xc_evtchn_close(xenfb->evt_xch);
+       if (xenfb->xsh)
+               xs_daemon_close(xenfb->xsh);
+       free(xenfb);
+}
+
+static enum xenbus_state xenfb_read_state(struct xs_handle *xsh,
+                                         const char *dir)
+{
+       int ret, state;
+
+       ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state);
+       if (ret < 0)
+               return XenbusStateUnknown;
+
+       if ((unsigned)state > XenbusStateClosed)
+               state = XenbusStateUnknown;
+       return state;
+}
+
+static int xenfb_switch_state(struct xenfb_device *dev,
+                             enum xenbus_state state)
+{
+       struct xs_handle *xsh = dev->xenfb->xsh;
+
+       if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0)
+               return -1;
+       dev->state = state;
+       return 0;
+}
+
+static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir,
+                               unsigned awaited)
+{
+       unsigned state, dummy;
+       char **vec;
+
+       for (;;) {
+               state = xenfb_read_state(xsh, dir);
+               if (state < 0)
+                       return -1;
+
+               if ((1 << state) & awaited)
+                       return state;
+
+               vec = xs_read_watch(xsh, &dummy);
+               if (!vec)
+                       return -1;
+               free(vec);
+       }
+}
+
+static int xenfb_wait_for_backend_creation(struct xenfb_device *dev)
+{
+       struct xs_handle *xsh = dev->xenfb->xsh;
+       int state;
+
+       if (!xs_watch(xsh, dev->nodename, ""))
+               return -1;
+       state = xenfb_wait_for_state(xsh, dev->nodename,
+                       (1 << XenbusStateInitialising)
+                       | (1 << XenbusStateClosed)
+#if 1 /* TODO fudging state to permit restarting; to be removed */
+                       | (1 << XenbusStateInitWait)
+                       | (1 << XenbusStateConnected)
+                       | (1 << XenbusStateClosing)
+#endif
+                       );
+       xs_unwatch(xsh, dev->nodename, "");
+
+       switch (state) {
+#if 1
+       case XenbusStateInitWait:
+       case XenbusStateConnected:
+               printf("Fudging state to %d\n", XenbusStateInitialising); /* 
FIXME */
+#endif
+       case XenbusStateInitialising:
+       case XenbusStateClosing:
+       case XenbusStateClosed:
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int xenfb_hotplug(struct xenfb_device *dev)
+{
+       if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename,
+                           "hotplug-status", "connected"))
+               return -1;
+       return 0;
+}
+
+static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev)
+{
+       switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
+#if 1 /* TODO fudging state to permit restarting; to be removed */
+                       (1 << XenbusStateInitialised)
+                       | (1 << XenbusStateConnected)
+#else
+                       1 << XenbusStateInitialised,
+#endif
+                       )) {
+#if 1
+       case XenbusStateConnected:
+               printf("Fudging state to %d\n", XenbusStateInitialised); /* 
FIXME */
+#endif
+       case XenbusStateInitialised:
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int xenfb_map_fb(struct xenfb_private *xenfb, int domid)
+{
+       struct xenfb_page *page = xenfb->fb.page;
+       int n_fbmfns;
+       int n_fbdirs;
+       unsigned long *fbmfns;
+
+       n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+       n_fbdirs = n_fbmfns * sizeof(unsigned long);
+       n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+
+       /*
+        * Bug alert: xc_map_foreign_batch() can fail partly and
+        * return a non-null value.  This is a design flaw.  When it
+        * happens, we happily continue here, and later crash on
+        * access.
+        */
+       fbmfns = xc_map_foreign_batch(xenfb->xc, domid,
+                       PROT_READ, page->pd, n_fbdirs);
+       if (fbmfns == NULL)
+               return -1;
+
+       xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid,
+                               PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
+       if (xenfb->pub.pixels == NULL) {
+               munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
+               return -1;
+       }
+
+       return munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
+}
+
+static int xenfb_bind(struct xenfb_device *dev)
+{
+       struct xenfb_private *xenfb = dev->xenfb;
+       unsigned long mfn;
+       evtchn_port_t evtchn;
+
+       if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu",
+                           &mfn) < 0)
+               return -1;
+       if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u",
+                           &evtchn) < 0)
+               return -1;
+
+       dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch,
+                                              dev->otherend_id, evtchn);
+       if (dev->port == -1)
+               return -1;
+
+       dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id,
+                       XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn);
+       if (dev->page == NULL)
+               return -1;
+
+       return 0;
+}
+
+static void xenfb_unbind(struct xenfb_device *dev)
+{
+       if (dev->page) {
+               munmap(dev->page, XC_PAGE_SIZE);
+               dev->page = NULL;
+       }
+        if (dev->port >= 0) {
+               xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port);
+               dev->port = -1;
+       }
+}
+
+static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev)
+{
+       switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
+                                    1 << XenbusStateConnected)) {
+       case XenbusStateConnected:
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static void xenfb_dev_fatal(struct xenfb_device *dev, int err,
+                           const char *fmt, ...)
+{
+       struct xs_handle *xsh = dev->xenfb->xsh;
+       va_list ap;
+       char errdir[80];
+       char buf[1024];
+       int n;
+
+       fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       if (err)
+               fprintf(stderr, " (%s)", strerror(err));
+       putc('\n', stderr);
+
+       if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0,
+                              "error/%s", dev->nodename))
+               goto out;       /* FIXME complain */
+
+       va_start(ap, fmt);
+       n = snprintf(buf, sizeof(buf), "%d ", err);
+       snprintf(buf + n, sizeof(buf) - n, fmt, ap);
+       va_end(ap);
+
+       if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0)
+               goto out;       /* FIXME complain */
+
+ out:
+       xenfb_switch_state(dev, XenbusStateClosing);
+}
+
+int xenfb_attach_dom(struct xenfb *xenfb_pub, int domid)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+       struct xs_handle *xsh = xenfb->xsh;
+       int val, serrno;
+       struct xenfb_page *fb_page;
+
+       xenfb_detach_dom(xenfb);
+
+       xenfb_device_set_domain(&xenfb->fb, domid);
+       xenfb_device_set_domain(&xenfb->kbd, domid);
+
+       if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0)
+               goto error;
+       if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0)
+               goto error;
+
+       if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", 
"1"))
+               goto error;
+       if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait))
+               goto error;
+       if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait))
+               goto error;
+
+       if (xenfb_hotplug(&xenfb->fb) < 0)
+               goto error;
+       if (xenfb_hotplug(&xenfb->kbd) < 0)
+               goto error;
+
+       if (!xs_watch(xsh, xenfb->fb.otherend, ""))
+               goto error;
+       if (!xs_watch(xsh, xenfb->kbd.otherend, ""))
+               goto error;
+
+       if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0)
+               goto error;
+       if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0)
+               goto error;
+
+       if (xenfb_bind(&xenfb->fb) < 0)
+               goto error;
+       if (xenfb_bind(&xenfb->kbd) < 0)
+               goto error;
+
+       if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update",
+                           "%d", &val) < 0)
+               val = 0;
+       if (!val) {
+               errno = ENOTSUP;
+               goto error;
+       }
+       xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1");
+
+       /* TODO check for permitted ranges */
+       fb_page = xenfb->fb.page;
+       xenfb->pub.depth = fb_page->depth;
+       xenfb->pub.width = fb_page->width;
+       xenfb->pub.height = fb_page->height;
+       /* TODO check for consistency with the above */
+       xenfb->fb_len = fb_page->mem_length;
+       xenfb->pub.row_stride = fb_page->line_length;
+
+       if (xenfb_map_fb(xenfb, domid) < 0)
+               goto error;
+
+       if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
+               goto error;
+       if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
+               goto error;
+
+       if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0)
+               goto error;
+       if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer",
+                           "%d", &val) < 0)
+               val = 0;
+       xenfb->pub.abs_pointer_wanted = val;
+
+       return 0;
+
+ error:
+       serrno = errno;
+       xenfb_detach_dom(xenfb);
+       xenfb_dev_fatal(&xenfb->fb, serrno, "on fire");
+       xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire");
+        errno = serrno;
+        return -1;
+}
+
+static void xenfb_detach_dom(struct xenfb_private *xenfb)
+{
+       xenfb_unbind(&xenfb->fb);
+       xenfb_unbind(&xenfb->kbd);
+       if (xenfb->pub.pixels) {
+               munmap(xenfb->pub.pixels, xenfb->fb_len);
+               xenfb->pub.pixels = NULL;
+       }
+}
+
+static void xenfb_on_fb_event(struct xenfb_private *xenfb)
+{
+       uint32_t prod, cons;
+       struct xenfb_page *page = xenfb->fb.page;
+
+       prod = page->out_prod;
+       if (prod == page->out_cons)
+               return;
+       rmb();                  /* ensure we see ring contents up to prod */
+       for (cons = page->out_cons; cons != prod; cons++) {
+               union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+
+               switch (event->type) {
+               case XENFB_TYPE_UPDATE:
+                    if (xenfb->pub.update)
+                       xenfb->pub.update(&xenfb->pub,
+                                         event->update.x, event->update.y,
+                                         event->update.width, 
event->update.height);
+                    break;
+               }
+       }
+       mb();                   /* ensure we're done with ring contents */
+       page->out_cons = cons;
+       xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);
+}
+
+static void xenfb_on_kbd_event(struct xenfb_private *xenfb)
+{
+       struct xenkbd_page *page = xenfb->kbd.page;
+
+       /* We don't understand any keyboard events, so just ignore them. */
+       if (page->out_prod == page->out_cons)
+               return;
+       page->out_cons = page->out_prod;
+       xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
+}
+
+static void xenfb_on_state_change(struct xenfb_device *dev)
+{
+       enum xenbus_state state;
+
+       state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
+
+       switch (state) {
+       case XenbusStateUnknown:
+       case XenbusStateInitialising:
+       case XenbusStateInitWait:
+       case XenbusStateInitialised:
+       case XenbusStateConnected:
+               break;
+       case XenbusStateClosing:
+               xenfb_unbind(dev);
+               xenfb_switch_state(dev, state);
+               break;
+       case XenbusStateClosed:
+               xs_unwatch(dev->xenfb->xsh, dev->otherend, "");
+               xenfb_switch_state(dev, state);
+       }
+}
+
+int xenfb_poll(struct xenfb *xenfb_pub, fd_set *readfds)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+       evtchn_port_t port;
+       unsigned dummy;
+       char **vec;
+
+       if (FD_ISSET(xc_evtchn_fd(xenfb->evt_xch), readfds)) {
+               port = xc_evtchn_pending(xenfb->evt_xch);
+               if (port == -1)
+                       return -1;
+
+               if (port == xenfb->fb.port)
+                       xenfb_on_fb_event(xenfb);
+               else if (port == xenfb->kbd.port)
+                       xenfb_on_kbd_event(xenfb);
+
+               if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1)
+                       return -1;
+       }
+
+       if (FD_ISSET(xs_fileno(xenfb->xsh), readfds)) {
+               vec = xs_read_watch(xenfb->xsh, &dummy);
+               free(vec);
+               xenfb_on_state_change(&xenfb->fb);
+               xenfb_on_state_change(&xenfb->kbd);
+       }
+
+       return 0;
+}
+
+int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+       int fd1 = xc_evtchn_fd(xenfb->evt_xch);
+       int fd2 = xs_fileno(xenfb->xsh);
+
+       FD_SET(fd1, readfds);
+       FD_SET(fd2, readfds);
+       return fd1 > fd2 ? fd1 + 1 : fd2 + 1;
+}
+
+static int xenfb_kbd_event(struct xenfb_private *xenfb,
+                          union xenkbd_in_event *event)
+{
+       uint32_t prod;
+       struct xenkbd_page *page = xenfb->kbd.page;
+
+       if (xenfb->kbd.state != XenbusStateConnected)
+               return 0;
+
+       prod = page->in_prod;
+       if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
+               errno = EAGAIN;
+               return -1;
+       }
+
+       mb();                   /* ensure ring space available */
+       XENKBD_IN_RING_REF(page, prod) = *event;
+       wmb();                  /* ensure ring contents visible */
+       page->in_prod = prod + 1;
+       return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
+}
+
+int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+       union xenkbd_in_event event;
+
+       memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+       event.type = XENKBD_TYPE_KEY;
+       event.key.pressed = down ? 1 : 0;
+       event.key.keycode = keycode;
+
+       return xenfb_kbd_event(xenfb, &event);
+}
+
+int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+       union xenkbd_in_event event;
+
+       memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+       event.type = XENKBD_TYPE_MOTION;
+       event.motion.rel_x = rel_x;
+       event.motion.rel_y = rel_y;
+
+       return xenfb_kbd_event(xenfb, &event);
+}
+
+int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y)
+{
+       struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
+       union xenkbd_in_event event;
+
+       memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+       event.type = XENKBD_TYPE_POS;
+       event.pos.abs_x = abs_x;
+       event.pos.abs_y = abs_y;
+
+       return xenfb_kbd_event(xenfb, &event);
+}
diff -r 2773c39df9a6 tools/xenfb/xenfb.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/xenfb/xenfb.h       Thu Nov 23 11:32:28 2006 +0100
@@ -0,0 +1,34 @@
+#ifndef _XENFB_H_
+#define _XENFB_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct xenfb
+{
+       void *pixels;
+
+       int row_stride;
+       int depth;
+       int width;
+       int height;
+       int abs_pointer_wanted;
+
+       void *user_data;
+
+       void (*update)(struct xenfb *xenfb, int x, int y, int width, int 
height);
+};
+
+struct xenfb *xenfb_new(void);
+void xenfb_delete(struct xenfb *xenfb);
+
+int xenfb_attach_dom(struct xenfb *xenfb, int domid);
+
+int xenfb_select_fds(struct xenfb *xenfb, fd_set *readfds);
+int xenfb_poll(struct xenfb *xenfb, fd_set *readfds);
+
+int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode);
+int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y);
+int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y);
+
+#endif
diff -r 2773c39df9a6 xen/include/public/io/fbif.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/public/io/fbif.h      Wed Nov 29 17:34:09 2006 +0100
@@ -0,0 +1,116 @@
+/*
+ * fbif.h -- Xen virtual frame buffer device
+ *
+ * Copyright (C) 2005 Anthony Liguori <aliguori@xxxxxxxxxx>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@xxxxxxxxxx>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#ifndef __XEN_PUBLIC_IO_FBIF_H__
+#define __XEN_PUBLIC_IO_FBIF_H__
+
+#include <asm/types.h>
+
+/* Out events (frontend -> backend) */
+
+/*
+ * Out events may be sent only when requested by backend, and receipt
+ * of an unknown out event is an error.
+ */
+
+/* Event type 1 currently not used */
+/*
+ * Framebuffer update notification event
+ * Capable frontend sets feature-update in xenstore.
+ * Backend requests it by setting request-update in xenstore.
+ */
+#define XENFB_TYPE_UPDATE 2
+
+struct xenfb_update
+{
+       __u8 type;              /* XENFB_TYPE_UPDATE */
+       __s32 x;                /* source x */
+       __s32 y;                /* source y */
+       __s32 width;            /* rect width */
+       __s32 height;           /* rect height */
+};
+
+#define XENFB_OUT_EVENT_SIZE 40
+
+union xenfb_out_event
+{
+       __u8 type;
+       struct xenfb_update update;
+       char pad[XENFB_OUT_EVENT_SIZE];
+};
+
+/* In events (backend -> frontend) */
+
+/*
+ * Frontends should ignore unknown in events.
+ * No in events currently defined.
+ */
+
+#define XENFB_IN_EVENT_SIZE 40
+
+union xenfb_in_event
+{
+       __u8 type;
+       char pad[XENFB_IN_EVENT_SIZE];
+};
+
+/* shared page */
+
+#define XENFB_IN_RING_SIZE 1024
+#define XENFB_IN_RING_LEN (XENFB_IN_RING_SIZE / XENFB_IN_EVENT_SIZE)
+#define XENFB_IN_RING_OFFS 1024
+#define XENFB_IN_RING(page) \
+    ((union xenfb_in_event *)((char *)(page) + XENFB_IN_RING_OFFS))
+#define XENFB_IN_RING_REF(page, idx) \
+    (XENFB_IN_RING((page))[(idx) % XENFB_IN_RING_LEN])
+
+#define XENFB_OUT_RING_SIZE 2048
+#define XENFB_OUT_RING_LEN (XENFB_OUT_RING_SIZE / XENFB_OUT_EVENT_SIZE)
+#define XENFB_OUT_RING_OFFS (XENFB_IN_RING_OFFS + XENFB_IN_RING_SIZE)
+#define XENFB_OUT_RING(page) \
+    ((union xenfb_out_event *)((char *)(page) + XENFB_OUT_RING_OFFS))
+#define XENFB_OUT_RING_REF(page, idx) \
+    (XENFB_OUT_RING((page))[(idx) % XENFB_OUT_RING_LEN])
+
+struct xenfb_page
+{
+       __u32 in_cons, in_prod;
+       __u32 out_cons, out_prod;
+
+       __s32 width;         /* the width of the framebuffer (in pixels) */
+       __s32 height;        /* the height of the framebuffer (in pixels) */
+       __u32 line_length;   /* the length of a row of pixels (in bytes) */
+       __u32 mem_length;    /* the length of the framebuffer (in bytes) */
+       __u8 depth;          /* the depth of a pixel (in bits) */
+
+       /*
+        * Framebuffer page directory
+        *
+        * Each directory page holds PAGE_SIZE / sizeof(*pd)
+        * framebuffer pages, and can thus map up to PAGE_SIZE *
+        * PAGE_SIZE / sizeof(*pd) bytes.  With PAGE_SIZE == 4096 and
+        * sizeof(unsigned long) == 4, that's 4 Megs.  Two directory
+        * pages should be enough for a while.
+        */
+       unsigned long pd[2];
+};
+
+/*
+ * Wart: xenkbd needs to know resolution.  Put it here until a better
+ * solution is found, but don't leak it to the backend.
+ */
+#ifdef __KERNEL__
+#define XENFB_WIDTH 800
+#define XENFB_HEIGHT 600
+#define XENFB_DEPTH 32
+#endif
+
+#endif
diff -r 2773c39df9a6 xen/include/public/io/kbdif.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/public/io/kbdif.h     Wed Nov 29 17:35:42 2006 +0100
@@ -0,0 +1,108 @@
+/*
+ * kbdif.h -- Xen virtual keyboard/mouse
+ *
+ * Copyright (C) 2005 Anthony Liguori <aliguori@xxxxxxxxxx>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@xxxxxxxxxx>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#ifndef __XEN_PUBLIC_IO_KBDIF_H__
+#define __XEN_PUBLIC_IO_KBDIF_H__
+
+#include <asm/types.h>
+
+/* In events (backend -> frontend) */
+
+/*
+ * Frontends should ignore unknown in events.
+ */
+
+/* Pointer movement event */
+#define XENKBD_TYPE_MOTION  1
+/* Event type 2 currently not used */
+/* Key event (includes pointer buttons) */
+#define XENKBD_TYPE_KEY     3
+/*
+ * Pointer position event
+ * Capable backend sets feature-abs-pointer in xenstore.
+ * Frontend requests ot instead of XENKBD_TYPE_MOTION by setting
+ * request-abs-update in xenstore.
+ */
+#define XENKBD_TYPE_POS     4
+
+struct xenkbd_motion
+{
+       __u8 type;         /* XENKBD_TYPE_MOTION */
+       __s32 rel_x;       /* relative X motion */
+       __s32 rel_y;       /* relative Y motion */
+};
+
+struct xenkbd_key
+{
+       __u8 type;         /* XENKBD_TYPE_KEY */
+       __u8 pressed;      /* 1 if pressed; 0 otherwise */
+       __u32 keycode;     /* KEY_* from linux/input.h */
+};
+
+struct xenkbd_position
+{
+       __u8 type;         /* XENKBD_TYPE_POS */
+       __s32 abs_x;       /* absolute X position (in FB pixels) */
+       __s32 abs_y;       /* absolute Y position (in FB pixels) */
+};
+
+#define XENKBD_IN_EVENT_SIZE 40
+
+union xenkbd_in_event
+{
+       __u8 type;
+       struct xenkbd_motion motion;
+       struct xenkbd_key key;
+       struct xenkbd_position pos;
+       char pad[XENKBD_IN_EVENT_SIZE];
+};
+
+/* Out events (frontend -> backend) */
+
+/*
+ * Out events may be sent only when requested by backend, and receipt
+ * of an unknown out event is an error.
+ * No out events currently defined.
+ */
+
+#define XENKBD_OUT_EVENT_SIZE 40
+
+union xenkbd_out_event
+{
+       __u8 type;
+       char pad[XENKBD_OUT_EVENT_SIZE];
+};
+
+/* shared page */
+
+#define XENKBD_IN_RING_SIZE 2048
+#define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE)
+#define XENKBD_IN_RING_OFFS 1024
+#define XENKBD_IN_RING(page) \
+    ((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS))
+#define XENKBD_IN_RING_REF(page, idx) \
+    (XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN])
+
+#define XENKBD_OUT_RING_SIZE 1024
+#define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE)
+#define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE)
+#define XENKBD_OUT_RING(page) \
+    ((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS))
+#define XENKBD_OUT_RING_REF(page, idx) \
+    (XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN])
+
+struct xenkbd_page
+{
+       __u32 in_cons, in_prod;
+       __u32 out_cons, out_prod;
+};
+
+#endif
diff -r 2773c39df9a6 linux-2.6-xen-sparse/drivers/char/tty_io.c
--- a/linux-2.6-xen-sparse/drivers/char/tty_io.c        Wed Nov 29 12:16:19 
2006 +0000
+++ /dev/null   Thu Jan 01 00:00:00 1970 +0000
@@ -1,3264 +0,0 @@
-/*
- *  linux/drivers/char/tty_io.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-/*
- * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
- * or rs-channels. It also implements echoing, cooked mode etc.
- *
- * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
- *
- * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
- * tty_struct and tty_queue structures.  Previously there was an array
- * of 256 tty_struct's which was statically allocated, and the
- * tty_queue structures were allocated at boot time.  Both are now
- * dynamically allocated only when the tty is open.
- *
- * Also restructured routines so that there is more of a separation
- * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
- * the low-level tty routines (serial.c, pty.c, console.c).  This
- * makes for cleaner and more compact code.  -TYT, 9/17/92 
- *
- * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
- * which can be dynamically activated and de-activated by the line
- * discipline handling modules (like SLIP).
- *
- * NOTE: pay no attention to the line discipline code (yet); its
- * interface is still subject to change in this version...
- * -- TYT, 1/31/92
- *
- * Added functionality to the OPOST tty handling.  No delays, but all
- * other bits should be there.
- *     -- Nick Holloway <alfie@xxxxxxxxxxxxxxxxx>, 27th May 1993.
- *
- * Rewrote canonical mode and added more termios flags.
- *     -- julian@xxxxxxxxxxxxxxxxxxxxxx (J. Cowley), 13Jan94
- *
- * Reorganized FASYNC support so mouse code can share it.
- *     -- ctm@xxxxxxxx, 9Sep95
- *
- * New TIOCLINUX variants added.
- *     -- mj@xxxxxxxxxxxxxxxxx, 19-Nov-95
- * 
- * Restrict vt switching via ioctl()
- *      -- grif@xxxxxxxxxx, 5-Dec-95
- *
- * Move console and virtual terminal code to more appropriate files,
- * implement CONFIG_VT and generalize console device interface.
- *     -- Marko Kohtala <Marko.Kohtala@xxxxxx>, March 97
- *
- * Rewrote init_dev and release_dev to eliminate races.
- *     -- Bill Hawes <whawes@xxxxxxxx>, June 97
- *
- * Added devfs support.
- *      -- C. Scott Ananian <cananian@xxxxxxxxxxxxxxxxxxxx>, 13-Jan-1998
- *
- * Added support for a Unix98-style ptmx device.
- *      -- C. Scott Ananian <cananian@xxxxxxxxxxxxxxxxxxxx>, 14-Jan-1998
- *
- * Reduced memory usage for older ARM systems
- *      -- Russell King <rmk@xxxxxxxxxxxxxxxx>
- *
- * Move do_SAK() into process context.  Less stack use in devfs functions.
- * alloc_tty_struct() always uses kmalloc() -- Andrew Morton 
<andrewm@xxxxxxxxxx> 17Mar01
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/devpts_fs.h>
-#include <linux/file.h>
-#include <linux/console.h>
-#include <linux/timer.h>
-#include <linux/ctype.h>
-#include <linux/kd.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/device.h>
-#include <linux/idr.h>
-#include <linux/wait.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-#include <linux/devfs_fs_kernel.h>
-
-#include <linux/kmod.h>
-
-#undef TTY_DEBUG_HANGUP
-
-#define TTY_PARANOIA_CHECK 1
-#define CHECK_TTY_COUNT 1
-
-struct termios tty_std_termios = {     /* for the benefit of tty drivers  */
-       .c_iflag = ICRNL | IXON,
-       .c_oflag = OPOST | ONLCR,
-       .c_cflag = B38400 | CS8 | CREAD | HUPCL,
-       .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
-                  ECHOCTL | ECHOKE | IEXTEN,
-       .c_cc = INIT_C_CC
-};
-
-EXPORT_SYMBOL(tty_std_termios);
-
-/* This list gets poked at by procfs and various bits of boot up code. This
-   could do with some rationalisation such as pulling the tty proc function
-   into this file */
-   
-LIST_HEAD(tty_drivers);                        /* linked list of tty drivers */
-
-/* Semaphore to protect creating and releasing a tty. This is shared with
-   vt.c for deeply disgusting hack reasons */
-DECLARE_MUTEX(tty_sem);
-
-int console_use_vt = 1;
-
-#ifdef CONFIG_UNIX98_PTYS
-extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
-extern int pty_limit;          /* Config limit on Unix98 ptys */
-static DEFINE_IDR(allocated_ptys);
-static DECLARE_MUTEX(allocated_ptys_lock);
-static int ptmx_open(struct inode *, struct file *);
-#endif
-
-extern void disable_early_printk(void);
-
-static void initialize_tty_struct(struct tty_struct *tty);
-
-static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
-static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
-ssize_t redirected_tty_write(struct file *, const char __user *, size_t, 
loff_t *);
-static unsigned int tty_poll(struct file *, poll_table *);
-static int tty_open(struct inode *, struct file *);
-static int tty_release(struct inode *, struct file *);
-int tty_ioctl(struct inode * inode, struct file * file,
-             unsigned int cmd, unsigned long arg);
-static int tty_fasync(int fd, struct file * filp, int on);
-static void release_mem(struct tty_struct *tty, int idx);
-
-
-static struct tty_struct *alloc_tty_struct(void)
-{
-       struct tty_struct *tty;
-
-       tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
-       if (tty)
-               memset(tty, 0, sizeof(struct tty_struct));
-       return tty;
-}
-
-static void tty_buffer_free_all(struct tty_struct *);
-
-static inline void free_tty_struct(struct tty_struct *tty)
-{
-       kfree(tty->write_buf);
-       tty_buffer_free_all(tty);
-       kfree(tty);
-}
-
-#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)
-
-char *tty_name(struct tty_struct *tty, char *buf)
-{
-       if (!tty) /* Hmm.  NULL pointer.  That's fun. */
-               strcpy(buf, "NULL tty");
-       else
-               strcpy(buf, tty->name);
-       return buf;
-}
-
-EXPORT_SYMBOL(tty_name);
-
-int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
-                             const char *routine)
-{
-#ifdef TTY_PARANOIA_CHECK
-       if (!tty) {
-               printk(KERN_WARNING
-                       "null TTY for (%d:%d) in %s\n",
-                       imajor(inode), iminor(inode), routine);
-               return 1;
-       }
-       if (tty->magic != TTY_MAGIC) {
-               printk(KERN_WARNING
-                       "bad magic number for tty struct (%d:%d) in %s\n",
-                       imajor(inode), iminor(inode), routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-static int check_tty_count(struct tty_struct *tty, const char *routine)
-{
-#ifdef CHECK_TTY_COUNT
-       struct list_head *p;
-       int count = 0;
-       
-       file_list_lock();
-       list_for_each(p, &tty->tty_files) {
-               count++;
-       }
-       file_list_unlock();
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_SLAVE &&
-           tty->link && tty->link->count)
-               count++;
-       if (tty->count != count) {
-               printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
-                                   "!= #fd's(%d) in %s\n",
-                      tty->name, tty->count, count, routine);
-               return count;
-       }       
-#endif
-       return 0;
-}
-
-/*
- * Tty buffer allocation management
- */
-
-static void tty_buffer_free_all(struct tty_struct *tty)
-{
-       struct tty_buffer *thead;
-       while((thead = tty->buf.head) != NULL) {
-               tty->buf.head = thead->next;
-               kfree(thead);
-       }
-       while((thead = tty->buf.free) != NULL) {
-               tty->buf.free = thead->next;
-               kfree(thead);
-       }
-       tty->buf.tail = NULL;
-}
-
-static void tty_buffer_init(struct tty_struct *tty)
-{
-       spin_lock_init(&tty->buf.lock);
-       tty->buf.head = NULL;
-       tty->buf.tail = NULL;
-       tty->buf.free = NULL;
-}
-
-static struct tty_buffer *tty_buffer_alloc(size_t size)
-{
-       struct tty_buffer *p = kmalloc(sizeof(struct tty_buffer) + 2 * size, 
GFP_ATOMIC);
-       if(p == NULL)
-               return NULL;
-       p->used = 0;
-       p->size = size;
-       p->next = NULL;
-       p->active = 0;
-       p->commit = 0;
-       p->read = 0;
-       p->char_buf_ptr = (char *)(p->data);
-       p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
-/*     printk("Flip create %p\n", p); */
-       return p;
-}
-
-/* Must be called with the tty_read lock held. This needs to acquire strategy
-   code to decide if we should kfree or relink a given expired buffer */
-
-static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
-{
-       /* Dumb strategy for now - should keep some stats */
-/*     printk("Flip dispose %p\n", b); */
-       if(b->size >= 512)
-               kfree(b);
-       else {
-               b->next = tty->buf.free;
-               tty->buf.free = b;
-       }
-}
-
-static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
-{
-       struct tty_buffer **tbh = &tty->buf.free;
-       while((*tbh) != NULL) {
-               struct tty_buffer *t = *tbh;
-               if(t->size >= size) {
-                       *tbh = t->next;
-                       t->next = NULL;
-                       t->used = 0;
-                       t->commit = 0;
-                       t->read = 0;
-                       /* DEBUG ONLY */
-/*                     memset(t->data, '*', size); */
-/*                     printk("Flip recycle %p\n", t); */
-                       return t;
-               }
-               tbh = &((*tbh)->next);
-       }
-       /* Round the buffer size out */
-       size = (size + 0xFF) & ~ 0xFF;
-       return tty_buffer_alloc(size);
-       /* Should possibly check if this fails for the largest buffer we
-          have queued and recycle that ? */
-}
-
-int tty_buffer_request_room(struct tty_struct *tty, size_t size)
-{
-       struct tty_buffer *b, *n;
-       int left;
-       unsigned long flags;
-
-       spin_lock_irqsave(&tty->buf.lock, flags);
-
-       /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
-          remove this conditional if its worth it. This would be invisible
-          to the callers */
-       if ((b = tty->buf.tail) != NULL) {
-               left = b->size - b->used;
-               b->active = 1;
-       } else
-               left = 0;
-
-       if (left < size) {
-               /* This is the slow path - looking for new buffers to use */
-               if ((n = tty_buffer_find(tty, size)) != NULL) {
-                       if (b != NULL) {
-                               b->next = n;
-                               b->active = 0;
-                               b->commit = b->used;
-                       } else
-                               tty->buf.head = n;
-                       tty->buf.tail = n;
-                       n->active = 1;
-               } else
-                       size = left;
-       }
-
-       spin_unlock_irqrestore(&tty->buf.lock, flags);
-       return size;
-}
-
-EXPORT_SYMBOL_GPL(tty_buffer_request_room);
-
-int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, 
size_t size)
-{
-       int copied = 0;
-       do {
-               int space = tty_buffer_request_room(tty, size - copied);
-               struct tty_buffer *tb = tty->buf.tail;
-               /* If there is no space then tb may be NULL */
-               if(unlikely(space == 0))
-                       break;
-               memcpy(tb->char_buf_ptr + tb->used, chars, space);
-               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
-               tb->used += space;
-               copied += space;
-               chars += space;
-/*             printk("Flip insert %d.\n", space); */
-       }
-       /* There is a small chance that we need to split the data over
-          several buffers. If this is the case we must loop */
-       while (unlikely(size > copied));
-       return copied;
-}
-
-EXPORT_SYMBOL_GPL(tty_insert_flip_string);
-
-int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, 
char *flags, size_t size)
-{
-       int copied = 0;
-       do {
-               int space = tty_buffer_request_room(tty, size - copied);
-               struct tty_buffer *tb = tty->buf.tail;
-               /* If there is no space then tb may be NULL */
-               if(unlikely(space == 0))
-                       break;
-               memcpy(tb->char_buf_ptr + tb->used, chars, space);
-               memcpy(tb->flag_buf_ptr + tb->used, flags, space);
-               tb->used += space;
-               copied += space;
-               chars += space;
-               flags += space;
-       }
-       /* There is a small chance that we need to split the data over
-          several buffers. If this is the case we must loop */
-       while (unlikely(size > copied));
-       return copied;
-}
-
-EXPORT_SYMBOL_GPL(tty_insert_flip_string_flags);
-
-
-/*
- *     Prepare a block of space in the buffer for data. Returns the length
- *     available and buffer pointer to the space which is now allocated and
- *     accounted for as ready for normal characters. This is used for drivers
- *     that need their own block copy routines into the buffer. There is no
- *     guarantee the buffer is a DMA target!
- */
-
-int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, 
size_t size)
-{
-       int space = tty_buffer_request_roo