All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] PV framebuffer
@ 2006-11-30 18:29 Markus Armbruster
  2006-12-01 13:25 ` Steven Smith
  0 siblings, 1 reply; 10+ messages in thread
From: Markus Armbruster @ 2006-11-30 18:29 UTC (permalink / raw)
  To: xen-devel

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@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
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@us.ibm.com>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ *  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@us.ibm.com>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ *  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@us.ibm.com>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ *  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@us.ibm.com>
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ *  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@dcs.warwick.ac.uk>, 27th May 1993.
- *
- * Rewrote canonical mode and added more termios flags.
- * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
- *
- * Reorganized FASYNC support so mouse code can share it.
- *	-- ctm@ardi.com, 9Sep95
- *
- * New TIOCLINUX variants added.
- *	-- mj@k332.feld.cvut.cz, 19-Nov-95
- * 
- * Restrict vt switching via ioctl()
- *      -- grif@cs.ucr.edu, 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@hut.fi>, March 97
- *
- * Rewrote init_dev and release_dev to eliminate races.
- *	-- Bill Hawes <whawes@star.net>, June 97
- *
- * Added devfs support.
- *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
- *
- * Added support for a Unix98-style ptmx device.
- *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
- *
- * Reduced memory usage for older ARM systems
- *      -- Russell King <rmk@arm.linux.org.uk>
- *
- * Move do_SAK() into process context.  Less stack use in devfs functions.
- * alloc_tty_struct() always uses kmalloc() -- Andrew Morton <andrewm@uow.edu.eu> 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_room(tty, size);
-	if (likely(space)) {
-		struct tty_buffer *tb = tty->buf.tail;
-		*chars = tb->char_buf_ptr + tb->used;
-		memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
-		tb->used += space;
-	}
-	return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
-
-/*
- *	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 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_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size)
-{
-	int space = tty_buffer_request_room(tty, size);
-	if (likely(space)) {
-		struct tty_buffer *tb = tty->buf.tail;
-		*chars = tb->char_buf_ptr + tb->used;
-		*flags = tb->flag_buf_ptr + tb->used;
-		tb->used += space;
-	}
-	return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
-
-
-
-/*
- *	This is probably overkill for real world processors but
- *	they are not on hot paths so a little discipline won't do 
- *	any harm.
- */
- 
-static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
-{
-	down(&tty->termios_sem);
-	tty->termios->c_line = num;
-	up(&tty->termios_sem);
-}
-
-/*
- *	This guards the refcounted line discipline lists. The lock
- *	must be taken with irqs off because there are hangup path
- *	callers who will do ldisc lookups and cannot sleep.
- */
- 
-static DEFINE_SPINLOCK(tty_ldisc_lock);
-static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
-static struct tty_ldisc tty_ldiscs[NR_LDISCS];	/* line disc dispatch table */
-
-int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
-{
-	unsigned long flags;
-	int ret = 0;
-	
-	if (disc < N_TTY || disc >= NR_LDISCS)
-		return -EINVAL;
-	
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	tty_ldiscs[disc] = *new_ldisc;
-	tty_ldiscs[disc].num = disc;
-	tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
-	tty_ldiscs[disc].refcount = 0;
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-	
-	return ret;
-}
-EXPORT_SYMBOL(tty_register_ldisc);
-
-int tty_unregister_ldisc(int disc)
-{
-	unsigned long flags;
-	int ret = 0;
-
-	if (disc < N_TTY || disc >= NR_LDISCS)
-		return -EINVAL;
-
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	if (tty_ldiscs[disc].refcount)
-		ret = -EBUSY;
-	else
-		tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
-	return ret;
-}
-EXPORT_SYMBOL(tty_unregister_ldisc);
-
-struct tty_ldisc *tty_ldisc_get(int disc)
-{
-	unsigned long flags;
-	struct tty_ldisc *ld;
-
-	if (disc < N_TTY || disc >= NR_LDISCS)
-		return NULL;
-	
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-
-	ld = &tty_ldiscs[disc];
-	/* Check the entry is defined */
-	if(ld->flags & LDISC_FLAG_DEFINED)
-	{
-		/* If the module is being unloaded we can't use it */
-		if (!try_module_get(ld->owner))
-		       	ld = NULL;
-		else /* lock it */
-			ld->refcount++;
-	}
-	else
-		ld = NULL;
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-	return ld;
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_get);
-
-void tty_ldisc_put(int disc)
-{
-	struct tty_ldisc *ld;
-	unsigned long flags;
-	
-	if (disc < N_TTY || disc >= NR_LDISCS)
-		BUG();
-		
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	ld = &tty_ldiscs[disc];
-	if(ld->refcount == 0)
-		BUG();
-	ld->refcount --;
-	module_put(ld->owner);
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-}
-	
-EXPORT_SYMBOL_GPL(tty_ldisc_put);
-
-static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
-{
-	tty->ldisc = *ld;
-	tty->ldisc.refcount = 0;
-}
-
-/**
- *	tty_ldisc_try		-	internal helper
- *	@tty: the tty
- *
- *	Make a single attempt to grab and bump the refcount on
- *	the tty ldisc. Return 0 on failure or 1 on success. This is
- *	used to implement both the waiting and non waiting versions
- *	of tty_ldisc_ref
- */
-
-static int tty_ldisc_try(struct tty_struct *tty)
-{
-	unsigned long flags;
-	struct tty_ldisc *ld;
-	int ret = 0;
-	
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	ld = &tty->ldisc;
-	if(test_bit(TTY_LDISC, &tty->flags))
-	{
-		ld->refcount++;
-		ret = 1;
-	}
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-	return ret;
-}
-
-/**
- *	tty_ldisc_ref_wait	-	wait for the tty ldisc
- *	@tty: tty device
- *
- *	Dereference the line discipline for the terminal and take a 
- *	reference to it. If the line discipline is in flux then 
- *	wait patiently until it changes.
- *
- *	Note: Must not be called from an IRQ/timer context. The caller
- *	must also be careful not to hold other locks that will deadlock
- *	against a discipline change, such as an existing ldisc reference
- *	(which we check for)
- */
- 
-struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
-{
-	/* wait_event is a macro */
-	wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
-	if(tty->ldisc.refcount == 0)
-		printk(KERN_ERR "tty_ldisc_ref_wait\n");
-	return &tty->ldisc;
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
-
-/**
- *	tty_ldisc_ref		-	get the tty ldisc
- *	@tty: tty device
- *
- *	Dereference the line discipline for the terminal and take a 
- *	reference to it. If the line discipline is in flux then 
- *	return NULL. Can be called from IRQ and timer functions.
- */
- 
-struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
-{
-	if(tty_ldisc_try(tty))
-		return &tty->ldisc;
-	return NULL;
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_ref);
-
-/**
- *	tty_ldisc_deref		-	free a tty ldisc reference
- *	@ld: reference to free up
- *
- *	Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
- *	be called in IRQ context.
- */
- 
-void tty_ldisc_deref(struct tty_ldisc *ld)
-{
-	unsigned long flags;
-
-	if(ld == NULL)
-		BUG();
-		
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	if(ld->refcount == 0)
-		printk(KERN_ERR "tty_ldisc_deref: no references.\n");
-	else
-		ld->refcount--;
-	if(ld->refcount == 0)
-		wake_up(&tty_ldisc_wait);
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_deref);
-
-/**
- *	tty_ldisc_enable	-	allow ldisc use
- *	@tty: terminal to activate ldisc on
- *
- *	Set the TTY_LDISC flag when the line discipline can be called
- *	again. Do neccessary wakeups for existing sleepers.
- *
- *	Note: nobody should set this bit except via this function. Clearing
- *	directly is allowed.
- */
-
-static void tty_ldisc_enable(struct tty_struct *tty)
-{
-	set_bit(TTY_LDISC, &tty->flags);
-	wake_up(&tty_ldisc_wait);
-}
-	
-/**
- *	tty_set_ldisc		-	set line discipline
- *	@tty: the terminal to set
- *	@ldisc: the line discipline
- *
- *	Set the discipline of a tty line. Must be called from a process
- *	context.
- */
- 
-static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
-{
-	int retval = 0;
-	struct tty_ldisc o_ldisc;
-	char buf[64];
-	int work;
-	unsigned long flags;
-	struct tty_ldisc *ld;
-	struct tty_struct *o_tty;
-
-	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
-		return -EINVAL;
-
-restart:
-
-	ld = tty_ldisc_get(ldisc);
-	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
-	/* Cyrus Durgin <cider@speakeasy.org> */
-	if (ld == NULL) {
-		request_module("tty-ldisc-%d", ldisc);
-		ld = tty_ldisc_get(ldisc);
-	}
-	if (ld == NULL)
-		return -EINVAL;
-
-	/*
-	 *	No more input please, we are switching. The new ldisc
-	 *	will update this value in the ldisc open function
-	 */
-
-	tty->receive_room = 0;
-
-	/*
-	 *	Problem: What do we do if this blocks ?
-	 */
-
-	tty_wait_until_sent(tty, 0);
-
-	if (tty->ldisc.num == ldisc) {
-		tty_ldisc_put(ldisc);
-		return 0;
-	}
-
-	o_ldisc = tty->ldisc;
-	o_tty = tty->link;
-
-	/*
-	 *	Make sure we don't change while someone holds a
-	 *	reference to the line discipline. The TTY_LDISC bit
-	 *	prevents anyone taking a reference once it is clear.
-	 *	We need the lock to avoid racing reference takers.
-	 */
-
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
-		if(tty->ldisc.refcount) {
-			/* Free the new ldisc we grabbed. Must drop the lock
-			   first. */
-			spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-			tty_ldisc_put(ldisc);
-			/*
-			 * There are several reasons we may be busy, including
-			 * random momentary I/O traffic. We must therefore
-			 * retry. We could distinguish between blocking ops
-			 * and retries if we made tty_ldisc_wait() smarter. That
-			 * is up for discussion.
-			 */
-			if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
-				return -ERESTARTSYS;
-			goto restart;
-		}
-		if(o_tty && o_tty->ldisc.refcount) {
-			spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-			tty_ldisc_put(ldisc);
-			if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
-				return -ERESTARTSYS;
-			goto restart;
-		}
-	}
-
-	/* if the TTY_LDISC bit is set, then we are racing against another ldisc change */
-
-	if (!test_bit(TTY_LDISC, &tty->flags)) {
-		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-		tty_ldisc_put(ldisc);
-		ld = tty_ldisc_ref_wait(tty);
-		tty_ldisc_deref(ld);
-		goto restart;
-	}
-
-	clear_bit(TTY_LDISC, &tty->flags);
-	clear_bit(TTY_DONT_FLIP, &tty->flags);
-	if (o_tty) {
-		clear_bit(TTY_LDISC, &o_tty->flags);
-		clear_bit(TTY_DONT_FLIP, &o_tty->flags);
-	}
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
-	/*
-	 *	From this point on we know nobody has an ldisc
-	 *	usage reference, nor can they obtain one until
-	 *	we say so later on.
-	 */
-
-	work = cancel_delayed_work(&tty->buf.work);
-	/*
-	 * Wait for ->hangup_work and ->buf.work handlers to terminate
-	 */
-	 
-	flush_scheduled_work();
-	/* Shutdown the current discipline. */
-	if (tty->ldisc.close)
-		(tty->ldisc.close)(tty);
-
-	/* Now set up the new line discipline. */
-	tty_ldisc_assign(tty, ld);
-	tty_set_termios_ldisc(tty, ldisc);
-	if (tty->ldisc.open)
-		retval = (tty->ldisc.open)(tty);
-	if (retval < 0) {
-		tty_ldisc_put(ldisc);
-		/* There is an outstanding reference here so this is safe */
-		tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
-		tty_set_termios_ldisc(tty, tty->ldisc.num);
-		if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
-			tty_ldisc_put(o_ldisc.num);
-			/* This driver is always present */
-			tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-			tty_set_termios_ldisc(tty, N_TTY);
-			if (tty->ldisc.open) {
-				int r = tty->ldisc.open(tty);
-
-				if (r < 0)
-					panic("Couldn't open N_TTY ldisc for "
-					      "%s --- error %d.",
-					      tty_name(tty, buf), r);
-			}
-		}
-	}
-	/* At this point we hold a reference to the new ldisc and a
-	   a reference to the old ldisc. If we ended up flipping back
-	   to the existing ldisc we have two references to it */
-	
-	if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)
-		tty->driver->set_ldisc(tty);
-		
-	tty_ldisc_put(o_ldisc.num);
-	
-	/*
-	 *	Allow ldisc referencing to occur as soon as the driver
-	 *	ldisc callback completes.
-	 */
-	 
-	tty_ldisc_enable(tty);
-	if (o_tty)
-		tty_ldisc_enable(o_tty);
-	
-	/* Restart it in case no characters kick it off. Safe if
-	   already running */
-	if (work)
-		schedule_delayed_work(&tty->buf.work, 1);
-	return retval;
-}
-
-/*
- * This routine returns a tty driver structure, given a device number
- */
-static struct tty_driver *get_tty_driver(dev_t device, int *index)
-{
-	struct tty_driver *p;
-
-	list_for_each_entry(p, &tty_drivers, tty_drivers) {
-		dev_t base = MKDEV(p->major, p->minor_start);
-		if (device < base || device >= base + p->num)
-			continue;
-		*index = device - base;
-		return p;
-	}
-	return NULL;
-}
-
-/*
- * If we try to write to, or set the state of, a terminal and we're
- * not in the foreground, send a SIGTTOU.  If the signal is blocked or
- * ignored, go ahead and perform the operation.  (POSIX 7.2)
- */
-int tty_check_change(struct tty_struct * tty)
-{
-	if (current->signal->tty != tty)
-		return 0;
-	if (tty->pgrp <= 0) {
-		printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");
-		return 0;
-	}
-	if (process_group(current) == tty->pgrp)
-		return 0;
-	if (is_ignored(SIGTTOU))
-		return 0;
-	if (is_orphaned_pgrp(process_group(current)))
-		return -EIO;
-	(void) kill_pg(process_group(current), SIGTTOU, 1);
-	return -ERESTARTSYS;
-}
-
-EXPORT_SYMBOL(tty_check_change);
-
-static ssize_t hung_up_tty_read(struct file * file, char __user * buf,
-				size_t count, loff_t *ppos)
-{
-	return 0;
-}
-
-static ssize_t hung_up_tty_write(struct file * file, const char __user * buf,
-				 size_t count, loff_t *ppos)
-{
-	return -EIO;
-}
-
-/* No kernel lock held - none needed ;) */
-static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait)
-{
-	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
-}
-
-static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
-			     unsigned int cmd, unsigned long arg)
-{
-	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
-}
-
-static struct file_operations tty_fops = {
-	.llseek		= no_llseek,
-	.read		= tty_read,
-	.write		= tty_write,
-	.poll		= tty_poll,
-	.ioctl		= tty_ioctl,
-	.open		= tty_open,
-	.release	= tty_release,
-	.fasync		= tty_fasync,
-};
-
-#ifdef CONFIG_UNIX98_PTYS
-static struct file_operations ptmx_fops = {
-	.llseek		= no_llseek,
-	.read		= tty_read,
-	.write		= tty_write,
-	.poll		= tty_poll,
-	.ioctl		= tty_ioctl,
-	.open		= ptmx_open,
-	.release	= tty_release,
-	.fasync		= tty_fasync,
-};
-#endif
-
-static struct file_operations console_fops = {
-	.llseek		= no_llseek,
-	.read		= tty_read,
-	.write		= redirected_tty_write,
-	.poll		= tty_poll,
-	.ioctl		= tty_ioctl,
-	.open		= tty_open,
-	.release	= tty_release,
-	.fasync		= tty_fasync,
-};
-
-static struct file_operations hung_up_tty_fops = {
-	.llseek		= no_llseek,
-	.read		= hung_up_tty_read,
-	.write		= hung_up_tty_write,
-	.poll		= hung_up_tty_poll,
-	.ioctl		= hung_up_tty_ioctl,
-	.release	= tty_release,
-};
-
-static DEFINE_SPINLOCK(redirect_lock);
-static struct file *redirect;
-
-/**
- *	tty_wakeup	-	request more data
- *	@tty: terminal
- *
- *	Internal and external helper for wakeups of tty. This function
- *	informs the line discipline if present that the driver is ready
- *	to receive more output data.
- */
- 
-void tty_wakeup(struct tty_struct *tty)
-{
-	struct tty_ldisc *ld;
-	
-	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
-		ld = tty_ldisc_ref(tty);
-		if(ld) {
-			if(ld->write_wakeup)
-				ld->write_wakeup(tty);
-			tty_ldisc_deref(ld);
-		}
-	}
-	wake_up_interruptible(&tty->write_wait);
-}
-
-EXPORT_SYMBOL_GPL(tty_wakeup);
-
-/**
- *	tty_ldisc_flush	-	flush line discipline queue
- *	@tty: tty
- *
- *	Flush the line discipline queue (if any) for this tty. If there
- *	is no line discipline active this is a no-op.
- */
- 
-void tty_ldisc_flush(struct tty_struct *tty)
-{
-	struct tty_ldisc *ld = tty_ldisc_ref(tty);
-	if(ld) {
-		if(ld->flush_buffer)
-			ld->flush_buffer(tty);
-		tty_ldisc_deref(ld);
-	}
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_flush);
-	
-/*
- * This can be called by the "eventd" kernel thread.  That is process synchronous,
- * but doesn't hold any locks, so we need to make sure we have the appropriate
- * locks for what we're doing..
- */
-static void do_tty_hangup(void *data)
-{
-	struct tty_struct *tty = (struct tty_struct *) data;
-	struct file * cons_filp = NULL;
-	struct file *filp, *f = NULL;
-	struct task_struct *p;
-	struct tty_ldisc *ld;
-	int    closecount = 0, n;
-
-	if (!tty)
-		return;
-
-	/* inuse_filps is protected by the single kernel lock */
-	lock_kernel();
-
-	spin_lock(&redirect_lock);
-	if (redirect && redirect->private_data == tty) {
-		f = redirect;
-		redirect = NULL;
-	}
-	spin_unlock(&redirect_lock);
-	
-	check_tty_count(tty, "do_tty_hangup");
-	file_list_lock();
-	/* This breaks for file handles being sent over AF_UNIX sockets ? */
-	list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
-		if (filp->f_op->write == redirected_tty_write)
-			cons_filp = filp;
-		if (filp->f_op->write != tty_write)
-			continue;
-		closecount++;
-		tty_fasync(-1, filp, 0);	/* can't block */
-		filp->f_op = &hung_up_tty_fops;
-	}
-	file_list_unlock();
-	
-	/* FIXME! What are the locking issues here? This may me overdoing things..
-	 * this question is especially important now that we've removed the irqlock. */
-
-	ld = tty_ldisc_ref(tty);
-	if(ld != NULL)	/* We may have no line discipline at this point */
-	{
-		if (ld->flush_buffer)
-			ld->flush_buffer(tty);
-		if (tty->driver->flush_buffer)
-			tty->driver->flush_buffer(tty);
-		if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
-		    ld->write_wakeup)
-			ld->write_wakeup(tty);
-		if (ld->hangup)
-			ld->hangup(tty);
-	}
-
-	/* FIXME: Once we trust the LDISC code better we can wait here for
-	   ldisc completion and fix the driver call race */
-	   
-	wake_up_interruptible(&tty->write_wait);
-	wake_up_interruptible(&tty->read_wait);
-
-	/*
-	 * Shutdown the current line discipline, and reset it to
-	 * N_TTY.
-	 */
-	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
-	{
-		down(&tty->termios_sem);
-		*tty->termios = tty->driver->init_termios;
-		up(&tty->termios_sem);
-	}
-	
-	/* Defer ldisc switch */
-	/* tty_deferred_ldisc_switch(N_TTY);
-	
-	  This should get done automatically when the port closes and
-	  tty_release is called */
-	
-	read_lock(&tasklist_lock);
-	if (tty->session > 0) {
-		do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-			if (p->signal->tty == tty)
-				p->signal->tty = NULL;
-			if (!p->signal->leader)
-				continue;
-			send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-			send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p);
-			if (tty->pgrp > 0)
-				p->signal->tty_old_pgrp = tty->pgrp;
-		} while_each_task_pid(tty->session, PIDTYPE_SID, p);
-	}
-	read_unlock(&tasklist_lock);
-
-	tty->flags = 0;
-	tty->session = 0;
-	tty->pgrp = -1;
-	tty->ctrl_status = 0;
-	/*
-	 *	If one of the devices matches a console pointer, we
-	 *	cannot just call hangup() because that will cause
-	 *	tty->count and state->count to go out of sync.
-	 *	So we just call close() the right number of times.
-	 */
-	if (cons_filp) {
-		if (tty->driver->close)
-			for (n = 0; n < closecount; n++)
-				tty->driver->close(tty, cons_filp);
-	} else if (tty->driver->hangup)
-		(tty->driver->hangup)(tty);
-		
-	/* We don't want to have driver/ldisc interactions beyond
-	   the ones we did here. The driver layer expects no
-	   calls after ->hangup() from the ldisc side. However we
-	   can't yet guarantee all that */
-
-	set_bit(TTY_HUPPED, &tty->flags);
-	if (ld) {
-		tty_ldisc_enable(tty);
-		tty_ldisc_deref(ld);
-	}
-	unlock_kernel();
-	if (f)
-		fput(f);
-}
-
-void tty_hangup(struct tty_struct * tty)
-{
-#ifdef TTY_DEBUG_HANGUP
-	char	buf[64];
-	
-	printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
-#endif
-	schedule_work(&tty->hangup_work);
-}
-
-EXPORT_SYMBOL(tty_hangup);
-
-void tty_vhangup(struct tty_struct * tty)
-{
-#ifdef TTY_DEBUG_HANGUP
-	char	buf[64];
-
-	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
-#endif
-	do_tty_hangup((void *) tty);
-}
-EXPORT_SYMBOL(tty_vhangup);
-
-int tty_hung_up_p(struct file * filp)
-{
-	return (filp->f_op == &hung_up_tty_fops);
-}
-
-EXPORT_SYMBOL(tty_hung_up_p);
-
-/*
- * This function is typically called only by the session leader, when
- * it wants to disassociate itself from its controlling tty.
- *
- * It performs the following functions:
- * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
- * 	(2)  Clears the tty from being controlling the session
- * 	(3)  Clears the controlling tty for all processes in the
- * 		session group.
- *
- * The argument on_exit is set to 1 if called when a process is
- * exiting; it is 0 if called by the ioctl TIOCNOTTY.
- */
-void disassociate_ctty(int on_exit)
-{
-	struct tty_struct *tty;
-	struct task_struct *p;
-	int tty_pgrp = -1;
-
-	lock_kernel();
-
-	down(&tty_sem);
-	tty = current->signal->tty;
-	if (tty) {
-		tty_pgrp = tty->pgrp;
-		up(&tty_sem);
-		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
-			tty_vhangup(tty);
-	} else {
-		if (current->signal->tty_old_pgrp) {
-			kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
-			kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
-		}
-		up(&tty_sem);
-		unlock_kernel();	
-		return;
-	}
-	if (tty_pgrp > 0) {
-		kill_pg(tty_pgrp, SIGHUP, on_exit);
-		if (!on_exit)
-			kill_pg(tty_pgrp, SIGCONT, on_exit);
-	}
-
-	/* Must lock changes to tty_old_pgrp */
-	down(&tty_sem);
-	current->signal->tty_old_pgrp = 0;
-	tty->session = 0;
-	tty->pgrp = -1;
-
-	/* Now clear signal->tty under the lock */
-	read_lock(&tasklist_lock);
-	do_each_task_pid(current->signal->session, PIDTYPE_SID, p) {
-		p->signal->tty = NULL;
-	} while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
-	read_unlock(&tasklist_lock);
-	up(&tty_sem);
-	unlock_kernel();
-}
-
-void stop_tty(struct tty_struct *tty)
-{
-	if (tty->stopped)
-		return;
-	tty->stopped = 1;
-	if (tty->link && tty->link->packet) {
-		tty->ctrl_status &= ~TIOCPKT_START;
-		tty->ctrl_status |= TIOCPKT_STOP;
-		wake_up_interruptible(&tty->link->read_wait);
-	}
-	if (tty->driver->stop)
-		(tty->driver->stop)(tty);
-}
-
-EXPORT_SYMBOL(stop_tty);
-
-void start_tty(struct tty_struct *tty)
-{
-	if (!tty->stopped || tty->flow_stopped)
-		return;
-	tty->stopped = 0;
-	if (tty->link && tty->link->packet) {
-		tty->ctrl_status &= ~TIOCPKT_STOP;
-		tty->ctrl_status |= TIOCPKT_START;
-		wake_up_interruptible(&tty->link->read_wait);
-	}
-	if (tty->driver->start)
-		(tty->driver->start)(tty);
-
-	/* If we have a running line discipline it may need kicking */
-	tty_wakeup(tty);
-	wake_up_interruptible(&tty->write_wait);
-}
-
-EXPORT_SYMBOL(start_tty);
-
-static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 
-			loff_t *ppos)
-{
-	int i;
-	struct tty_struct * tty;
-	struct inode *inode;
-	struct tty_ldisc *ld;
-
-	tty = (struct tty_struct *)file->private_data;
-	inode = file->f_dentry->d_inode;
-	if (tty_paranoia_check(tty, inode, "tty_read"))
-		return -EIO;
-	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
-		return -EIO;
-
-	/* We want to wait for the line discipline to sort out in this
-	   situation */
-	ld = tty_ldisc_ref_wait(tty);
-	lock_kernel();
-	if (ld->read)
-		i = (ld->read)(tty,file,buf,count);
-	else
-		i = -EIO;
-	tty_ldisc_deref(ld);
-	unlock_kernel();
-	if (i > 0)
-		inode->i_atime = current_fs_time(inode->i_sb);
-	return i;
-}
-
-/*
- * Split writes up in sane blocksizes to avoid
- * denial-of-service type attacks
- */
-static inline ssize_t do_tty_write(
-	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
-	struct tty_struct *tty,
-	struct file *file,
-	const char __user *buf,
-	size_t count)
-{
-	ssize_t ret = 0, written = 0;
-	unsigned int chunk;
-	
-	if (down_interruptible(&tty->atomic_write)) {
-		return -ERESTARTSYS;
-	}
-
-	/*
-	 * We chunk up writes into a temporary buffer. This
-	 * simplifies low-level drivers immensely, since they
-	 * don't have locking issues and user mode accesses.
-	 *
-	 * But if TTY_NO_WRITE_SPLIT is set, we should use a
-	 * big chunk-size..
-	 *
-	 * The default chunk-size is 2kB, because the NTTY
-	 * layer has problems with bigger chunks. It will
-	 * claim to be able to handle more characters than
-	 * it actually does.
-	 */
-	chunk = 2048;
-	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
-		chunk = 65536;
-	if (count < chunk)
-		chunk = count;
-
-	/* write_buf/write_cnt is protected by the atomic_write semaphore */
-	if (tty->write_cnt < chunk) {
-		unsigned char *buf;
-
-		if (chunk < 1024)
-			chunk = 1024;
-
-		buf = kmalloc(chunk, GFP_KERNEL);
-		if (!buf) {
-			up(&tty->atomic_write);
-			return -ENOMEM;
-		}
-		kfree(tty->write_buf);
-		tty->write_cnt = chunk;
-		tty->write_buf = buf;
-	}
-
-	/* Do the write .. */
-	for (;;) {
-		size_t size = count;
-		if (size > chunk)
-			size = chunk;
-		ret = -EFAULT;
-		if (copy_from_user(tty->write_buf, buf, size))
-			break;
-		lock_kernel();
-		ret = write(tty, file, tty->write_buf, size);
-		unlock_kernel();
-		if (ret <= 0)
-			break;
-		written += ret;
-		buf += ret;
-		count -= ret;
-		if (!count)
-			break;
-		ret = -ERESTARTSYS;
-		if (signal_pending(current))
-			break;
-		cond_resched();
-	}
-	if (written) {
-		struct inode *inode = file->f_dentry->d_inode;
-		inode->i_mtime = current_fs_time(inode->i_sb);
-		ret = written;
-	}
-	up(&tty->atomic_write);
-	return ret;
-}
-
-
-static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
-			 loff_t *ppos)
-{
-	struct tty_struct * tty;
-	struct inode *inode = file->f_dentry->d_inode;
-	ssize_t ret;
-	struct tty_ldisc *ld;
-	
-	tty = (struct tty_struct *)file->private_data;
-	if (tty_paranoia_check(tty, inode, "tty_write"))
-		return -EIO;
-	if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
-		return -EIO;
-
-	ld = tty_ldisc_ref_wait(tty);		
-	if (!ld->write)
-		ret = -EIO;
-	else
-		ret = do_tty_write(ld->write, tty, file, buf, count);
-	tty_ldisc_deref(ld);
-	return ret;
-}
-
-ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count,
-			 loff_t *ppos)
-{
-	struct file *p = NULL;
-
-	spin_lock(&redirect_lock);
-	if (redirect) {
-		get_file(redirect);
-		p = redirect;
-	}
-	spin_unlock(&redirect_lock);
-
-	if (p) {
-		ssize_t res;
-		res = vfs_write(p, buf, count, &p->f_pos);
-		fput(p);
-		return res;
-	}
-
-	return tty_write(file, buf, count, ppos);
-}
-
-static char ptychar[] = "pqrstuvwxyzabcde";
-
-static inline void pty_line_name(struct tty_driver *driver, int index, char *p)
-{
-	int i = index + driver->name_base;
-	/* ->name is initialized to "ttyp", but "tty" is expected */
-	sprintf(p, "%s%c%x",
-			driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
-			ptychar[i >> 4 & 0xf], i & 0xf);
-}
-
-static inline void tty_line_name(struct tty_driver *driver, int index, char *p)
-{
-	sprintf(p, "%s%d", driver->name, index + driver->name_base);
-}
-
-/*
- * WSH 06/09/97: Rewritten to remove races and properly clean up after a
- * failed open.  The new code protects the open with a semaphore, so it's
- * really quite straightforward.  The semaphore locking can probably be
- * relaxed for the (most common) case of reopening a tty.
- */
-static int init_dev(struct tty_driver *driver, int idx,
-	struct tty_struct **ret_tty)
-{
-	struct tty_struct *tty, *o_tty;
-	struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
-	struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
-	int retval=0;
-
-	/* check whether we're reopening an existing tty */
-	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
-		tty = devpts_get_tty(idx);
-		if (tty && driver->subtype == PTY_TYPE_MASTER)
-			tty = tty->link;
-	} else {
-		tty = driver->ttys[idx];
-	}
-	if (tty) goto fast_track;
-
-	/*
-	 * First time open is complex, especially for PTY devices.
-	 * This code guarantees that either everything succeeds and the
-	 * TTY is ready for operation, or else the table slots are vacated
-	 * and the allocated memory released.  (Except that the termios 
-	 * and locked termios may be retained.)
-	 */
-
-	if (!try_module_get(driver->owner)) {
-		retval = -ENODEV;
-		goto end_init;
-	}
-
-	o_tty = NULL;
-	tp = o_tp = NULL;
-	ltp = o_ltp = NULL;
-
-	tty = alloc_tty_struct();
-	if(!tty)
-		goto fail_no_mem;
-	initialize_tty_struct(tty);
-	tty->driver = driver;
-	tty->index = idx;
-	tty_line_name(driver, idx, tty->name);
-
-	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
-		tp_loc = &tty->termios;
-		ltp_loc = &tty->termios_locked;
-	} else {
-		tp_loc = &driver->termios[idx];
-		ltp_loc = &driver->termios_locked[idx];
-	}
-
-	if (!*tp_loc) {
-		tp = (struct termios *) kmalloc(sizeof(struct termios),
-						GFP_KERNEL);
-		if (!tp)
-			goto free_mem_out;
-		*tp = driver->init_termios;
-	}
-
-	if (!*ltp_loc) {
-		ltp = (struct termios *) kmalloc(sizeof(struct termios),
-						 GFP_KERNEL);
-		if (!ltp)
-			goto free_mem_out;
-		memset(ltp, 0, sizeof(struct termios));
-	}
-
-	if (driver->type == TTY_DRIVER_TYPE_PTY) {
-		o_tty = alloc_tty_struct();
-		if (!o_tty)
-			goto free_mem_out;
-		initialize_tty_struct(o_tty);
-		o_tty->driver = driver->other;
-		o_tty->index = idx;
-		tty_line_name(driver->other, idx, o_tty->name);
-
-		if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
-			o_tp_loc = &o_tty->termios;
-			o_ltp_loc = &o_tty->termios_locked;
-		} else {
-			o_tp_loc = &driver->other->termios[idx];
-			o_ltp_loc = &driver->other->termios_locked[idx];
-		}
-
-		if (!*o_tp_loc) {
-			o_tp = (struct termios *)
-				kmalloc(sizeof(struct termios), GFP_KERNEL);
-			if (!o_tp)
-				goto free_mem_out;
-			*o_tp = driver->other->init_termios;
-		}
-
-		if (!*o_ltp_loc) {
-			o_ltp = (struct termios *)
-				kmalloc(sizeof(struct termios), GFP_KERNEL);
-			if (!o_ltp)
-				goto free_mem_out;
-			memset(o_ltp, 0, sizeof(struct termios));
-		}
-
-		/*
-		 * Everything allocated ... set up the o_tty structure.
-		 */
-		if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {
-			driver->other->ttys[idx] = o_tty;
-		}
-		if (!*o_tp_loc)
-			*o_tp_loc = o_tp;
-		if (!*o_ltp_loc)
-			*o_ltp_loc = o_ltp;
-		o_tty->termios = *o_tp_loc;
-		o_tty->termios_locked = *o_ltp_loc;
-		driver->other->refcount++;
-		if (driver->subtype == PTY_TYPE_MASTER)
-			o_tty->count++;
-
-		/* Establish the links in both directions */
-		tty->link   = o_tty;
-		o_tty->link = tty;
-	}
-
-	/* 
-	 * All structures have been allocated, so now we install them.
-	 * Failures after this point use release_mem to clean up, so 
-	 * there's no need to null out the local pointers.
-	 */
-	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
-		driver->ttys[idx] = tty;
-	}
-	
-	if (!*tp_loc)
-		*tp_loc = tp;
-	if (!*ltp_loc)
-		*ltp_loc = ltp;
-	tty->termios = *tp_loc;
-	tty->termios_locked = *ltp_loc;
-	driver->refcount++;
-	tty->count++;
-
-	/* 
-	 * Structures all installed ... call the ldisc open routines.
-	 * If we fail here just call release_mem to clean up.  No need
-	 * to decrement the use counts, as release_mem doesn't care.
-	 */
-
-	if (tty->ldisc.open) {
-		retval = (tty->ldisc.open)(tty);
-		if (retval)
-			goto release_mem_out;
-	}
-	if (o_tty && o_tty->ldisc.open) {
-		retval = (o_tty->ldisc.open)(o_tty);
-		if (retval) {
-			if (tty->ldisc.close)
-				(tty->ldisc.close)(tty);
-			goto release_mem_out;
-		}
-		tty_ldisc_enable(o_tty);
-	}
-	tty_ldisc_enable(tty);
-	goto success;
-
-	/*
-	 * This fast open can be used if the tty is already open.
-	 * No memory is allocated, and the only failures are from
-	 * attempting to open a closing tty or attempting multiple
-	 * opens on a pty master.
-	 */
-fast_track:
-	if (test_bit(TTY_CLOSING, &tty->flags)) {
-		retval = -EIO;
-		goto end_init;
-	}
-	if (driver->type == TTY_DRIVER_TYPE_PTY &&
-	    driver->subtype == PTY_TYPE_MASTER) {
-		/*
-		 * special case for PTY masters: only one open permitted, 
-		 * and the slave side open count is incremented as well.
-		 */
-		if (tty->count) {
-			retval = -EIO;
-			goto end_init;
-		}
-		tty->link->count++;
-	}
-	tty->count++;
-	tty->driver = driver; /* N.B. why do this every time?? */
-
-	/* FIXME */
-	if(!test_bit(TTY_LDISC, &tty->flags))
-		printk(KERN_ERR "init_dev but no ldisc\n");
-success:
-	*ret_tty = tty;
-	
-	/* All paths come through here to release the semaphore */
-end_init:
-	return retval;
-
-	/* Release locally allocated memory ... nothing placed in slots */
-free_mem_out:
-	kfree(o_tp);
-	if (o_tty)
-		free_tty_struct(o_tty);
-	kfree(ltp);
-	kfree(tp);
-	free_tty_struct(tty);
-
-fail_no_mem:
-	module_put(driver->owner);
-	retval = -ENOMEM;
-	goto end_init;
-
-	/* call the tty release_mem routine to clean out this slot */
-release_mem_out:
-	printk(KERN_INFO "init_dev: ldisc open failed, "
-			 "clearing slot %d\n", idx);
-	release_mem(tty, idx);
-	goto end_init;
-}
-
-/*
- * Releases memory associated with a tty structure, and clears out the
- * driver table slots.
- */
-static void release_mem(struct tty_struct *tty, int idx)
-{
-	struct tty_struct *o_tty;
-	struct termios *tp;
-	int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
-
-	if ((o_tty = tty->link) != NULL) {
-		if (!devpts)
-			o_tty->driver->ttys[idx] = NULL;
-		if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-			tp = o_tty->termios;
-			if (!devpts)
-				o_tty->driver->termios[idx] = NULL;
-			kfree(tp);
-
-			tp = o_tty->termios_locked;
-			if (!devpts)
-				o_tty->driver->termios_locked[idx] = NULL;
-			kfree(tp);
-		}
-		o_tty->magic = 0;
-		o_tty->driver->refcount--;
-		file_list_lock();
-		list_del_init(&o_tty->tty_files);
-		file_list_unlock();
-		free_tty_struct(o_tty);
-	}
-
-	if (!devpts)
-		tty->driver->ttys[idx] = NULL;
-	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-		tp = tty->termios;
-		if (!devpts)
-			tty->driver->termios[idx] = NULL;
-		kfree(tp);
-
-		tp = tty->termios_locked;
-		if (!devpts)
-			tty->driver->termios_locked[idx] = NULL;
-		kfree(tp);
-	}
-
-	tty->magic = 0;
-	tty->driver->refcount--;
-	file_list_lock();
-	list_del_init(&tty->tty_files);
-	file_list_unlock();
-	module_put(tty->driver->owner);
-	free_tty_struct(tty);
-}
-
-/*
- * Even releasing the tty structures is a tricky business.. We have
- * to be very careful that the structures are all released at the
- * same time, as interrupts might otherwise get the wrong pointers.
- *
- * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
- * lead to double frees or releasing memory still in use.
- */
-static void release_dev(struct file * filp)
-{
-	struct tty_struct *tty, *o_tty;
-	int	pty_master, tty_closing, o_tty_closing, do_sleep;
-	int	devpts_master, devpts;
-	int	idx;
-	char	buf[64];
-	unsigned long flags;
-	
-	tty = (struct tty_struct *)filp->private_data;
-	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "release_dev"))
-		return;
-
-	check_tty_count(tty, "release_dev");
-
-	tty_fasync(-1, filp, 0);
-
-	idx = tty->index;
-	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-		      tty->driver->subtype == PTY_TYPE_MASTER);
-	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
-	devpts_master = pty_master && devpts;
-	o_tty = tty->link;
-
-#ifdef TTY_PARANOIA_CHECK
-	if (idx < 0 || idx >= tty->driver->num) {
-		printk(KERN_DEBUG "release_dev: bad idx when trying to "
-				  "free (%s)\n", tty->name);
-		return;
-	}
-	if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
-		if (tty != tty->driver->ttys[idx]) {
-			printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
-			       "for (%s)\n", idx, tty->name);
-			return;
-		}
-		if (tty->termios != tty->driver->termios[idx]) {
-			printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
-			       "for (%s)\n",
-			       idx, tty->name);
-			return;
-		}
-		if (tty->termios_locked != tty->driver->termios_locked[idx]) {
-			printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
-			       "termios_locked for (%s)\n",
-			       idx, tty->name);
-			return;
-		}
-	}
-#endif
-
-#ifdef TTY_DEBUG_HANGUP
-	printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",
-	       tty_name(tty, buf), tty->count);
-#endif
-
-#ifdef TTY_PARANOIA_CHECK
-	if (tty->driver->other &&
-	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
-		if (o_tty != tty->driver->other->ttys[idx]) {
-			printk(KERN_DEBUG "release_dev: other->table[%d] "
-					  "not o_tty for (%s)\n",
-			       idx, tty->name);
-			return;
-		}
-		if (o_tty->termios != tty->driver->other->termios[idx]) {
-			printk(KERN_DEBUG "release_dev: other->termios[%d] "
-					  "not o_termios for (%s)\n",
-			       idx, tty->name);
-			return;
-		}
-		if (o_tty->termios_locked != 
-		      tty->driver->other->termios_locked[idx]) {
-			printk(KERN_DEBUG "release_dev: other->termios_locked["
-					  "%d] not o_termios_locked for (%s)\n",
-			       idx, tty->name);
-			return;
-		}
-		if (o_tty->link != tty) {
-			printk(KERN_DEBUG "release_dev: bad pty pointers\n");
-			return;
-		}
-	}
-#endif
-	if (tty->driver->close)
-		tty->driver->close(tty, filp);
-
-	/*
-	 * Sanity check: if tty->count is going to zero, there shouldn't be
-	 * any waiters on tty->read_wait or tty->write_wait.  We test the
-	 * wait queues and kick everyone out _before_ actually starting to
-	 * close.  This ensures that we won't block while releasing the tty
-	 * structure.
-	 *
-	 * The test for the o_tty closing is necessary, since the master and
-	 * slave sides may close in any order.  If the slave side closes out
-	 * first, its count will be one, since the master side holds an open.
-	 * Thus this test wouldn't be triggered at the time the slave closes,
-	 * so we do it now.
-	 *
-	 * Note that it's possible for the tty to be opened again while we're
-	 * flushing out waiters.  By recalculating the closing flags before
-	 * each iteration we avoid any problems.
-	 */
-	while (1) {
-		/* Guard against races with tty->count changes elsewhere and
-		   opens on /dev/tty */
-		   
-		down(&tty_sem);
-		tty_closing = tty->count <= 1;
-		o_tty_closing = o_tty &&
-			(o_tty->count <= (pty_master ? 1 : 0));
-		do_sleep = 0;
-
-		if (tty_closing) {
-			if (waitqueue_active(&tty->read_wait)) {
-				wake_up(&tty->read_wait);
-				do_sleep++;
-			}
-			if (waitqueue_active(&tty->write_wait)) {
-				wake_up(&tty->write_wait);
-				do_sleep++;
-			}
-		}
-		if (o_tty_closing) {
-			if (waitqueue_active(&o_tty->read_wait)) {
-				wake_up(&o_tty->read_wait);
-				do_sleep++;
-			}
-			if (waitqueue_active(&o_tty->write_wait)) {
-				wake_up(&o_tty->write_wait);
-				do_sleep++;
-			}
-		}
-		if (!do_sleep)
-			break;
-
-		printk(KERN_WARNING "release_dev: %s: read/write wait queue "
-				    "active!\n", tty_name(tty, buf));
-		up(&tty_sem);
-		schedule();
-	}	
-
-	/*
-	 * The closing flags are now consistent with the open counts on 
-	 * both sides, and we've completed the last operation that could 
-	 * block, so it's safe to proceed with closing.
-	 */
-	if (pty_master) {
-		if (--o_tty->count < 0) {
-			printk(KERN_WARNING "release_dev: bad pty slave count "
-					    "(%d) for %s\n",
-			       o_tty->count, tty_name(o_tty, buf));
-			o_tty->count = 0;
-		}
-	}
-	if (--tty->count < 0) {
-		printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",
-		       tty->count, tty_name(tty, buf));
-		tty->count = 0;
-	}
-	
-	/*
-	 * We've decremented tty->count, so we need to remove this file
-	 * descriptor off the tty->tty_files list; this serves two
-	 * purposes:
-	 *  - check_tty_count sees the correct number of file descriptors
-	 *    associated with this tty.
-	 *  - do_tty_hangup no longer sees this file descriptor as
-	 *    something that needs to be handled for hangups.
-	 */
-	file_kill(filp);
-	filp->private_data = NULL;
-
-	/*
-	 * Perform some housekeeping before deciding whether to return.
-	 *
-	 * Set the TTY_CLOSING flag if this was the last open.  In the
-	 * case of a pty we may have to wait around for the other side
-	 * to close, and TTY_CLOSING makes sure we can't be reopened.
-	 */
-	if(tty_closing)
-		set_bit(TTY_CLOSING, &tty->flags);
-	if(o_tty_closing)
-		set_bit(TTY_CLOSING, &o_tty->flags);
-
-	/*
-	 * If _either_ side is closing, make sure there aren't any
-	 * processes that still think tty or o_tty is their controlling
-	 * tty.
-	 */
-	if (tty_closing || o_tty_closing) {
-		struct task_struct *p;
-
-		read_lock(&tasklist_lock);
-		do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-			p->signal->tty = NULL;
-		} while_each_task_pid(tty->session, PIDTYPE_SID, p);
-		if (o_tty)
-			do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {
-				p->signal->tty = NULL;
-			} while_each_task_pid(o_tty->session, PIDTYPE_SID, p);
-		read_unlock(&tasklist_lock);
-	}
-
-	up(&tty_sem);
-
-	/* check whether both sides are closing ... */
-	if (!tty_closing || (o_tty && !o_tty_closing))
-		return;
-	
-#ifdef TTY_DEBUG_HANGUP
-	printk(KERN_DEBUG "freeing tty structure...");
-#endif
-	/*
-	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
-	 * kill any delayed work. As this is the final close it does not
-	 * race with the set_ldisc code path.
-	 */
-	clear_bit(TTY_LDISC, &tty->flags);
-	clear_bit(TTY_DONT_FLIP, &tty->flags);
-	cancel_delayed_work(&tty->buf.work);
-
-	/*
-	 * Wait for ->hangup_work and ->buf.work handlers to terminate
-	 */
-	 
-	flush_scheduled_work();
-	
-	/*
-	 * Wait for any short term users (we know they are just driver
-	 * side waiters as the file is closing so user count on the file
-	 * side is zero.
-	 */
-	spin_lock_irqsave(&tty_ldisc_lock, flags);
-	while(tty->ldisc.refcount)
-	{
-		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-		wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
-		spin_lock_irqsave(&tty_ldisc_lock, flags);
-	}
-	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-	/*
-	 * Shutdown the current line discipline, and reset it to N_TTY.
-	 * N.B. why reset ldisc when we're releasing the memory??
-	 *
-	 * FIXME: this MUST get fixed for the new reflocking
-	 */
-	if (tty->ldisc.close)
-		(tty->ldisc.close)(tty);
-	tty_ldisc_put(tty->ldisc.num);
-	
-	/*
-	 *	Switch the line discipline back
-	 */
-	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-	tty_set_termios_ldisc(tty,N_TTY); 
-	if (o_tty) {
-		/* FIXME: could o_tty be in setldisc here ? */
-		clear_bit(TTY_LDISC, &o_tty->flags);
-		if (o_tty->ldisc.close)
-			(o_tty->ldisc.close)(o_tty);
-		tty_ldisc_put(o_tty->ldisc.num);
-		tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
-		tty_set_termios_ldisc(o_tty,N_TTY); 
-	}
-	/*
-	 * The release_mem function takes care of the details of clearing
-	 * the slots and preserving the termios structure.
-	 */
-	release_mem(tty, idx);
-
-#ifdef CONFIG_UNIX98_PTYS
-	/* Make this pty number available for reallocation */
-	if (devpts) {
-		down(&allocated_ptys_lock);
-		idr_remove(&allocated_ptys, idx);
-		up(&allocated_ptys_lock);
-	}
-#endif
-
-}
-
-/*
- * tty_open and tty_release keep up the tty count that contains the
- * number of opens done on a tty. We cannot use the inode-count, as
- * different inodes might point to the same tty.
- *
- * Open-counting is needed for pty masters, as well as for keeping
- * track of serial lines: DTR is dropped when the last close happens.
- * (This is not done solely through tty->count, now.  - Ted 1/27/92)
- *
- * The termios state of a pty is reset on first open so that
- * settings don't persist across reuse.
- */
-static int tty_open(struct inode * inode, struct file * filp)
-{
-	struct tty_struct *tty;
-	int noctty, retval;
-	struct tty_driver *driver;
-	int index;
-	dev_t device = inode->i_rdev;
-	unsigned short saved_flags = filp->f_flags;
-
-	nonseekable_open(inode, filp);
-	
-retry_open:
-	noctty = filp->f_flags & O_NOCTTY;
-	index  = -1;
-	retval = 0;
-	
-	down(&tty_sem);
-
-	if (device == MKDEV(TTYAUX_MAJOR,0)) {
-		if (!current->signal->tty) {
-			up(&tty_sem);
-			return -ENXIO;
-		}
-		driver = current->signal->tty->driver;
-		index = current->signal->tty->index;
-		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
-		/* noctty = 1; */
-		goto got_driver;
-	}
-#ifdef CONFIG_VT
-	if (console_use_vt && (device == MKDEV(TTY_MAJOR,0))) {
-		extern struct tty_driver *console_driver;
-		driver = console_driver;
-		index = fg_console;
-		noctty = 1;
-		goto got_driver;
-	}
-#endif
-	if (device == MKDEV(TTYAUX_MAJOR,1)) {
-		driver = console_device(&index);
-		if (driver) {
-			/* Don't let /dev/console block */
-			filp->f_flags |= O_NONBLOCK;
-			noctty = 1;
-			goto got_driver;
-		}
-		up(&tty_sem);
-		return -ENODEV;
-	}
-
-	driver = get_tty_driver(device, &index);
-	if (!driver) {
-		up(&tty_sem);
-		return -ENODEV;
-	}
-got_driver:
-	retval = init_dev(driver, index, &tty);
-	up(&tty_sem);
-	if (retval)
-		return retval;
-
-	filp->private_data = tty;
-	file_move(filp, &tty->tty_files);
-	check_tty_count(tty, "tty_open");
-	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-	    tty->driver->subtype == PTY_TYPE_MASTER)
-		noctty = 1;
-#ifdef TTY_DEBUG_HANGUP
-	printk(KERN_DEBUG "opening %s...", tty->name);
-#endif
-	if (!retval) {
-		if (tty->driver->open)
-			retval = tty->driver->open(tty, filp);
-		else
-			retval = -ENODEV;
-	}
-	filp->f_flags = saved_flags;
-
-	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
-		retval = -EBUSY;
-
-	if (retval) {
-#ifdef TTY_DEBUG_HANGUP
-		printk(KERN_DEBUG "error %d in opening %s...", retval,
-		       tty->name);
-#endif
-		release_dev(filp);
-		if (retval != -ERESTARTSYS)
-			return retval;
-		if (signal_pending(current))
-			return retval;
-		schedule();
-		/*
-		 * Need to reset f_op in case a hangup happened.
-		 */
-		if (filp->f_op == &hung_up_tty_fops)
-			filp->f_op = &tty_fops;
-		goto retry_open;
-	}
-	if (!noctty &&
-	    current->signal->leader &&
-	    !current->signal->tty &&
-	    tty->session == 0) {
-	    	task_lock(current);
-		current->signal->tty = tty;
-		task_unlock(current);
-		current->signal->tty_old_pgrp = 0;
-		tty->session = current->signal->session;
-		tty->pgrp = process_group(current);
-	}
-	return 0;
-}
-
-#ifdef CONFIG_UNIX98_PTYS
-static int ptmx_open(struct inode * inode, struct file * filp)
-{
-	struct tty_struct *tty;
-	int retval;
-	int index;
-	int idr_ret;
-
-	nonseekable_open(inode, filp);
-
-	/* find a device that is not in use. */
-	down(&allocated_ptys_lock);
-	if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
-		up(&allocated_ptys_lock);
-		return -ENOMEM;
-	}
-	idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
-	if (idr_ret < 0) {
-		up(&allocated_ptys_lock);
-		if (idr_ret == -EAGAIN)
-			return -ENOMEM;
-		return -EIO;
-	}
-	if (index >= pty_limit) {
-		idr_remove(&allocated_ptys, index);
-		up(&allocated_ptys_lock);
-		return -EIO;
-	}
-	up(&allocated_ptys_lock);
-
-	down(&tty_sem);
-	retval = init_dev(ptm_driver, index, &tty);
-	up(&tty_sem);
-	
-	if (retval)
-		goto out;
-
-	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
-	filp->private_data = tty;
-	file_move(filp, &tty->tty_files);
-
-	retval = -ENOMEM;
-	if (devpts_pty_new(tty->link))
-		goto out1;
-
-	check_tty_count(tty, "tty_open");
-	retval = ptm_driver->open(tty, filp);
-	if (!retval)
-		return 0;
-out1:
-	release_dev(filp);
-out:
-	down(&allocated_ptys_lock);
-	idr_remove(&allocated_ptys, index);
-	up(&allocated_ptys_lock);
-	return retval;
-}
-#endif
-
-static int tty_release(struct inode * inode, struct file * filp)
-{
-	lock_kernel();
-	release_dev(filp);
-	unlock_kernel();
-	return 0;
-}
-
-/* No kernel lock held - fine */
-static unsigned int tty_poll(struct file * filp, poll_table * wait)
-{
-	struct tty_struct * tty;
-	struct tty_ldisc *ld;
-	int ret = 0;
-
-	tty = (struct tty_struct *)filp->private_data;
-	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll"))
-		return 0;
-		
-	ld = tty_ldisc_ref_wait(tty);
-	if (ld->poll)
-		ret = (ld->poll)(tty, filp, wait);
-	tty_ldisc_deref(ld);
-	return ret;
-}
-
-static int tty_fasync(int fd, struct file * filp, int on)
-{
-	struct tty_struct * tty;
-	int retval;
-
-	tty = (struct tty_struct *)filp->private_data;
-	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_fasync"))
-		return 0;
-	
-	retval = fasync_helper(fd, filp, on, &tty->fasync);
-	if (retval <= 0)
-		return retval;
-
-	if (on) {
-		if (!waitqueue_active(&tty->read_wait))
-			tty->minimum_to_wake = 1;
-		retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0);
-		if (retval)
-			return retval;
-	} else {
-		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
-			tty->minimum_to_wake = N_TTY_BUF_SIZE;
-	}
-	return 0;
-}
-
-static int tiocsti(struct tty_struct *tty, char __user *p)
-{
-	char ch, mbz = 0;
-	struct tty_ldisc *ld;
-	
-	if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
-		return -EPERM;
-	if (get_user(ch, p))
-		return -EFAULT;
-	ld = tty_ldisc_ref_wait(tty);
-	ld->receive_buf(tty, &ch, &mbz, 1);
-	tty_ldisc_deref(ld);
-	return 0;
-}
-
-static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
-{
-	if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
-		return -EFAULT;
-	return 0;
-}
-
-static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
-	struct winsize __user * arg)
-{
-	struct winsize tmp_ws;
-
-	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
-		return -EFAULT;
-	if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
-		return 0;
-#ifdef CONFIG_VT
-	if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
-		int rc;
-
-		acquire_console_sem();
-		rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row);
-		release_console_sem();
-		if (rc)
-			return -ENXIO;
-	}
-#endif
-	if (tty->pgrp > 0)
-		kill_pg(tty->pgrp, SIGWINCH, 1);
-	if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))
-		kill_pg(real_tty->pgrp, SIGWINCH, 1);
-	tty->winsize = tmp_ws;
-	real_tty->winsize = tmp_ws;
-	return 0;
-}
-
-static int tioccons(struct file *file)
-{
-	if (!capable(CAP_SYS_ADMIN))
-		return -EPERM;
-	if (file->f_op->write == redirected_tty_write) {
-		struct file *f;
-		spin_lock(&redirect_lock);
-		f = redirect;
-		redirect = NULL;
-		spin_unlock(&redirect_lock);
-		if (f)
-			fput(f);
-		return 0;
-	}
-	spin_lock(&redirect_lock);
-	if (redirect) {
-		spin_unlock(&redirect_lock);
-		return -EBUSY;
-	}
-	get_file(file);
-	redirect = file;
-	spin_unlock(&redirect_lock);
-	return 0;
-}
-
-
-static int fionbio(struct file *file, int __user *p)
-{
-	int nonblock;
-
-	if (get_user(nonblock, p))
-		return -EFAULT;
-
-	if (nonblock)
-		file->f_flags |= O_NONBLOCK;
-	else
-		file->f_flags &= ~O_NONBLOCK;
-	return 0;
-}
-
-static int tiocsctty(struct tty_struct *tty, int arg)
-{
-	task_t *p;
-
-	if (current->signal->leader &&
-	    (current->signal->session == tty->session))
-		return 0;
-	/*
-	 * The process must be a session leader and
-	 * not have a controlling tty already.
-	 */
-	if (!current->signal->leader || current->signal->tty)
-		return -EPERM;
-	if (tty->session > 0) {
-		/*
-		 * This tty is already the controlling
-		 * tty for another session group!
-		 */
-		if ((arg == 1) && capable(CAP_SYS_ADMIN)) {
-			/*
-			 * Steal it away
-			 */
-
-			read_lock(&tasklist_lock);
-			do_each_task_pid(tty->session, PIDTYPE_SID, p) {
-				p->signal->tty = NULL;
-			} while_each_task_pid(tty->session, PIDTYPE_SID, p);
-			read_unlock(&tasklist_lock);
-		} else
-			return -EPERM;
-	}
-	task_lock(current);
-	current->signal->tty = tty;
-	task_unlock(current);
-	current->signal->tty_old_pgrp = 0;
-	tty->session = current->signal->session;
-	tty->pgrp = process_group(current);
-	return 0;
-}
-
-static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
-{
-	/*
-	 * (tty == real_tty) is a cheap way of
-	 * testing if the tty is NOT a master pty.
-	 */
-	if (tty == real_tty && current->signal->tty != real_tty)
-		return -ENOTTY;
-	return put_user(real_tty->pgrp, p);
-}
-
-static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
-{
-	pid_t pgrp;
-	int retval = tty_check_change(real_tty);
-
-	if (retval == -EIO)
-		return -ENOTTY;
-	if (retval)
-		return retval;
-	if (!current->signal->tty ||
-	    (current->signal->tty != real_tty) ||
-	    (real_tty->session != current->signal->session))
-		return -ENOTTY;
-	if (get_user(pgrp, p))
-		return -EFAULT;
-	if (pgrp < 0)
-		return -EINVAL;
-	if (session_of_pgrp(pgrp) != current->signal->session)
-		return -EPERM;
-	real_tty->pgrp = pgrp;
-	return 0;
-}
-
-static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
-{
-	/*
-	 * (tty == real_tty) is a cheap way of
-	 * testing if the tty is NOT a master pty.
-	*/
-	if (tty == real_tty && current->signal->tty != real_tty)
-		return -ENOTTY;
-	if (real_tty->session <= 0)
-		return -ENOTTY;
-	return put_user(real_tty->session, p);
-}
-
-static int tiocsetd(struct tty_struct *tty, int __user *p)
-{
-	int ldisc;
-
-	if (get_user(ldisc, p))
-		return -EFAULT;
-	return tty_set_ldisc(tty, ldisc);
-}
-
-static int send_break(struct tty_struct *tty, unsigned int duration)
-{
-	tty->driver->break_ctl(tty, -1);
-	if (!signal_pending(current)) {
-		msleep_interruptible(duration);
-	}
-	tty->driver->break_ctl(tty, 0);
-	if (signal_pending(current))
-		return -EINTR;
-	return 0;
-}
-
-static int
-tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
-{
-	int retval = -EINVAL;
-
-	if (tty->driver->tiocmget) {
-		retval = tty->driver->tiocmget(tty, file);
-
-		if (retval >= 0)
-			retval = put_user(retval, p);
-	}
-	return retval;
-}
-
-static int
-tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
-	     unsigned __user *p)
-{
-	int retval = -EINVAL;
-
-	if (tty->driver->tiocmset) {
-		unsigned int set, clear, val;
-
-		retval = get_user(val, p);
-		if (retval)
-			return retval;
-
-		set = clear = 0;
-		switch (cmd) {
-		case TIOCMBIS:
-			set = val;
-			break;
-		case TIOCMBIC:
-			clear = val;
-			break;
-		case TIOCMSET:
-			set = val;
-			clear = ~val;
-			break;
-		}
-
-		set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
-		clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
-
-		retval = tty->driver->tiocmset(tty, file, set, clear);
-	}
-	return retval;
-}
-
-/*
- * Split this up, as gcc can choke on it otherwise..
- */
-int tty_ioctl(struct inode * inode, struct file * file,
-	      unsigned int cmd, unsigned long arg)
-{
-	struct tty_struct *tty, *real_tty;
-	void __user *p = (void __user *)arg;
-	int retval;
-	struct tty_ldisc *ld;
-	
-	tty = (struct tty_struct *)file->private_data;
-	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
-		return -EINVAL;
-
-	real_tty = tty;
-	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-	    tty->driver->subtype == PTY_TYPE_MASTER)
-		real_tty = tty->link;
-
-	/*
-	 * Break handling by driver
-	 */
-	if (!tty->driver->break_ctl) {
-		switch(cmd) {
-		case TIOCSBRK:
-		case TIOCCBRK:
-			if (tty->driver->ioctl)
-				return tty->driver->ioctl(tty, file, cmd, arg);
-			return -EINVAL;
-			
-		/* These two ioctl's always return success; even if */
-		/* the driver doesn't support them. */
-		case TCSBRK:
-		case TCSBRKP:
-			if (!tty->driver->ioctl)
-				return 0;
-			retval = tty->driver->ioctl(tty, file, cmd, arg);
-			if (retval == -ENOIOCTLCMD)
-				retval = 0;
-			return retval;
-		}
-	}
-
-	/*
-	 * Factor out some common prep work
-	 */
-	switch (cmd) {
-	case TIOCSETD:
-	case TIOCSBRK:
-	case TIOCCBRK:
-	case TCSBRK:
-	case TCSBRKP:			
-		retval = tty_check_change(tty);
-		if (retval)
-			return retval;
-		if (cmd != TIOCCBRK) {
-			tty_wait_until_sent(tty, 0);
-			if (signal_pending(current))
-				return -EINTR;
-		}
-		break;
-	}
-
-	switch (cmd) {
-		case TIOCSTI:
-			return tiocsti(tty, p);
-		case TIOCGWINSZ:
-			return tiocgwinsz(tty, p);
-		case TIOCSWINSZ:
-			return tiocswinsz(tty, real_tty, p);
-		case TIOCCONS:
-			return real_tty!=tty ? -EINVAL : tioccons(file);
-		case FIONBIO:
-			return fionbio(file, p);
-		case TIOCEXCL:
-			set_bit(TTY_EXCLUSIVE, &tty->flags);
-			return 0;
-		case TIOCNXCL:
-			clear_bit(TTY_EXCLUSIVE, &tty->flags);
-			return 0;
-		case TIOCNOTTY:
-			if (current->signal->tty != tty)
-				return -ENOTTY;
-			if (current->signal->leader)
-				disassociate_ctty(0);
-			task_lock(current);
-			current->signal->tty = NULL;
-			task_unlock(current);
-			return 0;
-		case TIOCSCTTY:
-			return tiocsctty(tty, arg);
-		case TIOCGPGRP:
-			return tiocgpgrp(tty, real_tty, p);
-		case TIOCSPGRP:
-			return tiocspgrp(tty, real_tty, p);
-		case TIOCGSID:
-			return tiocgsid(tty, real_tty, p);
-		case TIOCGETD:
-			/* FIXME: check this is ok */
-			return put_user(tty->ldisc.num, (int __user *)p);
-		case TIOCSETD:
-			return tiocsetd(tty, p);
-#ifdef CONFIG_VT
-		case TIOCLINUX:
-			return tioclinux(tty, arg);
-#endif
-		/*
-		 * Break handling
-		 */
-		case TIOCSBRK:	/* Turn break on, unconditionally */
-			tty->driver->break_ctl(tty, -1);
-			return 0;
-			
-		case TIOCCBRK:	/* Turn break off, unconditionally */
-			tty->driver->break_ctl(tty, 0);
-			return 0;
-		case TCSBRK:   /* SVID version: non-zero arg --> no break */
-			/*
-			 * XXX is the above comment correct, or the
-			 * code below correct?  Is this ioctl used at
-			 * all by anyone?
-			 */
-			if (!arg)
-				return send_break(tty, 250);
-			return 0;
-		case TCSBRKP:	/* support for POSIX tcsendbreak() */	
-			return send_break(tty, arg ? arg*100 : 250);
-
-		case TIOCMGET:
-			return tty_tiocmget(tty, file, p);
-
-		case TIOCMSET:
-		case TIOCMBIC:
-		case TIOCMBIS:
-			return tty_tiocmset(tty, file, cmd, p);
-	}
-	if (tty->driver->ioctl) {
-		retval = (tty->driver->ioctl)(tty, file, cmd, arg);
-		if (retval != -ENOIOCTLCMD)
-			return retval;
-	}
-	ld = tty_ldisc_ref_wait(tty);
-	retval = -EINVAL;
-	if (ld->ioctl) {
-		retval = ld->ioctl(tty, file, cmd, arg);
-		if (retval == -ENOIOCTLCMD)
-			retval = -EINVAL;
-	}
-	tty_ldisc_deref(ld);
-	return retval;
-}
-
-
-/*
- * This implements the "Secure Attention Key" ---  the idea is to
- * prevent trojan horses by killing all processes associated with this
- * tty when the user hits the "Secure Attention Key".  Required for
- * super-paranoid applications --- see the Orange Book for more details.
- * 
- * This code could be nicer; ideally it should send a HUP, wait a few
- * seconds, then send a INT, and then a KILL signal.  But you then
- * have to coordinate with the init process, since all processes associated
- * with the current tty must be dead before the new getty is allowed
- * to spawn.
- *
- * Now, if it would be correct ;-/ The current code has a nasty hole -
- * it doesn't catch files in flight. We may send the descriptor to ourselves
- * via AF_UNIX socket, close it and later fetch from socket. FIXME.
- *
- * Nasty bug: do_SAK is being called in interrupt context.  This can
- * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
- */
-static void __do_SAK(void *arg)
-{
-#ifdef TTY_SOFT_SAK
-	tty_hangup(tty);
-#else
-	struct tty_struct *tty = arg;
-	struct task_struct *p;
-	int session;
-	int		i;
-	struct file	*filp;
-	struct tty_ldisc *disc;
-	struct fdtable *fdt;
-	
-	if (!tty)
-		return;
-	session  = tty->session;
-	
-	/* We don't want an ldisc switch during this */
-	disc = tty_ldisc_ref(tty);
-	if (disc && disc->flush_buffer)
-		disc->flush_buffer(tty);
-	tty_ldisc_deref(disc);
-
-	if (tty->driver->flush_buffer)
-		tty->driver->flush_buffer(tty);
-	
-	read_lock(&tasklist_lock);
-	do_each_task_pid(session, PIDTYPE_SID, p) {
-		if (p->signal->tty == tty || session > 0) {
-			printk(KERN_NOTICE "SAK: killed process %d"
-			    " (%s): p->signal->session==tty->session\n",
-			    p->pid, p->comm);
-			send_sig(SIGKILL, p, 1);
-			continue;
-		}
-		task_lock(p);
-		if (p->files) {
-			/*
-			 * We don't take a ref to the file, so we must
-			 * hold ->file_lock instead.
-			 */
-			spin_lock(&p->files->file_lock);
-			fdt = files_fdtable(p->files);
-			for (i=0; i < fdt->max_fds; i++) {
-				filp = fcheck_files(p->files, i);
-				if (!filp)
-					continue;
-				if (filp->f_op->read == tty_read &&
-				    filp->private_data == tty) {
-					printk(KERN_NOTICE "SAK: killed process %d"
-					    " (%s): fd#%d opened to the tty\n",
-					    p->pid, p->comm, i);
-					send_sig(SIGKILL, p, 1);
-					break;
-				}
-			}
-			spin_unlock(&p->files->file_lock);
-		}
-		task_unlock(p);
-	} while_each_task_pid(session, PIDTYPE_SID, p);
-	read_unlock(&tasklist_lock);
-#endif
-}
-
-/*
- * The tq handling here is a little racy - tty->SAK_work may already be queued.
- * Fortunately we don't need to worry, because if ->SAK_work is already queued,
- * the values which we write to it will be identical to the values which it
- * already has. --akpm
- */
-void do_SAK(struct tty_struct *tty)
-{
-	if (!tty)
-		return;
-	PREPARE_WORK(&tty->SAK_work, __do_SAK, tty);
-	schedule_work(&tty->SAK_work);
-}
-
-EXPORT_SYMBOL(do_SAK);
-
-/*
- * This routine is called out of the software interrupt to flush data
- * from the buffer chain to the line discipline.
- */
- 
-static void flush_to_ldisc(void *private_)
-{
-	struct tty_struct *tty = (struct tty_struct *) private_;
-	unsigned long 	flags;
-	struct tty_ldisc *disc;
-	struct tty_buffer *tbuf, *head;
-	int count;
-	char *char_buf;
-	unsigned char *flag_buf;
-
-	disc = tty_ldisc_ref(tty);
-	if (disc == NULL)	/*  !TTY_LDISC */
-		return;
-
-	if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
-		/*
-		 * Do it after the next timer tick:
-		 */
-		schedule_delayed_work(&tty->buf.work, 1);
-		goto out;
-	}
-	spin_lock_irqsave(&tty->buf.lock, flags);
-	head = tty->buf.head;
-	tty->buf.head = NULL;
-	while((tbuf = head) != NULL) {
-		while ((count = tbuf->commit - tbuf->read) != 0) {
-			char_buf = tbuf->char_buf_ptr + tbuf->read;
-			flag_buf = tbuf->flag_buf_ptr + tbuf->read;
-			tbuf->read += count;
-			spin_unlock_irqrestore(&tty->buf.lock, flags);
-			disc->receive_buf(tty, char_buf, flag_buf, count);
-			spin_lock_irqsave(&tty->buf.lock, flags);
-		}
-		if (tbuf->active) {
-			tty->buf.head = head;
-			break;
-		}
-		head = tbuf->next;
-		if (head == NULL)
-			tty->buf.tail = NULL;
-		tty_buffer_free(tty, tbuf);
-	}
-	spin_unlock_irqrestore(&tty->buf.lock, flags);
-out:
-	tty_ldisc_deref(disc);
-}
-
-/*
- * Routine which returns the baud rate of the tty
- *
- * Note that the baud_table needs to be kept in sync with the
- * include/asm/termbits.h file.
- */
-static int baud_table[] = {
-	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400, 460800,
-#ifdef __sparc__
-	76800, 153600, 307200, 614400, 921600
-#else
-	500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
-	2500000, 3000000, 3500000, 4000000
-#endif
-};
-
-static int n_baud_table = ARRAY_SIZE(baud_table);
-
-/**
- *	tty_termios_baud_rate
- *	@termios: termios structure
- *
- *	Convert termios baud rate data into a speed. This should be called
- *	with the termios lock held if this termios is a terminal termios
- *	structure. May change the termios data.
- */
- 
-int tty_termios_baud_rate(struct termios *termios)
-{
-	unsigned int cbaud;
-	
-	cbaud = termios->c_cflag & CBAUD;
-
-	if (cbaud & CBAUDEX) {
-		cbaud &= ~CBAUDEX;
-
-		if (cbaud < 1 || cbaud + 15 > n_baud_table)
-			termios->c_cflag &= ~CBAUDEX;
-		else
-			cbaud += 15;
-	}
-	return baud_table[cbaud];
-}
-
-EXPORT_SYMBOL(tty_termios_baud_rate);
-
-/**
- *	tty_get_baud_rate	-	get tty bit rates
- *	@tty: tty to query
- *
- *	Returns the baud rate as an integer for this terminal. The
- *	termios lock must be held by the caller and the terminal bit
- *	flags may be updated.
- */
- 
-int tty_get_baud_rate(struct tty_struct *tty)
-{
-	int baud = tty_termios_baud_rate(tty->termios);
-
-	if (baud == 38400 && tty->alt_speed) {
-		if (!tty->warned) {
-			printk(KERN_WARNING "Use of setserial/setrocket to "
-					    "set SPD_* flags is deprecated\n");
-			tty->warned = 1;
-		}
-		baud = tty->alt_speed;
-	}
-	
-	return baud;
-}
-
-EXPORT_SYMBOL(tty_get_baud_rate);
-
-/**
- *	tty_flip_buffer_push	-	terminal
- *	@tty: tty to push
- *
- *	Queue a push of the terminal flip buffers to the line discipline. This
- *	function must not be called from IRQ context if tty->low_latency is set.
- *
- *	In the event of the queue being busy for flipping the work will be
- *	held off and retried later.
- */
-
-void tty_flip_buffer_push(struct tty_struct *tty)
-{
-	unsigned long flags;
-	spin_lock_irqsave(&tty->buf.lock, flags);
-	if (tty->buf.tail != NULL) {
-		tty->buf.tail->active = 0;
-		tty->buf.tail->commit = tty->buf.tail->used;
-	}
-	spin_unlock_irqrestore(&tty->buf.lock, flags);
-
-	if (tty->low_latency)
-		flush_to_ldisc((void *) tty);
-	else
-		schedule_delayed_work(&tty->buf.work, 1);
-}
-
-EXPORT_SYMBOL(tty_flip_buffer_push);
-
-
-/*
- * This subroutine initializes a tty structure.
- */
-static void initialize_tty_struct(struct tty_struct *tty)
-{
-	memset(tty, 0, sizeof(struct tty_struct));
-	tty->magic = TTY_MAGIC;
-	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
-	tty->pgrp = -1;
-	tty->overrun_time = jiffies;
-	tty->buf.head = tty->buf.tail = NULL;
-	tty_buffer_init(tty);
-	INIT_WORK(&tty->buf.work, flush_to_ldisc, tty);
-	init_MUTEX(&tty->buf.pty_sem);
-	init_MUTEX(&tty->termios_sem);
-	init_waitqueue_head(&tty->write_wait);
-	init_waitqueue_head(&tty->read_wait);
-	INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
-	sema_init(&tty->atomic_read, 1);
-	sema_init(&tty->atomic_write, 1);
-	spin_lock_init(&tty->read_lock);
-	INIT_LIST_HEAD(&tty->tty_files);
-	INIT_WORK(&tty->SAK_work, NULL, NULL);
-}
-
-/*
- * The default put_char routine if the driver did not define one.
- */
-static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
-{
-	tty->driver->write(tty, &ch, 1);
-}
-
-static struct class *tty_class;
-
-/**
- * tty_register_device - register a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
- * @device: a struct device that is associated with this tty device.
- *	This field is optional, if there is no known struct device for this
- *	tty device it can be set to NULL safely.
- *
- * This call is required to be made to register an individual tty device if
- * the tty driver's flags have the TTY_DRIVER_NO_DEVFS bit set.  If that
- * bit is not set, this function should not be called.
- */
-void tty_register_device(struct tty_driver *driver, unsigned index,
-			 struct device *device)
-{
-	char name[64];
-	dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
-
-	if (index >= driver->num) {
-		printk(KERN_ERR "Attempt to register invalid tty line number "
-		       " (%d).\n", index);
-		return;
-	}
-
-	devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR,
-			"%s%d", driver->devfs_name, index + driver->name_base);
-
-	if (driver->type == TTY_DRIVER_TYPE_PTY)
-		pty_line_name(driver, index, name);
-	else
-		tty_line_name(driver, index, name);
-	class_device_create(tty_class, NULL, dev, device, "%s", name);
-}
-
-/**
- * tty_unregister_device - unregister a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
- *
- * If a tty device is registered with a call to tty_register_device() then
- * this function must be made when the tty device is gone.
- */
-void tty_unregister_device(struct tty_driver *driver, unsigned index)
-{
-	devfs_remove("%s%d", driver->devfs_name, index + driver->name_base);
-	class_device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index);
-}
-
-EXPORT_SYMBOL(tty_register_device);
-EXPORT_SYMBOL(tty_unregister_device);
-
-struct tty_driver *alloc_tty_driver(int lines)
-{
-	struct tty_driver *driver;
-
-	driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
-	if (driver) {
-		memset(driver, 0, sizeof(struct tty_driver));
-		driver->magic = TTY_DRIVER_MAGIC;
-		driver->num = lines;
-		/* later we'll move allocation of tables here */
-	}
-	return driver;
-}
-
-void put_tty_driver(struct tty_driver *driver)
-{
-	kfree(driver);
-}
-
-void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
-{
-	driver->open = op->open;
-	driver->close = op->close;
-	driver->write = op->write;
-	driver->put_char = op->put_char;
-	driver->flush_chars = op->flush_chars;
-	driver->write_room = op->write_room;
-	driver->chars_in_buffer = op->chars_in_buffer;
-	driver->ioctl = op->ioctl;
-	driver->set_termios = op->set_termios;
-	driver->throttle = op->throttle;
-	driver->unthrottle = op->unthrottle;
-	driver->stop = op->stop;
-	driver->start = op->start;
-	driver->hangup = op->hangup;
-	driver->break_ctl = op->break_ctl;
-	driver->flush_buffer = op->flush_buffer;
-	driver->set_ldisc = op->set_ldisc;
-	driver->wait_until_sent = op->wait_until_sent;
-	driver->send_xchar = op->send_xchar;
-	driver->read_proc = op->read_proc;
-	driver->write_proc = op->write_proc;
-	driver->tiocmget = op->tiocmget;
-	driver->tiocmset = op->tiocmset;
-}
-
-
-EXPORT_SYMBOL(alloc_tty_driver);
-EXPORT_SYMBOL(put_tty_driver);
-EXPORT_SYMBOL(tty_set_operations);
-
-/*
- * Called by a tty driver to register itself.
- */
-int tty_register_driver(struct tty_driver *driver)
-{
-	int error;
-        int i;
-	dev_t dev;
-	void **p = NULL;
-
-	if (driver->flags & TTY_DRIVER_INSTALLED)
-		return 0;
-
-	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
-		p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
-		if (!p)
-			return -ENOMEM;
-		memset(p, 0, driver->num * 3 * sizeof(void *));
-	}
-
-	if (!driver->major) {
-		error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
-						(char*)driver->name);
-		if (!error) {
-			driver->major = MAJOR(dev);
-			driver->minor_start = MINOR(dev);
-		}
-	} else {
-		dev = MKDEV(driver->major, driver->minor_start);
-		error = register_chrdev_region(dev, driver->num,
-						(char*)driver->name);
-	}
-	if (error < 0) {
-		kfree(p);
-		return error;
-	}
-
-	if (p) {
-		driver->ttys = (struct tty_struct **)p;
-		driver->termios = (struct termios **)(p + driver->num);
-		driver->termios_locked = (struct termios **)(p + driver->num * 2);
-	} else {
-		driver->ttys = NULL;
-		driver->termios = NULL;
-		driver->termios_locked = NULL;
-	}
-
-	cdev_init(&driver->cdev, &tty_fops);
-	driver->cdev.owner = driver->owner;
-	error = cdev_add(&driver->cdev, dev, driver->num);
-	if (error) {
-		cdev_del(&driver->cdev);
-		unregister_chrdev_region(dev, driver->num);
-		driver->ttys = NULL;
-		driver->termios = driver->termios_locked = NULL;
-		kfree(p);
-		return error;
-	}
-
-	if (!driver->put_char)
-		driver->put_char = tty_default_put_char;
-	
-	list_add(&driver->tty_drivers, &tty_drivers);
-	
-	if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
-		for(i = 0; i < driver->num; i++)
-		    tty_register_device(driver, i, NULL);
-	}
-	proc_tty_register_driver(driver);
-	return 0;
-}
-
-EXPORT_SYMBOL(tty_register_driver);
-
-/*
- * Called by a tty driver to unregister itself.
- */
-int tty_unregister_driver(struct tty_driver *driver)
-{
-	int i;
-	struct termios *tp;
-	void *p;
-
-	if (driver->refcount)
-		return -EBUSY;
-
-	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
-				driver->num);
-
-	list_del(&driver->tty_drivers);
-
-	/*
-	 * Free the termios and termios_locked structures because
-	 * we don't want to get memory leaks when modular tty
-	 * drivers are removed from the kernel.
-	 */
-	for (i = 0; i < driver->num; i++) {
-		tp = driver->termios[i];
-		if (tp) {
-			driver->termios[i] = NULL;
-			kfree(tp);
-		}
-		tp = driver->termios_locked[i];
-		if (tp) {
-			driver->termios_locked[i] = NULL;
-			kfree(tp);
-		}
-		if (!(driver->flags & TTY_DRIVER_NO_DEVFS))
-			tty_unregister_device(driver, i);
-	}
-	p = driver->ttys;
-	proc_tty_unregister_driver(driver);
-	driver->ttys = NULL;
-	driver->termios = driver->termios_locked = NULL;
-	kfree(p);
-	cdev_del(&driver->cdev);
-	return 0;
-}
-
-EXPORT_SYMBOL(tty_unregister_driver);
-
-
-/*
- * Initialize the console device. This is called *early*, so
- * we can't necessarily depend on lots of kernel help here.
- * Just do some early initializations, and do the complex setup
- * later.
- */
-void __init console_init(void)
-{
-	initcall_t *call;
-
-	/* Setup the default TTY line discipline. */
-	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
-
-	/*
-	 * set up the console device so that later boot sequences can 
-	 * inform about problems etc..
-	 */
-#ifdef CONFIG_EARLY_PRINTK
-	disable_early_printk();
-#endif
-	call = __con_initcall_start;
-	while (call < __con_initcall_end) {
-		(*call)();
-		call++;
-	}
-}
-
-#ifdef CONFIG_VT
-extern int vty_init(void);
-#endif
-
-static int __init tty_class_init(void)
-{
-	tty_class = class_create(THIS_MODULE, "tty");
-	if (IS_ERR(tty_class))
-		return PTR_ERR(tty_class);
-	return 0;
-}
-
-postcore_initcall(tty_class_init);
-
-/* 3/2004 jmc: why do these devices exist? */
-
-static struct cdev tty_cdev, console_cdev;
-#ifdef CONFIG_UNIX98_PTYS
-static struct cdev ptmx_cdev;
-#endif
-#ifdef CONFIG_VT
-static struct cdev vc0_cdev;
-#endif
-
-/*
- * Ok, now we can initialize the rest of the tty devices and can count
- * on memory allocations, interrupts etc..
- */
-static int __init tty_init(void)
-{
-	cdev_init(&tty_cdev, &tty_fops);
-	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
-	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
-		panic("Couldn't register /dev/tty driver\n");
-	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 0), S_IFCHR|S_IRUGO|S_IWUGO, "tty");
-	class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
-
-	cdev_init(&console_cdev, &console_fops);
-	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
-	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
-		panic("Couldn't register /dev/console driver\n");
-	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 1), S_IFCHR|S_IRUSR|S_IWUSR, "console");
-	class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
-
-#ifdef CONFIG_UNIX98_PTYS
-	cdev_init(&ptmx_cdev, &ptmx_fops);
-	if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
-	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
-		panic("Couldn't register /dev/ptmx driver\n");
-	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");
-	class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
-#endif
-
-#ifdef CONFIG_VT
-	if (!console_use_vt)
-		goto out_vt;
-	cdev_init(&vc0_cdev, &console_fops);
-	if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
-	    register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
-		panic("Couldn't register /dev/tty0 driver\n");
-	devfs_mk_cdev(MKDEV(TTY_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vc/0");
-	class_device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
-
-	vty_init();
- out_vt:
-#endif
-	return 0;
-}
-module_init(tty_init);

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-11-30 18:29 [PATCH] PV framebuffer Markus Armbruster
@ 2006-12-01 13:25 ` Steven Smith
  2006-12-01 18:04   ` Markus Armbruster
                     ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Steven Smith @ 2006-12-01 13:25 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: xen-devel, sos22


[-- Attachment #1.1: Type: text/plain, Size: 191081 bytes --]

Applied.

Thanks,

Steven.

> 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@redhat.com>
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
> ---
> 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@us.ibm.com>
> + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
> + *
> + *  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@us.ibm.com>
> + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
> + *
> + *  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@us.ibm.com>
> + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
> + *
> + *  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@us.ibm.com>
> + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
> + *
> + *  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@dcs.warwick.ac.uk>, 27th May 1993.
> - *
> - * Rewrote canonical mode and added more termios flags.
> - * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
> - *
> - * Reorganized FASYNC support so mouse code can share it.
> - *	-- ctm@ardi.com, 9Sep95
> - *
> - * New TIOCLINUX variants added.
> - *	-- mj@k332.feld.cvut.cz, 19-Nov-95
> - * 
> - * Restrict vt switching via ioctl()
> - *      -- grif@cs.ucr.edu, 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@hut.fi>, March 97
> - *
> - * Rewrote init_dev and release_dev to eliminate races.
> - *	-- Bill Hawes <whawes@star.net>, June 97
> - *
> - * Added devfs support.
> - *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
> - *
> - * Added support for a Unix98-style ptmx device.
> - *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
> - *
> - * Reduced memory usage for older ARM systems
> - *      -- Russell King <rmk@arm.linux.org.uk>
> - *
> - * Move do_SAK() into process context.  Less stack use in devfs functions.
> - * alloc_tty_struct() always uses kmalloc() -- Andrew Morton <andrewm@uow.edu.eu> 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_room(tty, size);
> -	if (likely(space)) {
> -		struct tty_buffer *tb = tty->buf.tail;
> -		*chars = tb->char_buf_ptr + tb->used;
> -		memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
> -		tb->used += space;
> -	}
> -	return space;
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
> -
> -/*
> - *	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 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_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size)
> -{
> -	int space = tty_buffer_request_room(tty, size);
> -	if (likely(space)) {
> -		struct tty_buffer *tb = tty->buf.tail;
> -		*chars = tb->char_buf_ptr + tb->used;
> -		*flags = tb->flag_buf_ptr + tb->used;
> -		tb->used += space;
> -	}
> -	return space;
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
> -
> -
> -
> -/*
> - *	This is probably overkill for real world processors but
> - *	they are not on hot paths so a little discipline won't do 
> - *	any harm.
> - */
> - 
> -static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
> -{
> -	down(&tty->termios_sem);
> -	tty->termios->c_line = num;
> -	up(&tty->termios_sem);
> -}
> -
> -/*
> - *	This guards the refcounted line discipline lists. The lock
> - *	must be taken with irqs off because there are hangup path
> - *	callers who will do ldisc lookups and cannot sleep.
> - */
> - 
> -static DEFINE_SPINLOCK(tty_ldisc_lock);
> -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
> -static struct tty_ldisc tty_ldiscs[NR_LDISCS];	/* line disc dispatch table */
> -
> -int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
> -{
> -	unsigned long flags;
> -	int ret = 0;
> -	
> -	if (disc < N_TTY || disc >= NR_LDISCS)
> -		return -EINVAL;
> -	
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	tty_ldiscs[disc] = *new_ldisc;
> -	tty_ldiscs[disc].num = disc;
> -	tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
> -	tty_ldiscs[disc].refcount = 0;
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -	
> -	return ret;
> -}
> -EXPORT_SYMBOL(tty_register_ldisc);
> -
> -int tty_unregister_ldisc(int disc)
> -{
> -	unsigned long flags;
> -	int ret = 0;
> -
> -	if (disc < N_TTY || disc >= NR_LDISCS)
> -		return -EINVAL;
> -
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	if (tty_ldiscs[disc].refcount)
> -		ret = -EBUSY;
> -	else
> -		tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL(tty_unregister_ldisc);
> -
> -struct tty_ldisc *tty_ldisc_get(int disc)
> -{
> -	unsigned long flags;
> -	struct tty_ldisc *ld;
> -
> -	if (disc < N_TTY || disc >= NR_LDISCS)
> -		return NULL;
> -	
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -
> -	ld = &tty_ldiscs[disc];
> -	/* Check the entry is defined */
> -	if(ld->flags & LDISC_FLAG_DEFINED)
> -	{
> -		/* If the module is being unloaded we can't use it */
> -		if (!try_module_get(ld->owner))
> -		       	ld = NULL;
> -		else /* lock it */
> -			ld->refcount++;
> -	}
> -	else
> -		ld = NULL;
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -	return ld;
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_ldisc_get);
> -
> -void tty_ldisc_put(int disc)
> -{
> -	struct tty_ldisc *ld;
> -	unsigned long flags;
> -	
> -	if (disc < N_TTY || disc >= NR_LDISCS)
> -		BUG();
> -		
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	ld = &tty_ldiscs[disc];
> -	if(ld->refcount == 0)
> -		BUG();
> -	ld->refcount --;
> -	module_put(ld->owner);
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -}
> -	
> -EXPORT_SYMBOL_GPL(tty_ldisc_put);
> -
> -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
> -{
> -	tty->ldisc = *ld;
> -	tty->ldisc.refcount = 0;
> -}
> -
> -/**
> - *	tty_ldisc_try		-	internal helper
> - *	@tty: the tty
> - *
> - *	Make a single attempt to grab and bump the refcount on
> - *	the tty ldisc. Return 0 on failure or 1 on success. This is
> - *	used to implement both the waiting and non waiting versions
> - *	of tty_ldisc_ref
> - */
> -
> -static int tty_ldisc_try(struct tty_struct *tty)
> -{
> -	unsigned long flags;
> -	struct tty_ldisc *ld;
> -	int ret = 0;
> -	
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	ld = &tty->ldisc;
> -	if(test_bit(TTY_LDISC, &tty->flags))
> -	{
> -		ld->refcount++;
> -		ret = 1;
> -	}
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -	return ret;
> -}
> -
> -/**
> - *	tty_ldisc_ref_wait	-	wait for the tty ldisc
> - *	@tty: tty device
> - *
> - *	Dereference the line discipline for the terminal and take a 
> - *	reference to it. If the line discipline is in flux then 
> - *	wait patiently until it changes.
> - *
> - *	Note: Must not be called from an IRQ/timer context. The caller
> - *	must also be careful not to hold other locks that will deadlock
> - *	against a discipline change, such as an existing ldisc reference
> - *	(which we check for)
> - */
> - 
> -struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
> -{
> -	/* wait_event is a macro */
> -	wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
> -	if(tty->ldisc.refcount == 0)
> -		printk(KERN_ERR "tty_ldisc_ref_wait\n");
> -	return &tty->ldisc;
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
> -
> -/**
> - *	tty_ldisc_ref		-	get the tty ldisc
> - *	@tty: tty device
> - *
> - *	Dereference the line discipline for the terminal and take a 
> - *	reference to it. If the line discipline is in flux then 
> - *	return NULL. Can be called from IRQ and timer functions.
> - */
> - 
> -struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
> -{
> -	if(tty_ldisc_try(tty))
> -		return &tty->ldisc;
> -	return NULL;
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_ldisc_ref);
> -
> -/**
> - *	tty_ldisc_deref		-	free a tty ldisc reference
> - *	@ld: reference to free up
> - *
> - *	Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
> - *	be called in IRQ context.
> - */
> - 
> -void tty_ldisc_deref(struct tty_ldisc *ld)
> -{
> -	unsigned long flags;
> -
> -	if(ld == NULL)
> -		BUG();
> -		
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	if(ld->refcount == 0)
> -		printk(KERN_ERR "tty_ldisc_deref: no references.\n");
> -	else
> -		ld->refcount--;
> -	if(ld->refcount == 0)
> -		wake_up(&tty_ldisc_wait);
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_ldisc_deref);
> -
> -/**
> - *	tty_ldisc_enable	-	allow ldisc use
> - *	@tty: terminal to activate ldisc on
> - *
> - *	Set the TTY_LDISC flag when the line discipline can be called
> - *	again. Do neccessary wakeups for existing sleepers.
> - *
> - *	Note: nobody should set this bit except via this function. Clearing
> - *	directly is allowed.
> - */
> -
> -static void tty_ldisc_enable(struct tty_struct *tty)
> -{
> -	set_bit(TTY_LDISC, &tty->flags);
> -	wake_up(&tty_ldisc_wait);
> -}
> -	
> -/**
> - *	tty_set_ldisc		-	set line discipline
> - *	@tty: the terminal to set
> - *	@ldisc: the line discipline
> - *
> - *	Set the discipline of a tty line. Must be called from a process
> - *	context.
> - */
> - 
> -static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
> -{
> -	int retval = 0;
> -	struct tty_ldisc o_ldisc;
> -	char buf[64];
> -	int work;
> -	unsigned long flags;
> -	struct tty_ldisc *ld;
> -	struct tty_struct *o_tty;
> -
> -	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
> -		return -EINVAL;
> -
> -restart:
> -
> -	ld = tty_ldisc_get(ldisc);
> -	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
> -	/* Cyrus Durgin <cider@speakeasy.org> */
> -	if (ld == NULL) {
> -		request_module("tty-ldisc-%d", ldisc);
> -		ld = tty_ldisc_get(ldisc);
> -	}
> -	if (ld == NULL)
> -		return -EINVAL;
> -
> -	/*
> -	 *	No more input please, we are switching. The new ldisc
> -	 *	will update this value in the ldisc open function
> -	 */
> -
> -	tty->receive_room = 0;
> -
> -	/*
> -	 *	Problem: What do we do if this blocks ?
> -	 */
> -
> -	tty_wait_until_sent(tty, 0);
> -
> -	if (tty->ldisc.num == ldisc) {
> -		tty_ldisc_put(ldisc);
> -		return 0;
> -	}
> -
> -	o_ldisc = tty->ldisc;
> -	o_tty = tty->link;
> -
> -	/*
> -	 *	Make sure we don't change while someone holds a
> -	 *	reference to the line discipline. The TTY_LDISC bit
> -	 *	prevents anyone taking a reference once it is clear.
> -	 *	We need the lock to avoid racing reference takers.
> -	 */
> -
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
> -		if(tty->ldisc.refcount) {
> -			/* Free the new ldisc we grabbed. Must drop the lock
> -			   first. */
> -			spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -			tty_ldisc_put(ldisc);
> -			/*
> -			 * There are several reasons we may be busy, including
> -			 * random momentary I/O traffic. We must therefore
> -			 * retry. We could distinguish between blocking ops
> -			 * and retries if we made tty_ldisc_wait() smarter. That
> -			 * is up for discussion.
> -			 */
> -			if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
> -				return -ERESTARTSYS;
> -			goto restart;
> -		}
> -		if(o_tty && o_tty->ldisc.refcount) {
> -			spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -			tty_ldisc_put(ldisc);
> -			if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
> -				return -ERESTARTSYS;
> -			goto restart;
> -		}
> -	}
> -
> -	/* if the TTY_LDISC bit is set, then we are racing against another ldisc change */
> -
> -	if (!test_bit(TTY_LDISC, &tty->flags)) {
> -		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -		tty_ldisc_put(ldisc);
> -		ld = tty_ldisc_ref_wait(tty);
> -		tty_ldisc_deref(ld);
> -		goto restart;
> -	}
> -
> -	clear_bit(TTY_LDISC, &tty->flags);
> -	clear_bit(TTY_DONT_FLIP, &tty->flags);
> -	if (o_tty) {
> -		clear_bit(TTY_LDISC, &o_tty->flags);
> -		clear_bit(TTY_DONT_FLIP, &o_tty->flags);
> -	}
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -
> -	/*
> -	 *	From this point on we know nobody has an ldisc
> -	 *	usage reference, nor can they obtain one until
> -	 *	we say so later on.
> -	 */
> -
> -	work = cancel_delayed_work(&tty->buf.work);
> -	/*
> -	 * Wait for ->hangup_work and ->buf.work handlers to terminate
> -	 */
> -	 
> -	flush_scheduled_work();
> -	/* Shutdown the current discipline. */
> -	if (tty->ldisc.close)
> -		(tty->ldisc.close)(tty);
> -
> -	/* Now set up the new line discipline. */
> -	tty_ldisc_assign(tty, ld);
> -	tty_set_termios_ldisc(tty, ldisc);
> -	if (tty->ldisc.open)
> -		retval = (tty->ldisc.open)(tty);
> -	if (retval < 0) {
> -		tty_ldisc_put(ldisc);
> -		/* There is an outstanding reference here so this is safe */
> -		tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
> -		tty_set_termios_ldisc(tty, tty->ldisc.num);
> -		if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
> -			tty_ldisc_put(o_ldisc.num);
> -			/* This driver is always present */
> -			tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
> -			tty_set_termios_ldisc(tty, N_TTY);
> -			if (tty->ldisc.open) {
> -				int r = tty->ldisc.open(tty);
> -
> -				if (r < 0)
> -					panic("Couldn't open N_TTY ldisc for "
> -					      "%s --- error %d.",
> -					      tty_name(tty, buf), r);
> -			}
> -		}
> -	}
> -	/* At this point we hold a reference to the new ldisc and a
> -	   a reference to the old ldisc. If we ended up flipping back
> -	   to the existing ldisc we have two references to it */
> -	
> -	if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)
> -		tty->driver->set_ldisc(tty);
> -		
> -	tty_ldisc_put(o_ldisc.num);
> -	
> -	/*
> -	 *	Allow ldisc referencing to occur as soon as the driver
> -	 *	ldisc callback completes.
> -	 */
> -	 
> -	tty_ldisc_enable(tty);
> -	if (o_tty)
> -		tty_ldisc_enable(o_tty);
> -	
> -	/* Restart it in case no characters kick it off. Safe if
> -	   already running */
> -	if (work)
> -		schedule_delayed_work(&tty->buf.work, 1);
> -	return retval;
> -}
> -
> -/*
> - * This routine returns a tty driver structure, given a device number
> - */
> -static struct tty_driver *get_tty_driver(dev_t device, int *index)
> -{
> -	struct tty_driver *p;
> -
> -	list_for_each_entry(p, &tty_drivers, tty_drivers) {
> -		dev_t base = MKDEV(p->major, p->minor_start);
> -		if (device < base || device >= base + p->num)
> -			continue;
> -		*index = device - base;
> -		return p;
> -	}
> -	return NULL;
> -}
> -
> -/*
> - * If we try to write to, or set the state of, a terminal and we're
> - * not in the foreground, send a SIGTTOU.  If the signal is blocked or
> - * ignored, go ahead and perform the operation.  (POSIX 7.2)
> - */
> -int tty_check_change(struct tty_struct * tty)
> -{
> -	if (current->signal->tty != tty)
> -		return 0;
> -	if (tty->pgrp <= 0) {
> -		printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");
> -		return 0;
> -	}
> -	if (process_group(current) == tty->pgrp)
> -		return 0;
> -	if (is_ignored(SIGTTOU))
> -		return 0;
> -	if (is_orphaned_pgrp(process_group(current)))
> -		return -EIO;
> -	(void) kill_pg(process_group(current), SIGTTOU, 1);
> -	return -ERESTARTSYS;
> -}
> -
> -EXPORT_SYMBOL(tty_check_change);
> -
> -static ssize_t hung_up_tty_read(struct file * file, char __user * buf,
> -				size_t count, loff_t *ppos)
> -{
> -	return 0;
> -}
> -
> -static ssize_t hung_up_tty_write(struct file * file, const char __user * buf,
> -				 size_t count, loff_t *ppos)
> -{
> -	return -EIO;
> -}
> -
> -/* No kernel lock held - none needed ;) */
> -static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait)
> -{
> -	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
> -}
> -
> -static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
> -			     unsigned int cmd, unsigned long arg)
> -{
> -	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
> -}
> -
> -static struct file_operations tty_fops = {
> -	.llseek		= no_llseek,
> -	.read		= tty_read,
> -	.write		= tty_write,
> -	.poll		= tty_poll,
> -	.ioctl		= tty_ioctl,
> -	.open		= tty_open,
> -	.release	= tty_release,
> -	.fasync		= tty_fasync,
> -};
> -
> -#ifdef CONFIG_UNIX98_PTYS
> -static struct file_operations ptmx_fops = {
> -	.llseek		= no_llseek,
> -	.read		= tty_read,
> -	.write		= tty_write,
> -	.poll		= tty_poll,
> -	.ioctl		= tty_ioctl,
> -	.open		= ptmx_open,
> -	.release	= tty_release,
> -	.fasync		= tty_fasync,
> -};
> -#endif
> -
> -static struct file_operations console_fops = {
> -	.llseek		= no_llseek,
> -	.read		= tty_read,
> -	.write		= redirected_tty_write,
> -	.poll		= tty_poll,
> -	.ioctl		= tty_ioctl,
> -	.open		= tty_open,
> -	.release	= tty_release,
> -	.fasync		= tty_fasync,
> -};
> -
> -static struct file_operations hung_up_tty_fops = {
> -	.llseek		= no_llseek,
> -	.read		= hung_up_tty_read,
> -	.write		= hung_up_tty_write,
> -	.poll		= hung_up_tty_poll,
> -	.ioctl		= hung_up_tty_ioctl,
> -	.release	= tty_release,
> -};
> -
> -static DEFINE_SPINLOCK(redirect_lock);
> -static struct file *redirect;
> -
> -/**
> - *	tty_wakeup	-	request more data
> - *	@tty: terminal
> - *
> - *	Internal and external helper for wakeups of tty. This function
> - *	informs the line discipline if present that the driver is ready
> - *	to receive more output data.
> - */
> - 
> -void tty_wakeup(struct tty_struct *tty)
> -{
> -	struct tty_ldisc *ld;
> -	
> -	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
> -		ld = tty_ldisc_ref(tty);
> -		if(ld) {
> -			if(ld->write_wakeup)
> -				ld->write_wakeup(tty);
> -			tty_ldisc_deref(ld);
> -		}
> -	}
> -	wake_up_interruptible(&tty->write_wait);
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_wakeup);
> -
> -/**
> - *	tty_ldisc_flush	-	flush line discipline queue
> - *	@tty: tty
> - *
> - *	Flush the line discipline queue (if any) for this tty. If there
> - *	is no line discipline active this is a no-op.
> - */
> - 
> -void tty_ldisc_flush(struct tty_struct *tty)
> -{
> -	struct tty_ldisc *ld = tty_ldisc_ref(tty);
> -	if(ld) {
> -		if(ld->flush_buffer)
> -			ld->flush_buffer(tty);
> -		tty_ldisc_deref(ld);
> -	}
> -}
> -
> -EXPORT_SYMBOL_GPL(tty_ldisc_flush);
> -	
> -/*
> - * This can be called by the "eventd" kernel thread.  That is process synchronous,
> - * but doesn't hold any locks, so we need to make sure we have the appropriate
> - * locks for what we're doing..
> - */
> -static void do_tty_hangup(void *data)
> -{
> -	struct tty_struct *tty = (struct tty_struct *) data;
> -	struct file * cons_filp = NULL;
> -	struct file *filp, *f = NULL;
> -	struct task_struct *p;
> -	struct tty_ldisc *ld;
> -	int    closecount = 0, n;
> -
> -	if (!tty)
> -		return;
> -
> -	/* inuse_filps is protected by the single kernel lock */
> -	lock_kernel();
> -
> -	spin_lock(&redirect_lock);
> -	if (redirect && redirect->private_data == tty) {
> -		f = redirect;
> -		redirect = NULL;
> -	}
> -	spin_unlock(&redirect_lock);
> -	
> -	check_tty_count(tty, "do_tty_hangup");
> -	file_list_lock();
> -	/* This breaks for file handles being sent over AF_UNIX sockets ? */
> -	list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
> -		if (filp->f_op->write == redirected_tty_write)
> -			cons_filp = filp;
> -		if (filp->f_op->write != tty_write)
> -			continue;
> -		closecount++;
> -		tty_fasync(-1, filp, 0);	/* can't block */
> -		filp->f_op = &hung_up_tty_fops;
> -	}
> -	file_list_unlock();
> -	
> -	/* FIXME! What are the locking issues here? This may me overdoing things..
> -	 * this question is especially important now that we've removed the irqlock. */
> -
> -	ld = tty_ldisc_ref(tty);
> -	if(ld != NULL)	/* We may have no line discipline at this point */
> -	{
> -		if (ld->flush_buffer)
> -			ld->flush_buffer(tty);
> -		if (tty->driver->flush_buffer)
> -			tty->driver->flush_buffer(tty);
> -		if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
> -		    ld->write_wakeup)
> -			ld->write_wakeup(tty);
> -		if (ld->hangup)
> -			ld->hangup(tty);
> -	}
> -
> -	/* FIXME: Once we trust the LDISC code better we can wait here for
> -	   ldisc completion and fix the driver call race */
> -	   
> -	wake_up_interruptible(&tty->write_wait);
> -	wake_up_interruptible(&tty->read_wait);
> -
> -	/*
> -	 * Shutdown the current line discipline, and reset it to
> -	 * N_TTY.
> -	 */
> -	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
> -	{
> -		down(&tty->termios_sem);
> -		*tty->termios = tty->driver->init_termios;
> -		up(&tty->termios_sem);
> -	}
> -	
> -	/* Defer ldisc switch */
> -	/* tty_deferred_ldisc_switch(N_TTY);
> -	
> -	  This should get done automatically when the port closes and
> -	  tty_release is called */
> -	
> -	read_lock(&tasklist_lock);
> -	if (tty->session > 0) {
> -		do_each_task_pid(tty->session, PIDTYPE_SID, p) {
> -			if (p->signal->tty == tty)
> -				p->signal->tty = NULL;
> -			if (!p->signal->leader)
> -				continue;
> -			send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p);
> -			send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p);
> -			if (tty->pgrp > 0)
> -				p->signal->tty_old_pgrp = tty->pgrp;
> -		} while_each_task_pid(tty->session, PIDTYPE_SID, p);
> -	}
> -	read_unlock(&tasklist_lock);
> -
> -	tty->flags = 0;
> -	tty->session = 0;
> -	tty->pgrp = -1;
> -	tty->ctrl_status = 0;
> -	/*
> -	 *	If one of the devices matches a console pointer, we
> -	 *	cannot just call hangup() because that will cause
> -	 *	tty->count and state->count to go out of sync.
> -	 *	So we just call close() the right number of times.
> -	 */
> -	if (cons_filp) {
> -		if (tty->driver->close)
> -			for (n = 0; n < closecount; n++)
> -				tty->driver->close(tty, cons_filp);
> -	} else if (tty->driver->hangup)
> -		(tty->driver->hangup)(tty);
> -		
> -	/* We don't want to have driver/ldisc interactions beyond
> -	   the ones we did here. The driver layer expects no
> -	   calls after ->hangup() from the ldisc side. However we
> -	   can't yet guarantee all that */
> -
> -	set_bit(TTY_HUPPED, &tty->flags);
> -	if (ld) {
> -		tty_ldisc_enable(tty);
> -		tty_ldisc_deref(ld);
> -	}
> -	unlock_kernel();
> -	if (f)
> -		fput(f);
> -}
> -
> -void tty_hangup(struct tty_struct * tty)
> -{
> -#ifdef TTY_DEBUG_HANGUP
> -	char	buf[64];
> -	
> -	printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
> -#endif
> -	schedule_work(&tty->hangup_work);
> -}
> -
> -EXPORT_SYMBOL(tty_hangup);
> -
> -void tty_vhangup(struct tty_struct * tty)
> -{
> -#ifdef TTY_DEBUG_HANGUP
> -	char	buf[64];
> -
> -	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
> -#endif
> -	do_tty_hangup((void *) tty);
> -}
> -EXPORT_SYMBOL(tty_vhangup);
> -
> -int tty_hung_up_p(struct file * filp)
> -{
> -	return (filp->f_op == &hung_up_tty_fops);
> -}
> -
> -EXPORT_SYMBOL(tty_hung_up_p);
> -
> -/*
> - * This function is typically called only by the session leader, when
> - * it wants to disassociate itself from its controlling tty.
> - *
> - * It performs the following functions:
> - * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
> - * 	(2)  Clears the tty from being controlling the session
> - * 	(3)  Clears the controlling tty for all processes in the
> - * 		session group.
> - *
> - * The argument on_exit is set to 1 if called when a process is
> - * exiting; it is 0 if called by the ioctl TIOCNOTTY.
> - */
> -void disassociate_ctty(int on_exit)
> -{
> -	struct tty_struct *tty;
> -	struct task_struct *p;
> -	int tty_pgrp = -1;
> -
> -	lock_kernel();
> -
> -	down(&tty_sem);
> -	tty = current->signal->tty;
> -	if (tty) {
> -		tty_pgrp = tty->pgrp;
> -		up(&tty_sem);
> -		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
> -			tty_vhangup(tty);
> -	} else {
> -		if (current->signal->tty_old_pgrp) {
> -			kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
> -			kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
> -		}
> -		up(&tty_sem);
> -		unlock_kernel();	
> -		return;
> -	}
> -	if (tty_pgrp > 0) {
> -		kill_pg(tty_pgrp, SIGHUP, on_exit);
> -		if (!on_exit)
> -			kill_pg(tty_pgrp, SIGCONT, on_exit);
> -	}
> -
> -	/* Must lock changes to tty_old_pgrp */
> -	down(&tty_sem);
> -	current->signal->tty_old_pgrp = 0;
> -	tty->session = 0;
> -	tty->pgrp = -1;
> -
> -	/* Now clear signal->tty under the lock */
> -	read_lock(&tasklist_lock);
> -	do_each_task_pid(current->signal->session, PIDTYPE_SID, p) {
> -		p->signal->tty = NULL;
> -	} while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
> -	read_unlock(&tasklist_lock);
> -	up(&tty_sem);
> -	unlock_kernel();
> -}
> -
> -void stop_tty(struct tty_struct *tty)
> -{
> -	if (tty->stopped)
> -		return;
> -	tty->stopped = 1;
> -	if (tty->link && tty->link->packet) {
> -		tty->ctrl_status &= ~TIOCPKT_START;
> -		tty->ctrl_status |= TIOCPKT_STOP;
> -		wake_up_interruptible(&tty->link->read_wait);
> -	}
> -	if (tty->driver->stop)
> -		(tty->driver->stop)(tty);
> -}
> -
> -EXPORT_SYMBOL(stop_tty);
> -
> -void start_tty(struct tty_struct *tty)
> -{
> -	if (!tty->stopped || tty->flow_stopped)
> -		return;
> -	tty->stopped = 0;
> -	if (tty->link && tty->link->packet) {
> -		tty->ctrl_status &= ~TIOCPKT_STOP;
> -		tty->ctrl_status |= TIOCPKT_START;
> -		wake_up_interruptible(&tty->link->read_wait);
> -	}
> -	if (tty->driver->start)
> -		(tty->driver->start)(tty);
> -
> -	/* If we have a running line discipline it may need kicking */
> -	tty_wakeup(tty);
> -	wake_up_interruptible(&tty->write_wait);
> -}
> -
> -EXPORT_SYMBOL(start_tty);
> -
> -static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 
> -			loff_t *ppos)
> -{
> -	int i;
> -	struct tty_struct * tty;
> -	struct inode *inode;
> -	struct tty_ldisc *ld;
> -
> -	tty = (struct tty_struct *)file->private_data;
> -	inode = file->f_dentry->d_inode;
> -	if (tty_paranoia_check(tty, inode, "tty_read"))
> -		return -EIO;
> -	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
> -		return -EIO;
> -
> -	/* We want to wait for the line discipline to sort out in this
> -	   situation */
> -	ld = tty_ldisc_ref_wait(tty);
> -	lock_kernel();
> -	if (ld->read)
> -		i = (ld->read)(tty,file,buf,count);
> -	else
> -		i = -EIO;
> -	tty_ldisc_deref(ld);
> -	unlock_kernel();
> -	if (i > 0)
> -		inode->i_atime = current_fs_time(inode->i_sb);
> -	return i;
> -}
> -
> -/*
> - * Split writes up in sane blocksizes to avoid
> - * denial-of-service type attacks
> - */
> -static inline ssize_t do_tty_write(
> -	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
> -	struct tty_struct *tty,
> -	struct file *file,
> -	const char __user *buf,
> -	size_t count)
> -{
> -	ssize_t ret = 0, written = 0;
> -	unsigned int chunk;
> -	
> -	if (down_interruptible(&tty->atomic_write)) {
> -		return -ERESTARTSYS;
> -	}
> -
> -	/*
> -	 * We chunk up writes into a temporary buffer. This
> -	 * simplifies low-level drivers immensely, since they
> -	 * don't have locking issues and user mode accesses.
> -	 *
> -	 * But if TTY_NO_WRITE_SPLIT is set, we should use a
> -	 * big chunk-size..
> -	 *
> -	 * The default chunk-size is 2kB, because the NTTY
> -	 * layer has problems with bigger chunks. It will
> -	 * claim to be able to handle more characters than
> -	 * it actually does.
> -	 */
> -	chunk = 2048;
> -	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
> -		chunk = 65536;
> -	if (count < chunk)
> -		chunk = count;
> -
> -	/* write_buf/write_cnt is protected by the atomic_write semaphore */
> -	if (tty->write_cnt < chunk) {
> -		unsigned char *buf;
> -
> -		if (chunk < 1024)
> -			chunk = 1024;
> -
> -		buf = kmalloc(chunk, GFP_KERNEL);
> -		if (!buf) {
> -			up(&tty->atomic_write);
> -			return -ENOMEM;
> -		}
> -		kfree(tty->write_buf);
> -		tty->write_cnt = chunk;
> -		tty->write_buf = buf;
> -	}
> -
> -	/* Do the write .. */
> -	for (;;) {
> -		size_t size = count;
> -		if (size > chunk)
> -			size = chunk;
> -		ret = -EFAULT;
> -		if (copy_from_user(tty->write_buf, buf, size))
> -			break;
> -		lock_kernel();
> -		ret = write(tty, file, tty->write_buf, size);
> -		unlock_kernel();
> -		if (ret <= 0)
> -			break;
> -		written += ret;
> -		buf += ret;
> -		count -= ret;
> -		if (!count)
> -			break;
> -		ret = -ERESTARTSYS;
> -		if (signal_pending(current))
> -			break;
> -		cond_resched();
> -	}
> -	if (written) {
> -		struct inode *inode = file->f_dentry->d_inode;
> -		inode->i_mtime = current_fs_time(inode->i_sb);
> -		ret = written;
> -	}
> -	up(&tty->atomic_write);
> -	return ret;
> -}
> -
> -
> -static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
> -			 loff_t *ppos)
> -{
> -	struct tty_struct * tty;
> -	struct inode *inode = file->f_dentry->d_inode;
> -	ssize_t ret;
> -	struct tty_ldisc *ld;
> -	
> -	tty = (struct tty_struct *)file->private_data;
> -	if (tty_paranoia_check(tty, inode, "tty_write"))
> -		return -EIO;
> -	if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
> -		return -EIO;
> -
> -	ld = tty_ldisc_ref_wait(tty);		
> -	if (!ld->write)
> -		ret = -EIO;
> -	else
> -		ret = do_tty_write(ld->write, tty, file, buf, count);
> -	tty_ldisc_deref(ld);
> -	return ret;
> -}
> -
> -ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count,
> -			 loff_t *ppos)
> -{
> -	struct file *p = NULL;
> -
> -	spin_lock(&redirect_lock);
> -	if (redirect) {
> -		get_file(redirect);
> -		p = redirect;
> -	}
> -	spin_unlock(&redirect_lock);
> -
> -	if (p) {
> -		ssize_t res;
> -		res = vfs_write(p, buf, count, &p->f_pos);
> -		fput(p);
> -		return res;
> -	}
> -
> -	return tty_write(file, buf, count, ppos);
> -}
> -
> -static char ptychar[] = "pqrstuvwxyzabcde";
> -
> -static inline void pty_line_name(struct tty_driver *driver, int index, char *p)
> -{
> -	int i = index + driver->name_base;
> -	/* ->name is initialized to "ttyp", but "tty" is expected */
> -	sprintf(p, "%s%c%x",
> -			driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
> -			ptychar[i >> 4 & 0xf], i & 0xf);
> -}
> -
> -static inline void tty_line_name(struct tty_driver *driver, int index, char *p)
> -{
> -	sprintf(p, "%s%d", driver->name, index + driver->name_base);
> -}
> -
> -/*
> - * WSH 06/09/97: Rewritten to remove races and properly clean up after a
> - * failed open.  The new code protects the open with a semaphore, so it's
> - * really quite straightforward.  The semaphore locking can probably be
> - * relaxed for the (most common) case of reopening a tty.
> - */
> -static int init_dev(struct tty_driver *driver, int idx,
> -	struct tty_struct **ret_tty)
> -{
> -	struct tty_struct *tty, *o_tty;
> -	struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
> -	struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
> -	int retval=0;
> -
> -	/* check whether we're reopening an existing tty */
> -	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
> -		tty = devpts_get_tty(idx);
> -		if (tty && driver->subtype == PTY_TYPE_MASTER)
> -			tty = tty->link;
> -	} else {
> -		tty = driver->ttys[idx];
> -	}
> -	if (tty) goto fast_track;
> -
> -	/*
> -	 * First time open is complex, especially for PTY devices.
> -	 * This code guarantees that either everything succeeds and the
> -	 * TTY is ready for operation, or else the table slots are vacated
> -	 * and the allocated memory released.  (Except that the termios 
> -	 * and locked termios may be retained.)
> -	 */
> -
> -	if (!try_module_get(driver->owner)) {
> -		retval = -ENODEV;
> -		goto end_init;
> -	}
> -
> -	o_tty = NULL;
> -	tp = o_tp = NULL;
> -	ltp = o_ltp = NULL;
> -
> -	tty = alloc_tty_struct();
> -	if(!tty)
> -		goto fail_no_mem;
> -	initialize_tty_struct(tty);
> -	tty->driver = driver;
> -	tty->index = idx;
> -	tty_line_name(driver, idx, tty->name);
> -
> -	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
> -		tp_loc = &tty->termios;
> -		ltp_loc = &tty->termios_locked;
> -	} else {
> -		tp_loc = &driver->termios[idx];
> -		ltp_loc = &driver->termios_locked[idx];
> -	}
> -
> -	if (!*tp_loc) {
> -		tp = (struct termios *) kmalloc(sizeof(struct termios),
> -						GFP_KERNEL);
> -		if (!tp)
> -			goto free_mem_out;
> -		*tp = driver->init_termios;
> -	}
> -
> -	if (!*ltp_loc) {
> -		ltp = (struct termios *) kmalloc(sizeof(struct termios),
> -						 GFP_KERNEL);
> -		if (!ltp)
> -			goto free_mem_out;
> -		memset(ltp, 0, sizeof(struct termios));
> -	}
> -
> -	if (driver->type == TTY_DRIVER_TYPE_PTY) {
> -		o_tty = alloc_tty_struct();
> -		if (!o_tty)
> -			goto free_mem_out;
> -		initialize_tty_struct(o_tty);
> -		o_tty->driver = driver->other;
> -		o_tty->index = idx;
> -		tty_line_name(driver->other, idx, o_tty->name);
> -
> -		if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
> -			o_tp_loc = &o_tty->termios;
> -			o_ltp_loc = &o_tty->termios_locked;
> -		} else {
> -			o_tp_loc = &driver->other->termios[idx];
> -			o_ltp_loc = &driver->other->termios_locked[idx];
> -		}
> -
> -		if (!*o_tp_loc) {
> -			o_tp = (struct termios *)
> -				kmalloc(sizeof(struct termios), GFP_KERNEL);
> -			if (!o_tp)
> -				goto free_mem_out;
> -			*o_tp = driver->other->init_termios;
> -		}
> -
> -		if (!*o_ltp_loc) {
> -			o_ltp = (struct termios *)
> -				kmalloc(sizeof(struct termios), GFP_KERNEL);
> -			if (!o_ltp)
> -				goto free_mem_out;
> -			memset(o_ltp, 0, sizeof(struct termios));
> -		}
> -
> -		/*
> -		 * Everything allocated ... set up the o_tty structure.
> -		 */
> -		if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {
> -			driver->other->ttys[idx] = o_tty;
> -		}
> -		if (!*o_tp_loc)
> -			*o_tp_loc = o_tp;
> -		if (!*o_ltp_loc)
> -			*o_ltp_loc = o_ltp;
> -		o_tty->termios = *o_tp_loc;
> -		o_tty->termios_locked = *o_ltp_loc;
> -		driver->other->refcount++;
> -		if (driver->subtype == PTY_TYPE_MASTER)
> -			o_tty->count++;
> -
> -		/* Establish the links in both directions */
> -		tty->link   = o_tty;
> -		o_tty->link = tty;
> -	}
> -
> -	/* 
> -	 * All structures have been allocated, so now we install them.
> -	 * Failures after this point use release_mem to clean up, so 
> -	 * there's no need to null out the local pointers.
> -	 */
> -	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
> -		driver->ttys[idx] = tty;
> -	}
> -	
> -	if (!*tp_loc)
> -		*tp_loc = tp;
> -	if (!*ltp_loc)
> -		*ltp_loc = ltp;
> -	tty->termios = *tp_loc;
> -	tty->termios_locked = *ltp_loc;
> -	driver->refcount++;
> -	tty->count++;
> -
> -	/* 
> -	 * Structures all installed ... call the ldisc open routines.
> -	 * If we fail here just call release_mem to clean up.  No need
> -	 * to decrement the use counts, as release_mem doesn't care.
> -	 */
> -
> -	if (tty->ldisc.open) {
> -		retval = (tty->ldisc.open)(tty);
> -		if (retval)
> -			goto release_mem_out;
> -	}
> -	if (o_tty && o_tty->ldisc.open) {
> -		retval = (o_tty->ldisc.open)(o_tty);
> -		if (retval) {
> -			if (tty->ldisc.close)
> -				(tty->ldisc.close)(tty);
> -			goto release_mem_out;
> -		}
> -		tty_ldisc_enable(o_tty);
> -	}
> -	tty_ldisc_enable(tty);
> -	goto success;
> -
> -	/*
> -	 * This fast open can be used if the tty is already open.
> -	 * No memory is allocated, and the only failures are from
> -	 * attempting to open a closing tty or attempting multiple
> -	 * opens on a pty master.
> -	 */
> -fast_track:
> -	if (test_bit(TTY_CLOSING, &tty->flags)) {
> -		retval = -EIO;
> -		goto end_init;
> -	}
> -	if (driver->type == TTY_DRIVER_TYPE_PTY &&
> -	    driver->subtype == PTY_TYPE_MASTER) {
> -		/*
> -		 * special case for PTY masters: only one open permitted, 
> -		 * and the slave side open count is incremented as well.
> -		 */
> -		if (tty->count) {
> -			retval = -EIO;
> -			goto end_init;
> -		}
> -		tty->link->count++;
> -	}
> -	tty->count++;
> -	tty->driver = driver; /* N.B. why do this every time?? */
> -
> -	/* FIXME */
> -	if(!test_bit(TTY_LDISC, &tty->flags))
> -		printk(KERN_ERR "init_dev but no ldisc\n");
> -success:
> -	*ret_tty = tty;
> -	
> -	/* All paths come through here to release the semaphore */
> -end_init:
> -	return retval;
> -
> -	/* Release locally allocated memory ... nothing placed in slots */
> -free_mem_out:
> -	kfree(o_tp);
> -	if (o_tty)
> -		free_tty_struct(o_tty);
> -	kfree(ltp);
> -	kfree(tp);
> -	free_tty_struct(tty);
> -
> -fail_no_mem:
> -	module_put(driver->owner);
> -	retval = -ENOMEM;
> -	goto end_init;
> -
> -	/* call the tty release_mem routine to clean out this slot */
> -release_mem_out:
> -	printk(KERN_INFO "init_dev: ldisc open failed, "
> -			 "clearing slot %d\n", idx);
> -	release_mem(tty, idx);
> -	goto end_init;
> -}
> -
> -/*
> - * Releases memory associated with a tty structure, and clears out the
> - * driver table slots.
> - */
> -static void release_mem(struct tty_struct *tty, int idx)
> -{
> -	struct tty_struct *o_tty;
> -	struct termios *tp;
> -	int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
> -
> -	if ((o_tty = tty->link) != NULL) {
> -		if (!devpts)
> -			o_tty->driver->ttys[idx] = NULL;
> -		if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
> -			tp = o_tty->termios;
> -			if (!devpts)
> -				o_tty->driver->termios[idx] = NULL;
> -			kfree(tp);
> -
> -			tp = o_tty->termios_locked;
> -			if (!devpts)
> -				o_tty->driver->termios_locked[idx] = NULL;
> -			kfree(tp);
> -		}
> -		o_tty->magic = 0;
> -		o_tty->driver->refcount--;
> -		file_list_lock();
> -		list_del_init(&o_tty->tty_files);
> -		file_list_unlock();
> -		free_tty_struct(o_tty);
> -	}
> -
> -	if (!devpts)
> -		tty->driver->ttys[idx] = NULL;
> -	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
> -		tp = tty->termios;
> -		if (!devpts)
> -			tty->driver->termios[idx] = NULL;
> -		kfree(tp);
> -
> -		tp = tty->termios_locked;
> -		if (!devpts)
> -			tty->driver->termios_locked[idx] = NULL;
> -		kfree(tp);
> -	}
> -
> -	tty->magic = 0;
> -	tty->driver->refcount--;
> -	file_list_lock();
> -	list_del_init(&tty->tty_files);
> -	file_list_unlock();
> -	module_put(tty->driver->owner);
> -	free_tty_struct(tty);
> -}
> -
> -/*
> - * Even releasing the tty structures is a tricky business.. We have
> - * to be very careful that the structures are all released at the
> - * same time, as interrupts might otherwise get the wrong pointers.
> - *
> - * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
> - * lead to double frees or releasing memory still in use.
> - */
> -static void release_dev(struct file * filp)
> -{
> -	struct tty_struct *tty, *o_tty;
> -	int	pty_master, tty_closing, o_tty_closing, do_sleep;
> -	int	devpts_master, devpts;
> -	int	idx;
> -	char	buf[64];
> -	unsigned long flags;
> -	
> -	tty = (struct tty_struct *)filp->private_data;
> -	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "release_dev"))
> -		return;
> -
> -	check_tty_count(tty, "release_dev");
> -
> -	tty_fasync(-1, filp, 0);
> -
> -	idx = tty->index;
> -	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
> -		      tty->driver->subtype == PTY_TYPE_MASTER);
> -	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
> -	devpts_master = pty_master && devpts;
> -	o_tty = tty->link;
> -
> -#ifdef TTY_PARANOIA_CHECK
> -	if (idx < 0 || idx >= tty->driver->num) {
> -		printk(KERN_DEBUG "release_dev: bad idx when trying to "
> -				  "free (%s)\n", tty->name);
> -		return;
> -	}
> -	if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
> -		if (tty != tty->driver->ttys[idx]) {
> -			printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
> -			       "for (%s)\n", idx, tty->name);
> -			return;
> -		}
> -		if (tty->termios != tty->driver->termios[idx]) {
> -			printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
> -			       "for (%s)\n",
> -			       idx, tty->name);
> -			return;
> -		}
> -		if (tty->termios_locked != tty->driver->termios_locked[idx]) {
> -			printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
> -			       "termios_locked for (%s)\n",
> -			       idx, tty->name);
> -			return;
> -		}
> -	}
> -#endif
> -
> -#ifdef TTY_DEBUG_HANGUP
> -	printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",
> -	       tty_name(tty, buf), tty->count);
> -#endif
> -
> -#ifdef TTY_PARANOIA_CHECK
> -	if (tty->driver->other &&
> -	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
> -		if (o_tty != tty->driver->other->ttys[idx]) {
> -			printk(KERN_DEBUG "release_dev: other->table[%d] "
> -					  "not o_tty for (%s)\n",
> -			       idx, tty->name);
> -			return;
> -		}
> -		if (o_tty->termios != tty->driver->other->termios[idx]) {
> -			printk(KERN_DEBUG "release_dev: other->termios[%d] "
> -					  "not o_termios for (%s)\n",
> -			       idx, tty->name);
> -			return;
> -		}
> -		if (o_tty->termios_locked != 
> -		      tty->driver->other->termios_locked[idx]) {
> -			printk(KERN_DEBUG "release_dev: other->termios_locked["
> -					  "%d] not o_termios_locked for (%s)\n",
> -			       idx, tty->name);
> -			return;
> -		}
> -		if (o_tty->link != tty) {
> -			printk(KERN_DEBUG "release_dev: bad pty pointers\n");
> -			return;
> -		}
> -	}
> -#endif
> -	if (tty->driver->close)
> -		tty->driver->close(tty, filp);
> -
> -	/*
> -	 * Sanity check: if tty->count is going to zero, there shouldn't be
> -	 * any waiters on tty->read_wait or tty->write_wait.  We test the
> -	 * wait queues and kick everyone out _before_ actually starting to
> -	 * close.  This ensures that we won't block while releasing the tty
> -	 * structure.
> -	 *
> -	 * The test for the o_tty closing is necessary, since the master and
> -	 * slave sides may close in any order.  If the slave side closes out
> -	 * first, its count will be one, since the master side holds an open.
> -	 * Thus this test wouldn't be triggered at the time the slave closes,
> -	 * so we do it now.
> -	 *
> -	 * Note that it's possible for the tty to be opened again while we're
> -	 * flushing out waiters.  By recalculating the closing flags before
> -	 * each iteration we avoid any problems.
> -	 */
> -	while (1) {
> -		/* Guard against races with tty->count changes elsewhere and
> -		   opens on /dev/tty */
> -		   
> -		down(&tty_sem);
> -		tty_closing = tty->count <= 1;
> -		o_tty_closing = o_tty &&
> -			(o_tty->count <= (pty_master ? 1 : 0));
> -		do_sleep = 0;
> -
> -		if (tty_closing) {
> -			if (waitqueue_active(&tty->read_wait)) {
> -				wake_up(&tty->read_wait);
> -				do_sleep++;
> -			}
> -			if (waitqueue_active(&tty->write_wait)) {
> -				wake_up(&tty->write_wait);
> -				do_sleep++;
> -			}
> -		}
> -		if (o_tty_closing) {
> -			if (waitqueue_active(&o_tty->read_wait)) {
> -				wake_up(&o_tty->read_wait);
> -				do_sleep++;
> -			}
> -			if (waitqueue_active(&o_tty->write_wait)) {
> -				wake_up(&o_tty->write_wait);
> -				do_sleep++;
> -			}
> -		}
> -		if (!do_sleep)
> -			break;
> -
> -		printk(KERN_WARNING "release_dev: %s: read/write wait queue "
> -				    "active!\n", tty_name(tty, buf));
> -		up(&tty_sem);
> -		schedule();
> -	}	
> -
> -	/*
> -	 * The closing flags are now consistent with the open counts on 
> -	 * both sides, and we've completed the last operation that could 
> -	 * block, so it's safe to proceed with closing.
> -	 */
> -	if (pty_master) {
> -		if (--o_tty->count < 0) {
> -			printk(KERN_WARNING "release_dev: bad pty slave count "
> -					    "(%d) for %s\n",
> -			       o_tty->count, tty_name(o_tty, buf));
> -			o_tty->count = 0;
> -		}
> -	}
> -	if (--tty->count < 0) {
> -		printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",
> -		       tty->count, tty_name(tty, buf));
> -		tty->count = 0;
> -	}
> -	
> -	/*
> -	 * We've decremented tty->count, so we need to remove this file
> -	 * descriptor off the tty->tty_files list; this serves two
> -	 * purposes:
> -	 *  - check_tty_count sees the correct number of file descriptors
> -	 *    associated with this tty.
> -	 *  - do_tty_hangup no longer sees this file descriptor as
> -	 *    something that needs to be handled for hangups.
> -	 */
> -	file_kill(filp);
> -	filp->private_data = NULL;
> -
> -	/*
> -	 * Perform some housekeeping before deciding whether to return.
> -	 *
> -	 * Set the TTY_CLOSING flag if this was the last open.  In the
> -	 * case of a pty we may have to wait around for the other side
> -	 * to close, and TTY_CLOSING makes sure we can't be reopened.
> -	 */
> -	if(tty_closing)
> -		set_bit(TTY_CLOSING, &tty->flags);
> -	if(o_tty_closing)
> -		set_bit(TTY_CLOSING, &o_tty->flags);
> -
> -	/*
> -	 * If _either_ side is closing, make sure there aren't any
> -	 * processes that still think tty or o_tty is their controlling
> -	 * tty.
> -	 */
> -	if (tty_closing || o_tty_closing) {
> -		struct task_struct *p;
> -
> -		read_lock(&tasklist_lock);
> -		do_each_task_pid(tty->session, PIDTYPE_SID, p) {
> -			p->signal->tty = NULL;
> -		} while_each_task_pid(tty->session, PIDTYPE_SID, p);
> -		if (o_tty)
> -			do_each_task_pid(o_tty->session, PIDTYPE_SID, p) {
> -				p->signal->tty = NULL;
> -			} while_each_task_pid(o_tty->session, PIDTYPE_SID, p);
> -		read_unlock(&tasklist_lock);
> -	}
> -
> -	up(&tty_sem);
> -
> -	/* check whether both sides are closing ... */
> -	if (!tty_closing || (o_tty && !o_tty_closing))
> -		return;
> -	
> -#ifdef TTY_DEBUG_HANGUP
> -	printk(KERN_DEBUG "freeing tty structure...");
> -#endif
> -	/*
> -	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
> -	 * kill any delayed work. As this is the final close it does not
> -	 * race with the set_ldisc code path.
> -	 */
> -	clear_bit(TTY_LDISC, &tty->flags);
> -	clear_bit(TTY_DONT_FLIP, &tty->flags);
> -	cancel_delayed_work(&tty->buf.work);
> -
> -	/*
> -	 * Wait for ->hangup_work and ->buf.work handlers to terminate
> -	 */
> -	 
> -	flush_scheduled_work();
> -	
> -	/*
> -	 * Wait for any short term users (we know they are just driver
> -	 * side waiters as the file is closing so user count on the file
> -	 * side is zero.
> -	 */
> -	spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	while(tty->ldisc.refcount)
> -	{
> -		spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -		wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
> -		spin_lock_irqsave(&tty_ldisc_lock, flags);
> -	}
> -	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
> -	/*
> -	 * Shutdown the current line discipline, and reset it to N_TTY.
> -	 * N.B. why reset ldisc when we're releasing the memory??
> -	 *
> -	 * FIXME: this MUST get fixed for the new reflocking
> -	 */
> -	if (tty->ldisc.close)
> -		(tty->ldisc.close)(tty);
> -	tty_ldisc_put(tty->ldisc.num);
> -	
> -	/*
> -	 *	Switch the line discipline back
> -	 */
> -	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
> -	tty_set_termios_ldisc(tty,N_TTY); 
> -	if (o_tty) {
> -		/* FIXME: could o_tty be in setldisc here ? */
> -		clear_bit(TTY_LDISC, &o_tty->flags);
> -		if (o_tty->ldisc.close)
> -			(o_tty->ldisc.close)(o_tty);
> -		tty_ldisc_put(o_tty->ldisc.num);
> -		tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
> -		tty_set_termios_ldisc(o_tty,N_TTY); 
> -	}
> -	/*
> -	 * The release_mem function takes care of the details of clearing
> -	 * the slots and preserving the termios structure.
> -	 */
> -	release_mem(tty, idx);
> -
> -#ifdef CONFIG_UNIX98_PTYS
> -	/* Make this pty number available for reallocation */
> -	if (devpts) {
> -		down(&allocated_ptys_lock);
> -		idr_remove(&allocated_ptys, idx);
> -		up(&allocated_ptys_lock);
> -	}
> -#endif
> -
> -}
> -
> -/*
> - * tty_open and tty_release keep up the tty count that contains the
> - * number of opens done on a tty. We cannot use the inode-count, as
> - * different inodes might point to the same tty.
> - *
> - * Open-counting is needed for pty masters, as well as for keeping
> - * track of serial lines: DTR is dropped when the last close happens.
> - * (This is not done solely through tty->count, now.  - Ted 1/27/92)
> - *
> - * The termios state of a pty is reset on first open so that
> - * settings don't persist across reuse.
> - */
> -static int tty_open(struct inode * inode, struct file * filp)
> -{
> -	struct tty_struct *tty;
> -	int noctty, retval;
> -	struct tty_driver *driver;
> -	int index;
> -	dev_t device = inode->i_rdev;
> -	unsigned short saved_flags = filp->f_flags;
> -
> -	nonseekable_open(inode, filp);
> -	
> -retry_open:
> -	noctty = filp->f_flags & O_NOCTTY;
> -	index  = -1;
> -	retval = 0;
> -	
> -	down(&tty_sem);
> -
> -	if (device == MKDEV(TTYAUX_MAJOR,0)) {
> -		if (!current->signal->tty) {
> -			up(&tty_sem);
> -			return -ENXIO;
> -		}
> -		driver = current->signal->tty->driver;
> -		index = current->signal->tty->index;
> -		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
> -		/* noctty = 1; */
> -		goto got_driver;
> -	}
> -#ifdef CONFIG_VT
> -	if (console_use_vt && (device == MKDEV(TTY_MAJOR,0))) {
> -		extern struct tty_driver *console_driver;
> -		driver = console_driver;
> -		index = fg_console;
> -		noctty = 1;
> -		goto got_driver;
> -	}
> -#endif
> -	if (device == MKDEV(TTYAUX_MAJOR,1)) {
> -		driver = console_device(&index);
> -		if (driver) {
> -			/* Don't let /dev/console block */
> -			filp->f_flags |= O_NONBLOCK;
> -			noctty = 1;
> -			goto got_driver;
> -		}
> -		up(&tty_sem);
> -		return -ENODEV;
> -	}
> -
> -	driver = get_tty_driver(device, &index);
> -	if (!driver) {
> -		up(&tty_sem);
> -		return -ENODEV;
> -	}
> -got_driver:
> -	retval = init_dev(driver, index, &tty);
> -	up(&tty_sem);
> -	if (retval)
> -		return retval;
> -
> -	filp->private_data = tty;
> -	file_move(filp, &tty->tty_files);
> -	check_tty_count(tty, "tty_open");
> -	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
> -	    tty->driver->subtype == PTY_TYPE_MASTER)
> -		noctty = 1;
> -#ifdef TTY_DEBUG_HANGUP
> -	printk(KERN_DEBUG "opening %s...", tty->name);
> -#endif
> -	if (!retval) {
> -		if (tty->driver->open)
> -			retval = tty->driver->open(tty, filp);
> -		else
> -			retval = -ENODEV;
> -	}
> -	filp->f_flags = saved_flags;
> -
> -	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
> -		retval = -EBUSY;
> -
> -	if (retval) {
> -#ifdef TTY_DEBUG_HANGUP
> -		printk(KERN_DEBUG "error %d in opening %s...", retval,
> -		       tty->name);
> -#endif
> -		release_dev(filp);
> -		if (retval != -ERESTARTSYS)
> -			return retval;
> -		if (signal_pending(current))
> -			return retval;
> -		schedule();
> -		/*
> -		 * Need to reset f_op in case a hangup happened.
> -		 */
> -		if (filp->f_op == &hung_up_tty_fops)
> -			filp->f_op = &tty_fops;
> -		goto retry_open;
> -	}
> -	if (!noctty &&
> -	    current->signal->leader &&
> -	    !current->signal->tty &&
> -	    tty->session == 0) {
> -	    	task_lock(current);
> -		current->signal->tty = tty;
> -		task_unlock(current);
> -		current->signal->tty_old_pgrp = 0;
> -		tty->session = current->signal->session;
> -		tty->pgrp = process_group(current);
> -	}
> -	return 0;
> -}
> -
> -#ifdef CONFIG_UNIX98_PTYS
> -static int ptmx_open(struct inode * inode, struct file * filp)
> -{
> -	struct tty_struct *tty;
> -	int retval;
> -	int index;
> -	int idr_ret;
> -
> -	nonseekable_open(inode, filp);
> -
> -	/* find a device that is not in use. */
> -	down(&allocated_ptys_lock);
> -	if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
> -		up(&allocated_ptys_lock);
> -		return -ENOMEM;
> -	}
> -	idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
> -	if (idr_ret < 0) {
> -		up(&allocated_ptys_lock);
> -		if (idr_ret == -EAGAIN)
> -			return -ENOMEM;
> -		return -EIO;
> -	}
> -	if (index >= pty_limit) {
> -		idr_remove(&allocated_ptys, index);
> -		up(&allocated_ptys_lock);
> -		return -EIO;
> -	}
> -	up(&allocated_ptys_lock);
> -
> -	down(&tty_sem);
> -	retval = init_dev(ptm_driver, index, &tty);
> -	up(&tty_sem);
> -	
> -	if (retval)
> -		goto out;
> -
> -	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
> -	filp->private_data = tty;
> -	file_move(filp, &tty->tty_files);
> -
> -	retval = -ENOMEM;
> -	if (devpts_pty_new(tty->link))
> -		goto out1;
> -
> -	check_tty_count(tty, "tty_open");
> -	retval = ptm_driver->open(tty, filp);
> -	if (!retval)
> -		return 0;
> -out1:
> -	release_dev(filp);
> -out:
> -	down(&allocated_ptys_lock);
> -	idr_remove(&allocated_ptys, index);
> -	up(&allocated_ptys_lock);
> -	return retval;
> -}
> -#endif
> -
> -static int tty_release(struct inode * inode, struct file * filp)
> -{
> -	lock_kernel();
> -	release_dev(filp);
> -	unlock_kernel();
> -	return 0;
> -}
> -
> -/* No kernel lock held - fine */
> -static unsigned int tty_poll(struct file * filp, poll_table * wait)
> -{
> -	struct tty_struct * tty;
> -	struct tty_ldisc *ld;
> -	int ret = 0;
> -
> -	tty = (struct tty_struct *)filp->private_data;
> -	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll"))
> -		return 0;
> -		
> -	ld = tty_ldisc_ref_wait(tty);
> -	if (ld->poll)
> -		ret = (ld->poll)(tty, filp, wait);
> -	tty_ldisc_deref(ld);
> -	return ret;
> -}
> -
> -static int tty_fasync(int fd, struct file * filp, int on)
> -{
> -	struct tty_struct * tty;
> -	int retval;
> -
> -	tty = (struct tty_struct *)filp->private_data;
> -	if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_fasync"))
> -		return 0;
> -	
> -	retval = fasync_helper(fd, filp, on, &tty->fasync);
> -	if (retval <= 0)
> -		return retval;
> -
> -	if (on) {
> -		if (!waitqueue_active(&tty->read_wait))
> -			tty->minimum_to_wake = 1;
> -		retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0);
> -		if (retval)
> -			return retval;
> -	} else {
> -		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
> -			tty->minimum_to_wake = N_TTY_BUF_SIZE;
> -	}
> -	return 0;
> -}
> -
> -static int tiocsti(struct tty_struct *tty, char __user *p)
> -{
> -	char ch, mbz = 0;
> -	struct tty_ldisc *ld;
> -	
> -	if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
> -		return -EPERM;
> -	if (get_user(ch, p))
> -		return -EFAULT;
> -	ld = tty_ldisc_ref_wait(tty);
> -	ld->receive_buf(tty, &ch, &mbz, 1);
> -	tty_ldisc_deref(ld);
> -	return 0;
> -}
> -
> -static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
> -{
> -	if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
> -		return -EFAULT;
> -	return 0;
> -}
> -
> -static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
> -	struct winsize __user * arg)
> -{
> -	struct winsize tmp_ws;
> -
> -	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
> -		return -EFAULT;
> -	if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
> -		return 0;
> -#ifdef CONFIG_VT
> -	if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
> -		int rc;
> -
> -		acquire_console_sem();
> -		rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row);
> -		release_console_sem();
> -		if (rc)
> -			return -ENXIO;
> -	}
> -#endif
> -	if (tty->pgrp > 0)
> -		kill_pg(tty->pgrp, SIGWINCH, 1);
> -	if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))
> -		kill_pg(real_tty->pgrp, SIGWINCH, 1);
> -	tty->winsize = tmp_ws;
> -	real_tty->winsize = tmp_ws;
> -	return 0;
> -}
> -
> -static int tioccons(struct file *file)
> -{
> -	if (!capable(CAP_SYS_ADMIN))
> -		return -EPERM;
> -	if (file->f_op->write == redirected_tty_write) {
> -		struct file *f;
> -		spin_lock(&redirect_lock);
> -		f = redirect;
> -		redirect = NULL;
> -		spin_unlock(&redirect_lock);
> -		if (f)
> -			fput(f);
> -		return 0;
> -	}
> -	spin_lock(&redirect_lock);
> -	if (redirect) {
> -		spin_unlock(&redirect_lock);
> -		return -EBUSY;
> -	}
> -	get_file(file);
> -	redirect = file;
> -	spin_unlock(&redirect_lock);
> -	return 0;
> -}
> -
> -
> -static int fionbio(struct file *file, int __user *p)
> -{
> -	int nonblock;
> -
> -	if (get_user(nonblock, p))
> -		return -EFAULT;
> -
> -	if (nonblock)
> -		file->f_flags |= O_NONBLOCK;
> -	else
> -		file->f_flags &= ~O_NONBLOCK;
> -	return 0;
> -}
> -
> -static int tiocsctty(struct tty_struct *tty, int arg)
> -{
> -	task_t *p;
> -
> -	if (current->signal->leader &&
> -	    (current->signal->session == tty->session))
> -		return 0;
> -	/*
> -	 * The process must be a session leader and
> -	 * not have a controlling tty already.
> -	 */
> -	if (!current->signal->leader || current->signal->tty)
> -		return -EPERM;
> -	if (tty->session > 0) {
> -		/*
> -		 * This tty is already the controlling
> -		 * tty for another session group!
> -		 */
> -		if ((arg == 1) && capable(CAP_SYS_ADMIN)) {
> -			/*
> -			 * Steal it away
> -			 */
> -
> -			read_lock(&tasklist_lock);
> -			do_each_task_pid(tty->session, PIDTYPE_SID, p) {
> -				p->signal->tty = NULL;
> -			} while_each_task_pid(tty->session, PIDTYPE_SID, p);
> -			read_unlock(&tasklist_lock);
> -		} else
> -			return -EPERM;
> -	}
> -	task_lock(current);
> -	current->signal->tty = tty;
> -	task_unlock(current);
> -	current->signal->tty_old_pgrp = 0;
> -	tty->session = current->signal->session;
> -	tty->pgrp = process_group(current);
> -	return 0;
> -}
> -
> -static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
> -{
> -	/*
> -	 * (tty == real_tty) is a cheap way of
> -	 * testing if the tty is NOT a master pty.
> -	 */
> -	if (tty == real_tty && current->signal->tty != real_tty)
> -		return -ENOTTY;
> -	return put_user(real_tty->pgrp, p);
> -}
> -
> -static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
> -{
> -	pid_t pgrp;
> -	int retval = tty_check_change(real_tty);
> -
> -	if (retval == -EIO)
> -		return -ENOTTY;
> -	if (retval)
> -		return retval;
> -	if (!current->signal->tty ||
> -	    (current->signal->tty != real_tty) ||
> -	    (real_tty->session != current->signal->session))
> -		return -ENOTTY;
> -	if (get_user(pgrp, p))
> -		return -EFAULT;
> -	if (pgrp < 0)
> -		return -EINVAL;
> -	if (session_of_pgrp(pgrp) != current->signal->session)
> -		return -EPERM;
> -	real_tty->pgrp = pgrp;
> -	return 0;
> -}
> -
> -static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
> -{
> -	/*
> -	 * (tty == real_tty) is a cheap way of
> -	 * testing if the tty is NOT a master pty.
> -	*/
> -	if (tty == real_tty && current->signal->tty != real_tty)
> -		return -ENOTTY;
> -	if (real_tty->session <= 0)
> -		return -ENOTTY;
> -	return put_user(real_tty->session, p);
> -}
> -
> -static int tiocsetd(struct tty_struct *tty, int __user *p)
> -{
> -	int ldisc;
> -
> -	if (get_user(ldisc, p))
> -		return -EFAULT;
> -	return tty_set_ldisc(tty, ldisc);
> -}
> -
> -static int send_break(struct tty_struct *tty, unsigned int duration)
> -{
> -	tty->driver->break_ctl(tty, -1);
> -	if (!signal_pending(current)) {
> -		msleep_interruptible(duration);
> -	}
> -	tty->driver->break_ctl(tty, 0);
> -	if (signal_pending(current))
> -		return -EINTR;
> -	return 0;
> -}
> -
> -static int
> -tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
> -{
> -	int retval = -EINVAL;
> -
> -	if (tty->driver->tiocmget) {
> -		retval = tty->driver->tiocmget(tty, file);
> -
> -		if (retval >= 0)
> -			retval = put_user(retval, p);
> -	}
> -	return retval;
> -}
> -
> -static int
> -tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
> -	     unsigned __user *p)
> -{
> -	int retval = -EINVAL;
> -
> -	if (tty->driver->tiocmset) {
> -		unsigned int set, clear, val;
> -
> -		retval = get_user(val, p);
> -		if (retval)
> -			return retval;
> -
> -		set = clear = 0;
> -		switch (cmd) {
> -		case TIOCMBIS:
> -			set = val;
> -			break;
> -		case TIOCMBIC:
> -			clear = val;
> -			break;
> -		case TIOCMSET:
> -			set = val;
> -			clear = ~val;
> -			break;
> -		}
> -
> -		set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
> -		clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
> -
> -		retval = tty->driver->tiocmset(tty, file, set, clear);
> -	}
> -	return retval;
> -}
> -
> -/*
> - * Split this up, as gcc can choke on it otherwise..
> - */
> -int tty_ioctl(struct inode * inode, struct file * file,
> -	      unsigned int cmd, unsigned long arg)
> -{
> -	struct tty_struct *tty, *real_tty;
> -	void __user *p = (void __user *)arg;
> -	int retval;
> -	struct tty_ldisc *ld;
> -	
> -	tty = (struct tty_struct *)file->private_data;
> -	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
> -		return -EINVAL;
> -
> -	real_tty = tty;
> -	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
> -	    tty->driver->subtype == PTY_TYPE_MASTER)
> -		real_tty = tty->link;
> -
> -	/*
> -	 * Break handling by driver
> -	 */
> -	if (!tty->driver->break_ctl) {
> -		switch(cmd) {
> -		case TIOCSBRK:
> -		case TIOCCBRK:
> -			if (tty->driver->ioctl)
> -				return tty->driver->ioctl(tty, file, cmd, arg);
> -			return -EINVAL;
> -			
> -		/* These two ioctl's always return success; even if */
> -		/* the driver doesn't support them. */
> -		case TCSBRK:
> -		case TCSBRKP:
> -			if (!tty->driver->ioctl)
> -				return 0;
> -			retval = tty->driver->ioctl(tty, file, cmd, arg);
> -			if (retval == -ENOIOCTLCMD)
> -				retval = 0;
> -			return retval;
> -		}
> -	}
> -
> -	/*
> -	 * Factor out some common prep work
> -	 */
> -	switch (cmd) {
> -	case TIOCSETD:
> -	case TIOCSBRK:
> -	case TIOCCBRK:
> -	case TCSBRK:
> -	case TCSBRKP:			
> -		retval = tty_check_change(tty);
> -		if (retval)
> -			return retval;
> -		if (cmd != TIOCCBRK) {
> -			tty_wait_until_sent(tty, 0);
> -			if (signal_pending(current))
> -				return -EINTR;
> -		}
> -		break;
> -	}
> -
> -	switch (cmd) {
> -		case TIOCSTI:
> -			return tiocsti(tty, p);
> -		case TIOCGWINSZ:
> -			return tiocgwinsz(tty, p);
> -		case TIOCSWINSZ:
> -			return tiocswinsz(tty, real_tty, p);
> -		case TIOCCONS:
> -			return real_tty!=tty ? -EINVAL : tioccons(file);
> -		case FIONBIO:
> -			return fionbio(file, p);
> -		case TIOCEXCL:
> -			set_bit(TTY_EXCLUSIVE, &tty->flags);
> -			return 0;
> -		case TIOCNXCL:
> -			clear_bit(TTY_EXCLUSIVE, &tty->flags);
> -			return 0;
> -		case TIOCNOTTY:
> -			if (current->signal->tty != tty)
> -				return -ENOTTY;
> -			if (current->signal->leader)
> -				disassociate_ctty(0);
> -			task_lock(current);
> -			current->signal->tty = NULL;
> -			task_unlock(current);
> -			return 0;
> -		case TIOCSCTTY:
> -			return tiocsctty(tty, arg);
> -		case TIOCGPGRP:
> -			return tiocgpgrp(tty, real_tty, p);
> -		case TIOCSPGRP:
> -			return tiocspgrp(tty, real_tty, p);
> -		case TIOCGSID:
> -			return tiocgsid(tty, real_tty, p);
> -		case TIOCGETD:
> -			/* FIXME: check this is ok */
> -			return put_user(tty->ldisc.num, (int __user *)p);
> -		case TIOCSETD:
> -			return tiocsetd(tty, p);
> -#ifdef CONFIG_VT
> -		case TIOCLINUX:
> -			return tioclinux(tty, arg);
> -#endif
> -		/*
> -		 * Break handling
> -		 */
> -		case TIOCSBRK:	/* Turn break on, unconditionally */
> -			tty->driver->break_ctl(tty, -1);
> -			return 0;
> -			
> -		case TIOCCBRK:	/* Turn break off, unconditionally */
> -			tty->driver->break_ctl(tty, 0);
> -			return 0;
> -		case TCSBRK:   /* SVID version: non-zero arg --> no break */
> -			/*
> -			 * XXX is the above comment correct, or the
> -			 * code below correct?  Is this ioctl used at
> -			 * all by anyone?
> -			 */
> -			if (!arg)
> -				return send_break(tty, 250);
> -			return 0;
> -		case TCSBRKP:	/* support for POSIX tcsendbreak() */	
> -			return send_break(tty, arg ? arg*100 : 250);
> -
> -		case TIOCMGET:
> -			return tty_tiocmget(tty, file, p);
> -
> -		case TIOCMSET:
> -		case TIOCMBIC:
> -		case TIOCMBIS:
> -			return tty_tiocmset(tty, file, cmd, p);
> -	}
> -	if (tty->driver->ioctl) {
> -		retval = (tty->driver->ioctl)(tty, file, cmd, arg);
> -		if (retval != -ENOIOCTLCMD)
> -			return retval;
> -	}
> -	ld = tty_ldisc_ref_wait(tty);
> -	retval = -EINVAL;
> -	if (ld->ioctl) {
> -		retval = ld->ioctl(tty, file, cmd, arg);
> -		if (retval == -ENOIOCTLCMD)
> -			retval = -EINVAL;
> -	}
> -	tty_ldisc_deref(ld);
> -	return retval;
> -}
> -
> -
> -/*
> - * This implements the "Secure Attention Key" ---  the idea is to
> - * prevent trojan horses by killing all processes associated with this
> - * tty when the user hits the "Secure Attention Key".  Required for
> - * super-paranoid applications --- see the Orange Book for more details.
> - * 
> - * This code could be nicer; ideally it should send a HUP, wait a few
> - * seconds, then send a INT, and then a KILL signal.  But you then
> - * have to coordinate with the init process, since all processes associated
> - * with the current tty must be dead before the new getty is allowed
> - * to spawn.
> - *
> - * Now, if it would be correct ;-/ The current code has a nasty hole -
> - * it doesn't catch files in flight. We may send the descriptor to ourselves
> - * via AF_UNIX socket, close it and later fetch from socket. FIXME.
> - *
> - * Nasty bug: do_SAK is being called in interrupt context.  This can
> - * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
> - */
> -static void __do_SAK(void *arg)
> -{
> -#ifdef TTY_SOFT_SAK
> -	tty_hangup(tty);
> -#else
> -	struct tty_struct *tty = arg;
> -	struct task_struct *p;
> -	int session;
> -	int		i;
> -	struct file	*filp;
> -	struct tty_ldisc *disc;
> -	struct fdtable *fdt;
> -	
> -	if (!tty)
> -		return;
> -	session  = tty->session;
> -	
> -	/* We don't want an ldisc switch during this */
> -	disc = tty_ldisc_ref(tty);
> -	if (disc && disc->flush_buffer)
> -		disc->flush_buffer(tty);
> -	tty_ldisc_deref(disc);
> -
> -	if (tty->driver->flush_buffer)
> -		tty->driver->flush_buffer(tty);
> -	
> -	read_lock(&tasklist_lock);
> -	do_each_task_pid(session, PIDTYPE_SID, p) {
> -		if (p->signal->tty == tty || session > 0) {
> -			printk(KERN_NOTICE "SAK: killed process %d"
> -			    " (%s): p->signal->session==tty->session\n",
> -			    p->pid, p->comm);
> -			send_sig(SIGKILL, p, 1);
> -			continue;
> -		}
> -		task_lock(p);
> -		if (p->files) {
> -			/*
> -			 * We don't take a ref to the file, so we must
> -			 * hold ->file_lock instead.
> -			 */
> -			spin_lock(&p->files->file_lock);
> -			fdt = files_fdtable(p->files);
> -			for (i=0; i < fdt->max_fds; i++) {
> -				filp = fcheck_files(p->files, i);
> -				if (!filp)
> -					continue;
> -				if (filp->f_op->read == tty_read &&
> -				    filp->private_data == tty) {
> -					printk(KERN_NOTICE "SAK: killed process %d"
> -					    " (%s): fd#%d opened to the tty\n",
> -					    p->pid, p->comm, i);
> -					send_sig(SIGKILL, p, 1);
> -					break;
> -				}
> -			}
> -			spin_unlock(&p->files->file_lock);
> -		}
> -		task_unlock(p);
> -	} while_each_task_pid(session, PIDTYPE_SID, p);
> -	read_unlock(&tasklist_lock);
> -#endif
> -}
> -
> -/*
> - * The tq handling here is a little racy - tty->SAK_work may already be queued.
> - * Fortunately we don't need to worry, because if ->SAK_work is already queued,
> - * the values which we write to it will be identical to the values which it
> - * already has. --akpm
> - */
> -void do_SAK(struct tty_struct *tty)
> -{
> -	if (!tty)
> -		return;
> -	PREPARE_WORK(&tty->SAK_work, __do_SAK, tty);
> -	schedule_work(&tty->SAK_work);
> -}
> -
> -EXPORT_SYMBOL(do_SAK);
> -
> -/*
> - * This routine is called out of the software interrupt to flush data
> - * from the buffer chain to the line discipline.
> - */
> - 
> -static void flush_to_ldisc(void *private_)
> -{
> -	struct tty_struct *tty = (struct tty_struct *) private_;
> -	unsigned long 	flags;
> -	struct tty_ldisc *disc;
> -	struct tty_buffer *tbuf, *head;
> -	int count;
> -	char *char_buf;
> -	unsigned char *flag_buf;
> -
> -	disc = tty_ldisc_ref(tty);
> -	if (disc == NULL)	/*  !TTY_LDISC */
> -		return;
> -
> -	if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
> -		/*
> -		 * Do it after the next timer tick:
> -		 */
> -		schedule_delayed_work(&tty->buf.work, 1);
> -		goto out;
> -	}
> -	spin_lock_irqsave(&tty->buf.lock, flags);
> -	head = tty->buf.head;
> -	tty->buf.head = NULL;
> -	while((tbuf = head) != NULL) {
> -		while ((count = tbuf->commit - tbuf->read) != 0) {
> -			char_buf = tbuf->char_buf_ptr + tbuf->read;
> -			flag_buf = tbuf->flag_buf_ptr + tbuf->read;
> -			tbuf->read += count;
> -			spin_unlock_irqrestore(&tty->buf.lock, flags);
> -			disc->receive_buf(tty, char_buf, flag_buf, count);
> -			spin_lock_irqsave(&tty->buf.lock, flags);
> -		}
> -		if (tbuf->active) {
> -			tty->buf.head = head;
> -			break;
> -		}
> -		head = tbuf->next;
> -		if (head == NULL)
> -			tty->buf.tail = NULL;
> -		tty_buffer_free(tty, tbuf);
> -	}
> -	spin_unlock_irqrestore(&tty->buf.lock, flags);
> -out:
> -	tty_ldisc_deref(disc);
> -}
> -
> -/*
> - * Routine which returns the baud rate of the tty
> - *
> - * Note that the baud_table needs to be kept in sync with the
> - * include/asm/termbits.h file.
> - */
> -static int baud_table[] = {
> -	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
> -	9600, 19200, 38400, 57600, 115200, 230400, 460800,
> -#ifdef __sparc__
> -	76800, 153600, 307200, 614400, 921600
> -#else
> -	500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
> -	2500000, 3000000, 3500000, 4000000
> -#endif
> -};
> -
> -static int n_baud_table = ARRAY_SIZE(baud_table);
> -
> -/**
> - *	tty_termios_baud_rate
> - *	@termios: termios structure
> - *
> - *	Convert termios baud rate data into a speed. This should be called
> - *	with the termios lock held if this termios is a terminal termios
> - *	structure. May change the termios data.
> - */
> - 
> -int tty_termios_baud_rate(struct termios *termios)
> -{
> -	unsigned int cbaud;
> -	
> -	cbaud = termios->c_cflag & CBAUD;
> -
> -	if (cbaud & CBAUDEX) {
> -		cbaud &= ~CBAUDEX;
> -
> -		if (cbaud < 1 || cbaud + 15 > n_baud_table)
> -			termios->c_cflag &= ~CBAUDEX;
> -		else
> -			cbaud += 15;
> -	}
> -	return baud_table[cbaud];
> -}
> -
> -EXPORT_SYMBOL(tty_termios_baud_rate);
> -
> -/**
> - *	tty_get_baud_rate	-	get tty bit rates
> - *	@tty: tty to query
> - *
> - *	Returns the baud rate as an integer for this terminal. The
> - *	termios lock must be held by the caller and the terminal bit
> - *	flags may be updated.
> - */
> - 
> -int tty_get_baud_rate(struct tty_struct *tty)
> -{
> -	int baud = tty_termios_baud_rate(tty->termios);
> -
> -	if (baud == 38400 && tty->alt_speed) {
> -		if (!tty->warned) {
> -			printk(KERN_WARNING "Use of setserial/setrocket to "
> -					    "set SPD_* flags is deprecated\n");
> -			tty->warned = 1;
> -		}
> -		baud = tty->alt_speed;
> -	}
> -	
> -	return baud;
> -}
> -
> -EXPORT_SYMBOL(tty_get_baud_rate);
> -
> -/**
> - *	tty_flip_buffer_push	-	terminal
> - *	@tty: tty to push
> - *
> - *	Queue a push of the terminal flip buffers to the line discipline. This
> - *	function must not be called from IRQ context if tty->low_latency is set.
> - *
> - *	In the event of the queue being busy for flipping the work will be
> - *	held off and retried later.
> - */
> -
> -void tty_flip_buffer_push(struct tty_struct *tty)
> -{
> -	unsigned long flags;
> -	spin_lock_irqsave(&tty->buf.lock, flags);
> -	if (tty->buf.tail != NULL) {
> -		tty->buf.tail->active = 0;
> -		tty->buf.tail->commit = tty->buf.tail->used;
> -	}
> -	spin_unlock_irqrestore(&tty->buf.lock, flags);
> -
> -	if (tty->low_latency)
> -		flush_to_ldisc((void *) tty);
> -	else
> -		schedule_delayed_work(&tty->buf.work, 1);
> -}
> -
> -EXPORT_SYMBOL(tty_flip_buffer_push);
> -
> -
> -/*
> - * This subroutine initializes a tty structure.
> - */
> -static void initialize_tty_struct(struct tty_struct *tty)
> -{
> -	memset(tty, 0, sizeof(struct tty_struct));
> -	tty->magic = TTY_MAGIC;
> -	tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
> -	tty->pgrp = -1;
> -	tty->overrun_time = jiffies;
> -	tty->buf.head = tty->buf.tail = NULL;
> -	tty_buffer_init(tty);
> -	INIT_WORK(&tty->buf.work, flush_to_ldisc, tty);
> -	init_MUTEX(&tty->buf.pty_sem);
> -	init_MUTEX(&tty->termios_sem);
> -	init_waitqueue_head(&tty->write_wait);
> -	init_waitqueue_head(&tty->read_wait);
> -	INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
> -	sema_init(&tty->atomic_read, 1);
> -	sema_init(&tty->atomic_write, 1);
> -	spin_lock_init(&tty->read_lock);
> -	INIT_LIST_HEAD(&tty->tty_files);
> -	INIT_WORK(&tty->SAK_work, NULL, NULL);
> -}
> -
> -/*
> - * The default put_char routine if the driver did not define one.
> - */
> -static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
> -{
> -	tty->driver->write(tty, &ch, 1);
> -}
> -
> -static struct class *tty_class;
> -
> -/**
> - * tty_register_device - register a tty device
> - * @driver: the tty driver that describes the tty device
> - * @index: the index in the tty driver for this tty device
> - * @device: a struct device that is associated with this tty device.
> - *	This field is optional, if there is no known struct device for this
> - *	tty device it can be set to NULL safely.
> - *
> - * This call is required to be made to register an individual tty device if
> - * the tty driver's flags have the TTY_DRIVER_NO_DEVFS bit set.  If that
> - * bit is not set, this function should not be called.
> - */
> -void tty_register_device(struct tty_driver *driver, unsigned index,
> -			 struct device *device)
> -{
> -	char name[64];
> -	dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
> -
> -	if (index >= driver->num) {
> -		printk(KERN_ERR "Attempt to register invalid tty line number "
> -		       " (%d).\n", index);
> -		return;
> -	}
> -
> -	devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR,
> -			"%s%d", driver->devfs_name, index + driver->name_base);
> -
> -	if (driver->type == TTY_DRIVER_TYPE_PTY)
> -		pty_line_name(driver, index, name);
> -	else
> -		tty_line_name(driver, index, name);
> -	class_device_create(tty_class, NULL, dev, device, "%s", name);
> -}
> -
> -/**
> - * tty_unregister_device - unregister a tty device
> - * @driver: the tty driver that describes the tty device
> - * @index: the index in the tty driver for this tty device
> - *
> - * If a tty device is registered with a call to tty_register_device() then
> - * this function must be made when the tty device is gone.
> - */
> -void tty_unregister_device(struct tty_driver *driver, unsigned index)
> -{
> -	devfs_remove("%s%d", driver->devfs_name, index + driver->name_base);
> -	class_device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index);
> -}
> -
> -EXPORT_SYMBOL(tty_register_device);
> -EXPORT_SYMBOL(tty_unregister_device);
> -
> -struct tty_driver *alloc_tty_driver(int lines)
> -{
> -	struct tty_driver *driver;
> -
> -	driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
> -	if (driver) {
> -		memset(driver, 0, sizeof(struct tty_driver));
> -		driver->magic = TTY_DRIVER_MAGIC;
> -		driver->num = lines;
> -		/* later we'll move allocation of tables here */
> -	}
> -	return driver;
> -}
> -
> -void put_tty_driver(struct tty_driver *driver)
> -{
> -	kfree(driver);
> -}
> -
> -void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
> -{
> -	driver->open = op->open;
> -	driver->close = op->close;
> -	driver->write = op->write;
> -	driver->put_char = op->put_char;
> -	driver->flush_chars = op->flush_chars;
> -	driver->write_room = op->write_room;
> -	driver->chars_in_buffer = op->chars_in_buffer;
> -	driver->ioctl = op->ioctl;
> -	driver->set_termios = op->set_termios;
> -	driver->throttle = op->throttle;
> -	driver->unthrottle = op->unthrottle;
> -	driver->stop = op->stop;
> -	driver->start = op->start;
> -	driver->hangup = op->hangup;
> -	driver->break_ctl = op->break_ctl;
> -	driver->flush_buffer = op->flush_buffer;
> -	driver->set_ldisc = op->set_ldisc;
> -	driver->wait_until_sent = op->wait_until_sent;
> -	driver->send_xchar = op->send_xchar;
> -	driver->read_proc = op->read_proc;
> -	driver->write_proc = op->write_proc;
> -	driver->tiocmget = op->tiocmget;
> -	driver->tiocmset = op->tiocmset;
> -}
> -
> -
> -EXPORT_SYMBOL(alloc_tty_driver);
> -EXPORT_SYMBOL(put_tty_driver);
> -EXPORT_SYMBOL(tty_set_operations);
> -
> -/*
> - * Called by a tty driver to register itself.
> - */
> -int tty_register_driver(struct tty_driver *driver)
> -{
> -	int error;
> -        int i;
> -	dev_t dev;
> -	void **p = NULL;
> -
> -	if (driver->flags & TTY_DRIVER_INSTALLED)
> -		return 0;
> -
> -	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
> -		p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
> -		if (!p)
> -			return -ENOMEM;
> -		memset(p, 0, driver->num * 3 * sizeof(void *));
> -	}
> -
> -	if (!driver->major) {
> -		error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
> -						(char*)driver->name);
> -		if (!error) {
> -			driver->major = MAJOR(dev);
> -			driver->minor_start = MINOR(dev);
> -		}
> -	} else {
> -		dev = MKDEV(driver->major, driver->minor_start);
> -		error = register_chrdev_region(dev, driver->num,
> -						(char*)driver->name);
> -	}
> -	if (error < 0) {
> -		kfree(p);
> -		return error;
> -	}
> -
> -	if (p) {
> -		driver->ttys = (struct tty_struct **)p;
> -		driver->termios = (struct termios **)(p + driver->num);
> -		driver->termios_locked = (struct termios **)(p + driver->num * 2);
> -	} else {
> -		driver->ttys = NULL;
> -		driver->termios = NULL;
> -		driver->termios_locked = NULL;
> -	}
> -
> -	cdev_init(&driver->cdev, &tty_fops);
> -	driver->cdev.owner = driver->owner;
> -	error = cdev_add(&driver->cdev, dev, driver->num);
> -	if (error) {
> -		cdev_del(&driver->cdev);
> -		unregister_chrdev_region(dev, driver->num);
> -		driver->ttys = NULL;
> -		driver->termios = driver->termios_locked = NULL;
> -		kfree(p);
> -		return error;
> -	}
> -
> -	if (!driver->put_char)
> -		driver->put_char = tty_default_put_char;
> -	
> -	list_add(&driver->tty_drivers, &tty_drivers);
> -	
> -	if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
> -		for(i = 0; i < driver->num; i++)
> -		    tty_register_device(driver, i, NULL);
> -	}
> -	proc_tty_register_driver(driver);
> -	return 0;
> -}
> -
> -EXPORT_SYMBOL(tty_register_driver);
> -
> -/*
> - * Called by a tty driver to unregister itself.
> - */
> -int tty_unregister_driver(struct tty_driver *driver)
> -{
> -	int i;
> -	struct termios *tp;
> -	void *p;
> -
> -	if (driver->refcount)
> -		return -EBUSY;
> -
> -	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
> -				driver->num);
> -
> -	list_del(&driver->tty_drivers);
> -
> -	/*
> -	 * Free the termios and termios_locked structures because
> -	 * we don't want to get memory leaks when modular tty
> -	 * drivers are removed from the kernel.
> -	 */
> -	for (i = 0; i < driver->num; i++) {
> -		tp = driver->termios[i];
> -		if (tp) {
> -			driver->termios[i] = NULL;
> -			kfree(tp);
> -		}
> -		tp = driver->termios_locked[i];
> -		if (tp) {
> -			driver->termios_locked[i] = NULL;
> -			kfree(tp);
> -		}
> -		if (!(driver->flags & TTY_DRIVER_NO_DEVFS))
> -			tty_unregister_device(driver, i);
> -	}
> -	p = driver->ttys;
> -	proc_tty_unregister_driver(driver);
> -	driver->ttys = NULL;
> -	driver->termios = driver->termios_locked = NULL;
> -	kfree(p);
> -	cdev_del(&driver->cdev);
> -	return 0;
> -}
> -
> -EXPORT_SYMBOL(tty_unregister_driver);
> -
> -
> -/*
> - * Initialize the console device. This is called *early*, so
> - * we can't necessarily depend on lots of kernel help here.
> - * Just do some early initializations, and do the complex setup
> - * later.
> - */
> -void __init console_init(void)
> -{
> -	initcall_t *call;
> -
> -	/* Setup the default TTY line discipline. */
> -	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
> -
> -	/*
> -	 * set up the console device so that later boot sequences can 
> -	 * inform about problems etc..
> -	 */
> -#ifdef CONFIG_EARLY_PRINTK
> -	disable_early_printk();
> -#endif
> -	call = __con_initcall_start;
> -	while (call < __con_initcall_end) {
> -		(*call)();
> -		call++;
> -	}
> -}
> -
> -#ifdef CONFIG_VT
> -extern int vty_init(void);
> -#endif
> -
> -static int __init tty_class_init(void)
> -{
> -	tty_class = class_create(THIS_MODULE, "tty");
> -	if (IS_ERR(tty_class))
> -		return PTR_ERR(tty_class);
> -	return 0;
> -}
> -
> -postcore_initcall(tty_class_init);
> -
> -/* 3/2004 jmc: why do these devices exist? */
> -
> -static struct cdev tty_cdev, console_cdev;
> -#ifdef CONFIG_UNIX98_PTYS
> -static struct cdev ptmx_cdev;
> -#endif
> -#ifdef CONFIG_VT
> -static struct cdev vc0_cdev;
> -#endif
> -
> -/*
> - * Ok, now we can initialize the rest of the tty devices and can count
> - * on memory allocations, interrupts etc..
> - */
> -static int __init tty_init(void)
> -{
> -	cdev_init(&tty_cdev, &tty_fops);
> -	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
> -	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
> -		panic("Couldn't register /dev/tty driver\n");
> -	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 0), S_IFCHR|S_IRUGO|S_IWUGO, "tty");
> -	class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
> -
> -	cdev_init(&console_cdev, &console_fops);
> -	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
> -	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
> -		panic("Couldn't register /dev/console driver\n");
> -	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 1), S_IFCHR|S_IRUSR|S_IWUSR, "console");
> -	class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
> -
> -#ifdef CONFIG_UNIX98_PTYS
> -	cdev_init(&ptmx_cdev, &ptmx_fops);
> -	if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
> -	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
> -		panic("Couldn't register /dev/ptmx driver\n");
> -	devfs_mk_cdev(MKDEV(TTYAUX_MAJOR, 2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");
> -	class_device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
> -#endif
> -
> -#ifdef CONFIG_VT
> -	if (!console_use_vt)
> -		goto out_vt;
> -	cdev_init(&vc0_cdev, &console_fops);
> -	if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
> -	    register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
> -		panic("Couldn't register /dev/tty0 driver\n");
> -	devfs_mk_cdev(MKDEV(TTY_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vc/0");
> -	class_device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
> -
> -	vty_init();
> - out_vt:
> -#endif
> -	return 0;
> -}
> -module_init(tty_init);
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xensource.com
> http://lists.xensource.com/xen-devel

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-01 13:25 ` Steven Smith
@ 2006-12-01 18:04   ` Markus Armbruster
  2006-12-04 19:51   ` Markus Armbruster
  2006-12-04 20:29   ` Daniel P. Berrange
  2 siblings, 0 replies; 10+ messages in thread
From: Markus Armbruster @ 2006-12-01 18:04 UTC (permalink / raw)
  To: Steven Smith; +Cc: xen-devel, sos22

Steven Smith <sos22-xen@srcf.ucam.org> writes:

> Applied.

Cool, many thanks!

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-01 13:25 ` Steven Smith
  2006-12-01 18:04   ` Markus Armbruster
@ 2006-12-04 19:51   ` Markus Armbruster
  2006-12-04 20:43     ` Ewan Mellor
  2006-12-05 18:11     ` Steven Smith
  2006-12-04 20:29   ` Daniel P. Berrange
  2 siblings, 2 replies; 10+ messages in thread
From: Markus Armbruster @ 2006-12-04 19:51 UTC (permalink / raw)
  To: Steven Smith; +Cc: xen-devel, sos22

Hi Steven,

I had a look at your changes and started to backport them to versions
we use.  I might have broken something, but before I go hunting for
that, could you please try two little things for me in your version,
to make sure its okay in your tree?

1. Does xm restore work for you?  I got the following in xend.log:

[2006-12-04 20:34:54 xend 3507] ERROR (XendDomain:268) Restore failed
Traceback (most recent call last):
  File "/usr/lib/python2.4/site-packages/xen/xend/XendDomain.py", line 263, in domain_restore_fd
    return XendCheckpoint.restore(self, fd)
  File "/usr/lib/python2.4/site-packages/xen/xend/XendCheckpoint.py", line 134, in restore
    dominfo = xd.restore_(vmconfig)
  File "/usr/lib/python2.4/site-packages/xen/xend/XendDomain.py", line 288, in restore_
    dominfo = XendDomainInfo.restore(config)
  File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 271, in restore
    vm.createDevices()
  File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1515, in createDevices
    self.createDevice(n, c)
  File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1086, in createDevice
    return self.getDeviceController(deviceClass).createDevice(devconfig)
  File "/usr/lib/python2.4/site-packages/xen/xend/server/vfbif.py", line 64, in createDevice
    raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
VmError: Unknown vfb type None (['vfb', ['backend', '0']])

2. My xm shutdown spits this:

[2006-12-04 20:33:37 xend.XendDomainInfo 3507] ERROR (XendDomainInfo:1382) XendDomainInfo.cleanup: image.destroy() failed.
Traceback (most recent call last):
  File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1380, in cleanupDomain
    self.image.destroy()
  File "/usr/lib/python2.4/site-packages/xen/xend/image.py", line 210, in destroy
    if not self.pid:
AttributeError: LinuxImageHandler instance has no attribute 'pid'

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-01 13:25 ` Steven Smith
  2006-12-01 18:04   ` Markus Armbruster
  2006-12-04 19:51   ` Markus Armbruster
@ 2006-12-04 20:29   ` Daniel P. Berrange
  2006-12-05  1:59     ` Daniel P. Berrange
  2 siblings, 1 reply; 10+ messages in thread
From: Daniel P. Berrange @ 2006-12-04 20:29 UTC (permalink / raw)
  To: Steven Smith; +Cc: xen-devel, Markus Armbruster, sos22


On Fri, Dec 01, 2006 at 01:25:42PM +0000, Steven Smith wrote:
> Applied.
> 
> Thanks,
> 
> Steven.

Looking at the patches applied for the PVFB code & config re-arrangement
I think that VNC password handling for fullyvirt HVM guests has been
broken

In changeset  

  changeset:   12679:000609d8c93fb24b2ca5dd7961dfae19ddb0022f
  user:        Steven Smith <ssmith@xensource.com>
  date:        Fri Dec 01 11:49:30 2006 +0000
  files:       tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/image.py tools/python/xen/xend/server/vfbif.py tools/python/xen/xm/create.py
  description:
  [PVFB][TOOLS] Change the configuration syntax for PVFB backends to more
  closely match that of other devices.


The following hunk  against tools/python/xen/xm/create.py removes processing
of the 'vncpassword' config parameter from the guest config files for HVM
guests:


  @@ -678,20 +690,13 @@ 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:


