[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] Re: [PATCH 2/2] PV framebuffer
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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |