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
|