I've not been able to actually verify it since I don't currently have
a working xen-unstable build, but I don't see how it can work without
the 'vncpasswd' parameter included there in the args for configure_hvm.

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-04 19:51   ` Markus Armbruster
@ 2006-12-04 20:43     ` Ewan Mellor
  2006-12-05 10:01       ` Markus Armbruster
  2006-12-05 18:11     ` Steven Smith
  1 sibling, 1 reply; 10+ messages in thread
From: Ewan Mellor @ 2006-12-04 20:43 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: xen-devel, sos22

On Mon, Dec 04, 2006 at 08:51:38PM +0100, Markus Armbruster wrote:

> Hi Steven,
> 
> I had a look at your changes and started to backport them to versions
> we use.  I might have broken something, but before I go hunting for
> that, could you please try two little things for me in your version,
> to make sure its okay in your tree?

Steven's 30000 feet over the Atlantic at the moment.  Perhaps I can help?

> 1. Does xm restore work for you?  I got the following in xend.log:
> 
> [2006-12-04 20:34:54 xend 3507] ERROR (XendDomain:268) Restore failed
> Traceback (most recent call last):
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomain.py", line 263, in domain_restore_fd
>     return XendCheckpoint.restore(self, fd)
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendCheckpoint.py", line 134, in restore
>     dominfo = xd.restore_(vmconfig)
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomain.py", line 288, in restore_
>     dominfo = XendDomainInfo.restore(config)
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 271, in restore
>     vm.createDevices()
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1515, in createDevices
>     self.createDevice(n, c)
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1086, in createDevice
>     return self.getDeviceController(deviceClass).createDevice(devconfig)
>   File "/usr/lib/python2.4/site-packages/xen/xend/server/vfbif.py", line 64, in createDevice
>     raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
> VmError: Unknown vfb type None (['vfb', ['backend', '0']])

