WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] Re: [PATCH 2/2] PV framebuffer

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
From: Markus Armbruster <armbru@xxxxxxxxxx>
Date: Fri, 24 Nov 2006 09:05:42 +0100
Delivery-date: Fri, 24 Nov 2006 00:06:26 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
In-reply-to: <874psydx6m.fsf@xxxxxxxxxxxxxxxxx> (Markus Armbruster's message of "Fri, 17 Nov 2006 14:23:13 +0100")
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <87lkmjptqq.fsf@xxxxxxxxxxxxxxxxx> <874psydx6m.fsf@xxxxxxxxxxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux)
PV framebuffer backend.  Derived from http://hg.codemonkey.ws/vncfb

Extensive changes based on feedback from xen-devel.

Signed-off-by: Markus Armbruster <armbru@xxxxxxxxxx>
---
 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                     |  727 ++++++++++++++++++++++++++++++++
 tools/xenfb/xenfb.h                     |   34 +
 11 files changed, 1678 insertions(+), 6 deletions(-)

diff -r c98a8e2c62d1 tools/Makefile
--- a/tools/Makefile    Thu Nov 23 15:06:35 2006 +0000
+++ b/tools/Makefile    Fri Nov 10 08:01:00 2006 +0100
@@ -19,6 +19,7 @@ SUBDIRS-y += libaio
 SUBDIRS-y += libaio
 SUBDIRS-y += blktap
 SUBDIRS-y += libfsimage
+SUBDIRS-y += xenfb
 
 # These don't cross-compile
 ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH))
diff -r c98a8e2c62d1 tools/python/xen/xend/XendDevices.py
--- a/tools/python/xen/xend/XendDevices.py      Thu Nov 23 15:06:35 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 c98a8e2c62d1 tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py   Thu Nov 23 15:06:35 2006 +0000
+++ b/tools/python/xen/xend/XendDomainInfo.py   Thu Nov 23 18:54:35 2006 +0100
@@ -459,7 +459,7 @@ class XendDomainInfo:
             try:
                 self._constructDomain()
                 self._storeVmDetails()
-                self._createDevices()
+                self._restoreDomain()
                 self._createChannels()
                 self._storeDomDetails()
                 self._endRestore()
@@ -1331,6 +1331,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 c98a8e2c62d1 tools/python/xen/xend/image.py
--- a/tools/python/xen/xend/image.py    Thu Nov 23 15:06:35 2006 +0000
+++ b/tools/python/xen/xend/image.py    Wed Nov 22 18:59:11 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 c98a8e2c62d1 tools/python/xen/xm/create.py
--- a/tools/python/xen/xm/create.py     Thu Nov 23 15:06:35 2006 +0000
+++ b/tools/python/xen/xm/create.py     Fri Nov 17 16:02:28 2006 +0100
@@ -280,6 +280,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.
@@ -508,8 +516,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):
@@ -560,6 +570,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.
@@ -657,13 +674,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', 'usb', 'usbdevice', 'keymap' ]
     for a in args:
         if (vals.__dict__[a]):
@@ -738,6 +762,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 c98a8e2c62d1 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 c98a8e2c62d1 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 c98a8e2c62d1 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 c98a8e2c62d1 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 c98a8e2c62d1 tools/xenfb/xenfb.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/xenfb/xenfb.c       Thu Nov 23 18:49:10 2006 +0100
@@ -0,0 +1,727 @@
+#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/xenfb.h>
+#include <xen/io/xenkbd.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 void *xenfb_map_foreign_range(int xc_handle, uint32_t dom,
+                                    int size, int prot,
+                                    unsigned long mfn)
+{
+       int rc;
+
+       rc = xc_domain_translate_gpfn_list(xc_handle, dom, 1, &mfn, &mfn);
+       if (rc < 0 && errno != EINVAL)
+               return NULL;
+       return xc_map_foreign_range(xc_handle, dom, size, prot, mfn);
+}
+
+static void *xenfb_map_foreign_batch(int xc_handle, uint32_t dom, int prot,
+                                    xen_pfn_t *arr, int num)
+{
+       xen_pfn_t *buf;
+       int rc;
+       void *ret;
+
+       /* make a copy to avoid clobbering arr[] */
+       buf = malloc(num * sizeof(*buf));
+       if (!buf)
+               return NULL;
+       memcpy(buf, arr, num * sizeof(*buf));
+
+       rc = xc_domain_translate_gpfn_list(xc_handle, dom, num, buf, buf);
+       if (rc < 0 && errno != EINVAL) {
+               free(buf);
+               return NULL;
+       }
+
+       /*
+        * 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.
+        */
+       ret = xc_map_foreign_batch(xc_handle, dom, prot, buf, num);
+       free(buf);
+       return ret;
+}
+
+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;
+
+       fbmfns = xenfb_map_foreign_batch(xenfb->xc, domid,
+                       PROT_READ, page->pd, n_fbdirs);
+       if (fbmfns == NULL)
+               return -1;
+
+       xenfb->pub.pixels = xenfb_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 = xenfb_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 c98a8e2c62d1 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

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