It looks like your device configuration hasn't been saved properly, and so
it's not present on restore, or it's not being parsed properly on restore.
Try sticking some tracing in XendConfig.all_devices_sxpr to see whether it's
getting saved properly, and in the if cfg_sxp bit of device_add to see whether
it's being reparsed.

This changed quite a lot on the same day as the xenfb patches got committed,
so it wouldn't be a surprise if we had a merge conflict.

> 2. My xm shutdown spits this:
> 
> [2006-12-04 20:33:37 xend.XendDomainInfo 3507] ERROR (XendDomainInfo:1382) XendDomainInfo.cleanup: image.destroy() failed.
> Traceback (most recent call last):
>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1380, in cleanupDomain
>     self.image.destroy()
>   File "/usr/lib/python2.4/site-packages/xen/xend/image.py", line 210, in destroy
>     if not self.pid:
> AttributeError: LinuxImageHandler instance has no attribute 'pid'

For me, the pid field is specific to the HVMImageHandler, and shouldn't be
being referenced for LinuxImageHandler instances.  Are you up to date?  I
don't have an "if not self.pid:" statement anywhere in image.py.

Ewan.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-04 20:29   ` Daniel P. Berrange
@ 2006-12-05  1:59     ` Daniel P. Berrange
  0 siblings, 0 replies; 10+ messages in thread
From: Daniel P. Berrange @ 2006-12-05  1:59 UTC (permalink / raw)
  To: Steven Smith; +Cc: xen-devel, Markus Armbruster, sos22

On Mon, Dec 04, 2006 at 08:29:25PM +0000, Daniel P. Berrange wrote:
> 
> On Fri, Dec 01, 2006 at 01:25:42PM +0000, Steven Smith wrote:
> > Applied.
> > 
> > Thanks,
> > 
> > Steven.
> 
> Looking at the patches applied for the PVFB code & config re-arrangement
> I think that VNC password handling for fullyvirt HVM guests has been
> broken

> The following hunk  against tools/python/xen/xm/create.py removes processing
> of the 'vncpassword' config parameter from the guest config files for HVM
> guests:
> 
> 
>   @@ -678,20 +690,13 @@ 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:
> 
> 
> I've not been able to actually verify it since I don't currently have
> a working xen-unstable build, but I don't see how it can work without
> the 'vncpasswd' parameter included there in the args for configure_hvm.

Ignore me. I see there is special case code for vncpasswd parameter elsewhere
in the create.py file, which ensures its processed correctly, so there's no
bug here.  I had missed it because of a merge issue with my vncpassword
patches for the Paravirt FB.

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-04 20:43     ` Ewan Mellor
@ 2006-12-05 10:01       ` Markus Armbruster
  0 siblings, 0 replies; 10+ messages in thread
From: Markus Armbruster @ 2006-12-05 10:01 UTC (permalink / raw)
  To: Ewan Mellor; +Cc: xen-devel, sos22

Ewan Mellor <ewan@xensource.com> writes:

> On Mon, Dec 04, 2006 at 08:51:38PM +0100, Markus Armbruster wrote:
>
>> Hi Steven,
>> 
>> I had a look at your changes and started to backport them to versions
>> we use.  I might have broken something, but before I go hunting for
>> that, could you please try two little things for me in your version,
>> to make sure its okay in your tree?
>
> Steven's 30000 feet over the Atlantic at the moment.  Perhaps I can help?

Appreciated :)

>> 1. Does xm restore work for you?  I got the following in xend.log:
>> 
>> [2006-12-04 20:34:54 xend 3507] ERROR (XendDomain:268) Restore failed
>> Traceback (most recent call last):
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomain.py", line 263, in domain_restore_fd
>>     return XendCheckpoint.restore(self, fd)
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendCheckpoint.py", line 134, in restore
>>     dominfo = xd.restore_(vmconfig)
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomain.py", line 288, in restore_
>>     dominfo = XendDomainInfo.restore(config)
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 271, in restore
>>     vm.createDevices()
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1515, in createDevices
>>     self.createDevice(n, c)
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1086, in createDevice
>>     return self.getDeviceController(deviceClass).createDevice(devconfig)
>>   File "/usr/lib/python2.4/site-packages/xen/xend/server/vfbif.py", line 64, in createDevice
>>     raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
>> VmError: Unknown vfb type None (['vfb', ['backend', '0']])
>
> It looks like your device configuration hasn't been saved properly, and so
> it's not present on restore, or it's not being parsed properly on restore.
> Try sticking some tracing in XendConfig.all_devices_sxpr to see whether it's
> getting saved properly, and in the if cfg_sxp bit of device_add to see whether
> it's being reparsed.
>
> This changed quite a lot on the same day as the xenfb patches got committed,
> so it wouldn't be a surprise if we had a merge conflict.

Oww.  This looks quite different in 3.0.3.  Does anybody remember how
device config was saved there?  A quick pointer could save me some
digging...

>> 2. My xm shutdown spits this:
>> 
>> [2006-12-04 20:33:37 xend.XendDomainInfo 3507] ERROR (XendDomainInfo:1382) XendDomainInfo.cleanup: image.destroy() failed.
>> Traceback (most recent call last):
>>   File "/usr/lib/python2.4/site-packages/xen/xend/XendDomainInfo.py", line 1380, in cleanupDomain
>>     self.image.destroy()
>>   File "/usr/lib/python2.4/site-packages/xen/xend/image.py", line 210, in destroy
>>     if not self.pid:
>> AttributeError: LinuxImageHandler instance has no attribute 'pid'
>
> For me, the pid field is specific to the HVMImageHandler, and shouldn't be
> being referenced for LinuxImageHandler instances.  Are you up to date?  I
> don't have an "if not self.pid:" statement anywhere in image.py.

I missed this one:

changeset:   12683:fb0a586854c1d8a7b814a4b0d77388ee05bb5fe3
user:        Steven Smith <ssmith@xensource.com>
date:        Fri Dec 01 12:09:10 2006 +0000
files:       tools/python/xen/xend/image.py
description:
[TOOLS] Remove some dead code.

The description is misleading: the removed code wasn't dead, it was
rendered incorrect by Steven's previous changes.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-04 19:51   ` Markus Armbruster
  2006-12-04 20:43     ` Ewan Mellor
@ 2006-12-05 18:11     ` Steven Smith
  2006-12-05 18:51       ` Markus Armbruster
  1 sibling, 1 reply; 10+ messages in thread
From: Steven Smith @ 2006-12-05 18:11 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: xen-devel, sos22


[-- Attachment #1.1: Type: text/plain, Size: 593 bytes --]

> 1. Does xm restore work for you?  I got the following in xend.log:
> 
...
>     raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
> VmError: Unknown vfb type None (['vfb', ['backend', '0']])
Did you pick up

# HG changeset patch
# User Steven Smith <ssmith@xensource.com>
# Date 1164976721 0
# Node ID 2a6a793a2f680bbac8d11696e955d69335f98bf4
# Parent  b22274d1311c3f59bc9ddf4cab1a94b368aa8748
[PVFB][TOOLS] Fix save/restore for domains with PV framebuffers.

Signed-off-by: Steven Smith <sos22@cam.ac.uk>

?  That fixed a very similar looking issue.

Steven.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] PV framebuffer
  2006-12-05 18:11     ` Steven Smith
@ 2006-12-05 18:51       ` Markus Armbruster
  0 siblings, 0 replies; 10+ messages in thread
From: Markus Armbruster @ 2006-12-05 18:51 UTC (permalink / raw)
  To: Steven Smith; +Cc: xen-devel, sos22

Steven Smith <sos22-xen@srcf.ucam.org> writes:

>> 1. Does xm restore work for you?  I got the following in xend.log:
>> 
> ...
>>     raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
>> VmError: Unknown vfb type None (['vfb', ['backend', '0']])
> Did you pick up
>
> # HG changeset patch
> # User Steven Smith <ssmith@xensource.com>
> # Date 1164976721 0
> # Node ID 2a6a793a2f680bbac8d11696e955d69335f98bf4
> # Parent  b22274d1311c3f59bc9ddf4cab1a94b368aa8748
> [PVFB][TOOLS] Fix save/restore for domains with PV framebuffers.
>
> Signed-off-by: Steven Smith <sos22@cam.ac.uk>
>
> ?  That fixed a very similar looking issue.
>
> Steven.

Yup, got it, but I have to backport it to make it work.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2006-12-05 18:51 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-11-30 18:29 [PATCH] PV framebuffer Markus Armbruster
2006-12-01 13:25 ` Steven Smith
2006-12-01 18:04   ` Markus Armbruster
2006-12-04 19:51   ` Markus Armbruster
2006-12-04 20:43     ` Ewan Mellor
2006-12-05 10:01       ` Markus Armbruster
2006-12-05 18:11     ` Steven Smith
2006-12-05 18:51       ` Markus Armbruster
2006-12-04 20:29   ` Daniel P. Berrange
2006-12-05  1:59     ` Daniel P. Berrange

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.