ia64/xen-unstable

changeset 16234:695871933840

pv-qemu 7/10: Async negotiation with xenfb frontend

This patch re-factors the paravirt console xenfb_attach_dom
method. The original method blocks the caller until the front &
backends have both switched to the connected state. This isn't an
immediate problem, but patches which follow will extend qemu to also
handle the text console so blocking on graphics console startup will
block the text console processing.

The new code is basically a state machine. It starts off with a watch
waiting for the KBD backend to switch to 'initialized' mode, then does
the same for the FB backend. Now it waits for KBD & FB frontend
devices to initialize, reading & mapping the framebuffer & its config
at the appropriate step. When the KBD frontend finally reaches the
connected state it registers a graphical console with QEMU and sets up
the various framebuffer, mouse & keyboard event handlers. If a client
connects to the VNC server before this is completed, then they will
merely see a text console (or perhaps the monitor if configured that
way).

The main difference from previous versions of this patch, is that at
the suggestion of Markus Armbruster, I'vere-ordered the individual
static functions so they are in order-of-call, rather than
reversed. Although I now have to pre-declare them, it is much easier
to read the code. I have also fixed the keycode -> keysym translations
to match previous behaviour.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author Keir Fraser <keir@xensource.com>
date Thu Oct 25 14:41:35 2007 +0100 (2007-10-25)
parents b760a4aa8dcf
children b34ba3bcab0b
files tools/ioemu/hw/xen_machine_pv.c tools/ioemu/hw/xenfb.c tools/ioemu/hw/xenfb.h
line diff
     1.1 --- a/tools/ioemu/hw/xen_machine_pv.c	Thu Oct 25 14:40:19 2007 +0100
     1.2 +++ b/tools/ioemu/hw/xen_machine_pv.c	Thu Oct 25 14:41:35 2007 +0100
     1.3 @@ -40,19 +40,12 @@ static void xen_init_pv(uint64_t ram_siz
     1.4      extern int domid;
     1.5  
     1.6      /* Prepare PVFB state */
     1.7 -    xenfb = xenfb_new();
     1.8 +    xenfb = xenfb_new(domid, ds);
     1.9      if (xenfb == NULL) {
    1.10          fprintf(stderr, "Could not create framebuffer (%s)\n",
    1.11                  strerror(errno));
    1.12          exit(1);
    1.13      }
    1.14 -
    1.15 -    /* Talk to the guest */
    1.16 -    if (xenfb_attach_dom(xenfb, domid, ds) < 0) {
    1.17 -        fprintf(stderr, "Could not connect to domain (%s)\n",
    1.18 -                strerror(errno));
    1.19 -        exit(1);
    1.20 -    }
    1.21  }
    1.22  
    1.23  QEMUMachine xenpv_machine = {
     2.1 --- a/tools/ioemu/hw/xenfb.c	Thu Oct 25 14:40:19 2007 +0100
     2.2 +++ b/tools/ioemu/hw/xenfb.c	Thu Oct 25 14:41:35 2007 +0100
     2.3 @@ -52,6 +52,20 @@ struct xenfb {
     2.4  	char protocol[64];	/* frontend protocol */
     2.5  };
     2.6  
     2.7 +/* Functions for frontend/backend state machine*/
     2.8 +static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler);
     2.9 +static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler);
    2.10 +static void xenfb_backend_created_kbd(void *opaque);
    2.11 +static void xenfb_backend_created_fb(void *opaque);
    2.12 +static void xenfb_frontend_initialized_kbd(void *opaque);
    2.13 +static void xenfb_frontend_initialized_fb(void *opaque);
    2.14 +static void xenfb_frontend_connected_kbd(void *opaque);
    2.15 +
    2.16 +/* Helper functions for checking state of frontend/backend devices */
    2.17 +static int xenfb_frontend_connected(struct xenfb_device *dev);
    2.18 +static int xenfb_frontend_initialized(struct xenfb_device *dev);
    2.19 +static int xenfb_backend_created(struct xenfb_device *dev);
    2.20 +
    2.21  /* Functions which tie the PVFB into the QEMU device model */
    2.22  static void xenfb_key_event(void *opaque, int keycode);
    2.23  static void xenfb_mouse_event(void *opaque,
    2.24 @@ -60,6 +74,7 @@ static void xenfb_guest_copy(struct xenf
    2.25  static void xenfb_update(void *opaque);
    2.26  static void xenfb_invalidate(void *opaque);
    2.27  static void xenfb_screen_dump(void *opaque, const char *name);
    2.28 +static int xenfb_register_console(struct xenfb *xenfb);
    2.29  
    2.30  /*
    2.31   * Tables to map from scancode to Linux input layer keycode.
    2.32 @@ -103,34 +118,6 @@ static const unsigned char atkbd_unxlate
    2.33  
    2.34  static unsigned char scancode2linux[512];
    2.35  
    2.36 -
    2.37 -static void xenfb_detach_dom(struct xenfb *);
    2.38 -
    2.39 -static char *xenfb_path_in_dom(struct xs_handle *xsh,
    2.40 -			       char *buf, size_t size,
    2.41 -			       unsigned domid, const char *fmt, ...)
    2.42 -{
    2.43 -	va_list ap;
    2.44 -	char *domp = xs_get_domain_path(xsh, domid);
    2.45 -	int n;
    2.46 -
    2.47 -        if (domp == NULL)
    2.48 -		return NULL;
    2.49 -
    2.50 -	n = snprintf(buf, size, "%s/", domp);
    2.51 -	free(domp);
    2.52 -	if (n >= size)
    2.53 -		return NULL;
    2.54 -
    2.55 -	va_start(ap, fmt);
    2.56 -	n += vsnprintf(buf + n, size - n, fmt, ap);
    2.57 -	va_end(ap);
    2.58 -	if (n >= size)
    2.59 -		return NULL;
    2.60 -
    2.61 -	return buf;
    2.62 -}
    2.63 -
    2.64  static int xenfb_xs_scanf1(struct xs_handle *xsh,
    2.65  			   const char *dir, const char *node,
    2.66  			   const char *fmt, void *dest)
    2.67 @@ -193,27 +180,7 @@ static void xenfb_device_init(struct xen
    2.68  	dev->xenfb = xenfb;
    2.69  }
    2.70  
    2.71 -static int xenfb_device_set_domain(struct xenfb_device *dev, int domid)
    2.72 -{
    2.73 -	dev->otherend_id = domid;
    2.74 -
    2.75 -	if (!xenfb_path_in_dom(dev->xenfb->xsh,
    2.76 -			       dev->otherend, sizeof(dev->otherend),
    2.77 -			       domid, "device/%s/0", dev->devicetype)) {
    2.78 -		errno = ENOENT;
    2.79 -		return -1;
    2.80 -	}
    2.81 -	if (!xenfb_path_in_dom(dev->xenfb->xsh,
    2.82 -			       dev->nodename, sizeof(dev->nodename),
    2.83 -			       0, "backend/%s/%d/0", dev->devicetype, domid)) {
    2.84 -		errno = ENOENT;
    2.85 -		return -1;
    2.86 -	}
    2.87 -
    2.88 -	return 0;
    2.89 -}
    2.90 -
    2.91 -struct xenfb *xenfb_new(void)
    2.92 +struct xenfb *xenfb_new(int domid, DisplayState *ds)
    2.93  {
    2.94  	struct xenfb *xenfb = qemu_malloc(sizeof(struct xenfb));
    2.95  	int serrno;
    2.96 @@ -246,35 +213,18 @@ struct xenfb *xenfb_new(void)
    2.97  	if (!xenfb->xsh)
    2.98  		goto fail;
    2.99  
   2.100 +	fprintf(stderr, "FB: Waiting for KBD backend creation\n");
   2.101 +	xenfb_wait_for_backend(&xenfb->kbd, xenfb_backend_created_kbd);
   2.102 +
   2.103  	return xenfb;
   2.104  
   2.105   fail:
   2.106  	serrno = errno;
   2.107 -	xenfb_delete(xenfb);
   2.108 +	xenfb_shutdown(xenfb);
   2.109  	errno = serrno;
   2.110  	return NULL;
   2.111  }
   2.112  
   2.113 -/* Remove the backend area in xenbus since the framebuffer really is
   2.114 -   going away. */
   2.115 -void xenfb_teardown(struct xenfb *xenfb)
   2.116 -{
   2.117 -       xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename);
   2.118 -       xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename);
   2.119 -}
   2.120 -
   2.121 -
   2.122 -void xenfb_delete(struct xenfb *xenfb)
   2.123 -{
   2.124 -	xenfb_detach_dom(xenfb);
   2.125 -	if (xenfb->xc >= 0)
   2.126 -		xc_interface_close(xenfb->xc);
   2.127 -	if (xenfb->evt_xch >= 0)
   2.128 -		xc_evtchn_close(xenfb->evt_xch);
   2.129 -	if (xenfb->xsh)
   2.130 -		xs_daemon_close(xenfb->xsh);
   2.131 -	free(xenfb);
   2.132 -}
   2.133  
   2.134  static enum xenbus_state xenfb_read_state(struct xs_handle *xsh,
   2.135  					  const char *dir)
   2.136 @@ -301,60 +251,6 @@ static int xenfb_switch_state(struct xen
   2.137  	return 0;
   2.138  }
   2.139  
   2.140 -static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir,
   2.141 -				unsigned awaited)
   2.142 -{
   2.143 -	unsigned state, dummy;
   2.144 -	char **vec;
   2.145 -
   2.146 -	awaited |= 1 << XenbusStateUnknown;
   2.147 -
   2.148 -	for (;;) {
   2.149 -		state = xenfb_read_state(xsh, dir);
   2.150 -		if ((1 << state) & awaited)
   2.151 -			return state;
   2.152 -
   2.153 -		vec = xs_read_watch(xsh, &dummy);
   2.154 -		if (!vec)
   2.155 -			return -1;
   2.156 -		free(vec);
   2.157 -	}
   2.158 -}
   2.159 -
   2.160 -static int xenfb_wait_for_backend_creation(struct xenfb_device *dev)
   2.161 -{
   2.162 -	struct xs_handle *xsh = dev->xenfb->xsh;
   2.163 -	int state;
   2.164 -
   2.165 -	if (!xs_watch(xsh, dev->nodename, ""))
   2.166 -		return -1;
   2.167 -	state = xenfb_wait_for_state(xsh, dev->nodename,
   2.168 -			(1 << XenbusStateInitialising)
   2.169 -			| (1 << XenbusStateClosed)
   2.170 -#if 1 /* TODO fudging state to permit restarting; to be removed */
   2.171 -			| (1 << XenbusStateInitWait)
   2.172 -			| (1 << XenbusStateConnected)
   2.173 -			| (1 << XenbusStateClosing)
   2.174 -#endif
   2.175 -			);
   2.176 -	xs_unwatch(xsh, dev->nodename, "");
   2.177 -
   2.178 -	switch (state) {
   2.179 -#if 1
   2.180 -	case XenbusStateInitWait:
   2.181 -	case XenbusStateConnected:
   2.182 -		printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */
   2.183 -#endif
   2.184 -	case XenbusStateInitialising:
   2.185 -	case XenbusStateClosing:
   2.186 -	case XenbusStateClosed:
   2.187 -		break;
   2.188 -	default:
   2.189 -		return -1;
   2.190 -	}
   2.191 -
   2.192 -	return 0;
   2.193 -}
   2.194  
   2.195  static int xenfb_hotplug(struct xenfb_device *dev)
   2.196  {
   2.197 @@ -364,29 +260,6 @@ static int xenfb_hotplug(struct xenfb_de
   2.198  	return 0;
   2.199  }
   2.200  
   2.201 -static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev)
   2.202 -{
   2.203 -	switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
   2.204 -#if 1 /* TODO fudging state to permit restarting; to be removed */
   2.205 -			(1 << XenbusStateInitialised)
   2.206 -			| (1 << XenbusStateConnected)
   2.207 -#else
   2.208 -			1 << XenbusStateInitialised,
   2.209 -#endif
   2.210 -			)) {
   2.211 -#if 1
   2.212 -	case XenbusStateConnected:
   2.213 -		printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */
   2.214 -#endif
   2.215 -	case XenbusStateInitialised:
   2.216 -		break;
   2.217 -	default:
   2.218 -		return -1;
   2.219 -	}
   2.220 -
   2.221 -	return 0;
   2.222 -}
   2.223 -
   2.224  static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
   2.225  {
   2.226  	uint32_t *src32 = src;
   2.227 @@ -522,52 +395,6 @@ static void xenfb_unbind(struct xenfb_de
   2.228  	}
   2.229  }
   2.230  
   2.231 -static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev)
   2.232 -{
   2.233 -	switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
   2.234 -				     1 << XenbusStateConnected)) {
   2.235 -	case XenbusStateConnected:
   2.236 -		break;
   2.237 -	default:
   2.238 -		return -1;
   2.239 -	}
   2.240 -
   2.241 -	return 0;
   2.242 -}
   2.243 -
   2.244 -static void xenfb_dev_fatal(struct xenfb_device *dev, int err,
   2.245 -			    const char *fmt, ...)
   2.246 -{
   2.247 -	struct xs_handle *xsh = dev->xenfb->xsh;
   2.248 -	va_list ap;
   2.249 -	char errdir[80];
   2.250 -	char buf[1024];
   2.251 -	int n;
   2.252 -
   2.253 -	fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */
   2.254 -	va_start(ap, fmt);
   2.255 -	vfprintf(stderr, fmt, ap);
   2.256 -	va_end(ap);
   2.257 -	if (err)
   2.258 -		fprintf(stderr, " (%s)", strerror(err));
   2.259 -	putc('\n', stderr);
   2.260 -
   2.261 -	if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0,
   2.262 -			       "error/%s", dev->nodename))
   2.263 -		goto out;	/* FIXME complain */
   2.264 -
   2.265 -	va_start(ap, fmt);
   2.266 -	n = snprintf(buf, sizeof(buf), "%d ", err);
   2.267 -	snprintf(buf + n, sizeof(buf) - n, fmt, ap);
   2.268 -	va_end(ap);
   2.269 -
   2.270 -	if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0)
   2.271 -		goto out;	/* FIXME complain */
   2.272 -
   2.273 - out:
   2.274 -	xenfb_switch_state(dev, XenbusStateClosing);
   2.275 -}
   2.276 -
   2.277  
   2.278  static void xenfb_detach_dom(struct xenfb *xenfb)
   2.279  {
   2.280 @@ -579,6 +406,24 @@ static void xenfb_detach_dom(struct xenf
   2.281  	}
   2.282  }
   2.283  
   2.284 +/* Remove the backend area in xenbus since the framebuffer really is
   2.285 +   going away. */
   2.286 +void xenfb_shutdown(struct xenfb *xenfb)
   2.287 +{
   2.288 +	fprintf(stderr, "FB: Shutting down backend\n");
   2.289 +	xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename);
   2.290 +	xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename);
   2.291 +
   2.292 +	xenfb_detach_dom(xenfb);
   2.293 +	if (xenfb->xc >= 0)
   2.294 +		xc_interface_close(xenfb->xc);
   2.295 +	if (xenfb->evt_xch >= 0)
   2.296 +		xc_evtchn_close(xenfb->evt_xch);
   2.297 +	if (xenfb->xsh)
   2.298 +		xs_daemon_close(xenfb->xsh);
   2.299 +	free(xenfb);
   2.300 +}
   2.301 +
   2.302  
   2.303  static void xenfb_on_fb_event(struct xenfb *xenfb)
   2.304  {
   2.305 @@ -643,6 +488,7 @@ static int xenfb_on_state_change(struct 
   2.306  	return 0;
   2.307  }
   2.308  
   2.309 +/* Send an event to the keyboard frontend driver */
   2.310  static int xenfb_kbd_event(struct xenfb *xenfb,
   2.311  			   union xenkbd_in_event *event)
   2.312  {
   2.313 @@ -665,6 +511,7 @@ static int xenfb_kbd_event(struct xenfb 
   2.314  	return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
   2.315  }
   2.316  
   2.317 +/* Send a keyboard (or mouse button) event */
   2.318  static int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode)
   2.319  {
   2.320  	union xenkbd_in_event event;
   2.321 @@ -677,6 +524,7 @@ static int xenfb_send_key(struct xenfb *
   2.322  	return xenfb_kbd_event(xenfb, &event);
   2.323  }
   2.324  
   2.325 +/* Send a relative mouse movement event */
   2.326  static int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y)
   2.327  {
   2.328  	union xenkbd_in_event event;
   2.329 @@ -689,6 +537,7 @@ static int xenfb_send_motion(struct xenf
   2.330  	return xenfb_kbd_event(xenfb, &event);
   2.331  }
   2.332  
   2.333 +/* Send an absolute mouse movement event */
   2.334  static int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y)
   2.335  {
   2.336  	union xenkbd_in_event event;
   2.337 @@ -701,24 +550,29 @@ static int xenfb_send_position(struct xe
   2.338  	return xenfb_kbd_event(xenfb, &event);
   2.339  }
   2.340  
   2.341 -
   2.342 +/* Process events from the frontend event channel */
   2.343  static void xenfb_dispatch_channel(void *opaque)
   2.344  {
   2.345  	struct xenfb *xenfb = (struct xenfb *)opaque;
   2.346  	evtchn_port_t port;
   2.347  	port = xc_evtchn_pending(xenfb->evt_xch);
   2.348 -	if (port == -1)
   2.349 +	if (port == -1) {
   2.350 +		xenfb_shutdown(xenfb);
   2.351  		exit(1);
   2.352 +	}
   2.353  
   2.354  	if (port == xenfb->fb.port)
   2.355  		xenfb_on_fb_event(xenfb);
   2.356  	else if (port == xenfb->kbd.port)
   2.357  		xenfb_on_kbd_event(xenfb);
   2.358  
   2.359 -	if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1)
   2.360 +	if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) {
   2.361 +		xenfb_shutdown(xenfb);
   2.362  		exit(1);
   2.363 +	}
   2.364  }
   2.365  
   2.366 +/* Process ongoing events from the frontend devices */
   2.367  static void xenfb_dispatch_store(void *opaque)
   2.368  {
   2.369  	struct xenfb *xenfb = (struct xenfb *)opaque;
   2.370 @@ -731,125 +585,348 @@ static void xenfb_dispatch_store(void *o
   2.371  	r = xenfb_on_state_change(&xenfb->fb);
   2.372  	if (r == 0)
   2.373  		r = xenfb_on_state_change(&xenfb->kbd);
   2.374 -	if (r == -1)
   2.375 +	if (r < 0) {
   2.376 +		xenfb_shutdown(xenfb);
   2.377  		exit(1);
   2.378 +	}
   2.379  }
   2.380  
   2.381  
   2.382 -int xenfb_attach_dom(struct xenfb *xenfb, int domid, DisplayState *ds)
   2.383 -{
   2.384 -	struct xs_handle *xsh = xenfb->xsh;
   2.385 -	int val, serrno;
   2.386 -	struct xenfb_page *fb_page;
   2.387 -
   2.388 -	xenfb_detach_dom(xenfb);
   2.389 -
   2.390 -	xenfb_device_set_domain(&xenfb->fb, domid);
   2.391 -	xenfb_device_set_domain(&xenfb->kbd, domid);
   2.392 -
   2.393 -	if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0)
   2.394 -		goto error;
   2.395 -	if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0)
   2.396 -		goto error;
   2.397 -
   2.398 -	if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", "1"))
   2.399 -		goto error;
   2.400 -	if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait))
   2.401 -		goto error;
   2.402 -	if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait))
   2.403 -		goto error;
   2.404 -
   2.405 -	if (xenfb_hotplug(&xenfb->fb) < 0)
   2.406 -		goto error;
   2.407 -	if (xenfb_hotplug(&xenfb->kbd) < 0)
   2.408 -		goto error;
   2.409 -
   2.410 -	if (!xs_watch(xsh, xenfb->fb.otherend, ""))
   2.411 -		goto error;
   2.412 -	if (!xs_watch(xsh, xenfb->kbd.otherend, ""))
   2.413 -		goto error;
   2.414 +/****************************************************************
   2.415 + *
   2.416 + * Functions for processing frontend config
   2.417 + *
   2.418 + ****************************************************************/
   2.419  
   2.420 -	if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0)
   2.421 -		goto error;
   2.422 -	if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0)
   2.423 -		goto error;
   2.424 -
   2.425 -	if (xenfb_bind(&xenfb->fb) < 0)
   2.426 -		goto error;
   2.427 -	if (xenfb_bind(&xenfb->kbd) < 0)
   2.428 -		goto error;
   2.429  
   2.430 -	if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update",
   2.431 -			    "%d", &val) < 0)
   2.432 -		val = 0;
   2.433 -	if (!val) {
   2.434 -		errno = ENOTSUP;
   2.435 -		goto error;
   2.436 -	}
   2.437 -	if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "protocol", "%63s",
   2.438 -			    xenfb->protocol) < 0)
   2.439 -		xenfb->protocol[0] = '\0';
   2.440 -	xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1");
   2.441 +/* Process the frontend framebuffer config */
   2.442 +static int xenfb_read_frontend_fb_config(struct xenfb *xenfb) {
   2.443 +	struct xenfb_page *fb_page;
   2.444 +	int val;
   2.445  
   2.446 -	/* TODO check for permitted ranges */
   2.447 -	fb_page = xenfb->fb.page;
   2.448 -	xenfb->depth = fb_page->depth;
   2.449 -	xenfb->width = fb_page->width;
   2.450 -	xenfb->height = fb_page->height;
   2.451 -	/* TODO check for consistency with the above */
   2.452 -	xenfb->fb_len = fb_page->mem_length;
   2.453 -	xenfb->row_stride = fb_page->line_length;
   2.454 +        if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "feature-update",
   2.455 +                            "%d", &val) < 0)
   2.456 +                val = 0;
   2.457 +        if (!val) {
   2.458 +                fprintf(stderr, "feature-update not supported\n");
   2.459 +                errno = ENOTSUP;
   2.460 +                return -1;
   2.461 +        }
   2.462 +        if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "protocol", "%63s",
   2.463 +                            xenfb->protocol) < 0)
   2.464 +                xenfb->protocol[0] = '\0';
   2.465 +        xenfb_xs_printf(xenfb->xsh, xenfb->fb.nodename, "request-update", "1");
   2.466  
   2.467 -	if (xenfb_map_fb(xenfb, domid) < 0)
   2.468 -		goto error;
   2.469 +        /* TODO check for permitted ranges */
   2.470 +        fb_page = xenfb->fb.page;
   2.471 +        xenfb->depth = fb_page->depth;
   2.472 +        xenfb->width = fb_page->width;
   2.473 +        xenfb->height = fb_page->height;
   2.474 +        /* TODO check for consistency with the above */
   2.475 +        xenfb->fb_len = fb_page->mem_length;
   2.476 +        xenfb->row_stride = fb_page->line_length;
   2.477 +        fprintf(stderr, "Framebuffer depth %d width %d height %d line %d\n",
   2.478 +                fb_page->depth, fb_page->width, fb_page->height, fb_page->line_length);
   2.479 +        if (xenfb_map_fb(xenfb, xenfb->fb.otherend_id) < 0)
   2.480 +		return -1;
   2.481  
   2.482 -	if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
   2.483 -		goto error;
   2.484 -	if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
   2.485 -		goto error;
   2.486 +        if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
   2.487 +                return -1;
   2.488 +        if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
   2.489 +                return -1;
   2.490  
   2.491 -	if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0)
   2.492 -		goto error;
   2.493 -	if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer",
   2.494 +	return 0;
   2.495 +}
   2.496 +
   2.497 +/* Process the frontend keyboard config */
   2.498 +static int xenfb_read_frontend_kbd_config(struct xenfb *xenfb)
   2.499 +{
   2.500 +	int val;
   2.501 +
   2.502 +	if (xenfb_xs_scanf1(xenfb->xsh, xenfb->kbd.otherend, "request-abs-pointer",
   2.503  			    "%d", &val) < 0)
   2.504  		val = 0;
   2.505  	xenfb->abs_pointer_wanted = val;
   2.506  
   2.507 -	/* Listen for events from xenstore */
   2.508 -	if (qemu_set_fd_handler2(xs_fileno(xenfb->xsh), NULL, xenfb_dispatch_store, NULL, xenfb) < 0)
   2.509 -		goto error;
   2.510 -
   2.511 -	/* Listen for events from the event channel */
   2.512 -	if (qemu_set_fd_handler2(xc_evtchn_fd(xenfb->evt_xch), NULL, xenfb_dispatch_channel, NULL, xenfb) < 0)
   2.513 -		goto error;
   2.514 +	return 0;
   2.515 +}
   2.516  
   2.517 -	/* Register our keyboard & mouse handlers */
   2.518 -	qemu_add_kbd_event_handler(xenfb_key_event, xenfb);
   2.519 -	qemu_add_mouse_event_handler(xenfb_mouse_event, xenfb,
   2.520 -				     xenfb->abs_pointer_wanted,
   2.521 -				     "Xen PVFB Mouse");
   2.522  
   2.523 -	xenfb->ds = ds;
   2.524 +/****************************************************************
   2.525 + *
   2.526 + * Functions for frontend/backend state machine
   2.527 + *
   2.528 + ****************************************************************/
   2.529  
   2.530 -	/* Tell QEMU to allocate a graphical console */
   2.531 -	graphic_console_init(ds,
   2.532 -			     xenfb_update,
   2.533 -			     xenfb_invalidate,
   2.534 -			     xenfb_screen_dump,
   2.535 -			     xenfb);
   2.536 -	dpy_resize(ds, xenfb->width, xenfb->height);
   2.537 +/* Register a watch against a frontend device, and setup
   2.538 + * QEMU event loop to poll the xenstore FD for notification */
   2.539 +static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler)
   2.540 +{
   2.541 +        fprintf(stderr, "Doing frontend watch on %s\n", dev->otherend);
   2.542 +	if (!xs_watch(dev->xenfb->xsh, dev->otherend, "")) {
   2.543 +		fprintf(stderr, "Watch for dev failed\n");
   2.544 +		return -1;
   2.545 +	}
   2.546 +
   2.547 +	if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0)
   2.548 +		return -1;
   2.549  
   2.550  	return 0;
   2.551 +}
   2.552  
   2.553 - error:
   2.554 -	serrno = errno;
   2.555 -	xenfb_detach_dom(xenfb);
   2.556 -	xenfb_dev_fatal(&xenfb->fb, serrno, "on fire");
   2.557 -	xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire");
   2.558 -        errno = serrno;
   2.559 -        return -1;
   2.560 +/* Register a watch against a backend device, and setup
   2.561 + * QEMU event loop to poll the xenstore FD for notification */
   2.562 +static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler)
   2.563 +{
   2.564 +	fprintf(stderr, "Doing backend watch on %s\n", dev->nodename);
   2.565 +	if (!xs_watch(dev->xenfb->xsh, dev->nodename, "")) {
   2.566 +		fprintf(stderr, "Watch for dev failed\n");
   2.567 +		return -1;
   2.568 +	}
   2.569 +
   2.570 +	if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0)
   2.571 +		return -1;
   2.572 +
   2.573 +	return 0;
   2.574  }
   2.575  
   2.576 +/* Callback invoked while waiting for KBD backend to change
   2.577 + * to the created state */
   2.578 +static void xenfb_backend_created_kbd(void *opaque)
   2.579 +{
   2.580 +	struct xenfb_device *dev = (struct xenfb_device *)opaque;
   2.581 +	int ret = xenfb_backend_created(dev);
   2.582 +	if (ret < 0) {
   2.583 +		xenfb_shutdown(dev->xenfb);
   2.584 +		exit(1);
   2.585 +	}
   2.586 +	if (ret)
   2.587 +		return; /* Still waiting */
   2.588 +
   2.589 +	if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, "feature-abs-pointer", "1")) {
   2.590 +		xenfb_shutdown(dev->xenfb);
   2.591 +		exit(1);
   2.592 +	}
   2.593 +
   2.594 +	fprintf(stderr, "FB: Waiting for FB backend creation\n");
   2.595 +	xenfb_wait_for_backend(&dev->xenfb->fb, xenfb_backend_created_fb);
   2.596 +}
   2.597 +
   2.598 +/* Callback invoked while waiting for FB backend to change
   2.599 + * to the created state */
   2.600 +static void xenfb_backend_created_fb(void *opaque)
   2.601 +{
   2.602 +	struct xenfb_device *dev = (struct xenfb_device *)opaque;
   2.603 +	int ret = xenfb_backend_created(dev);
   2.604 +	if (ret < 0) {
   2.605 +		xenfb_shutdown(dev->xenfb);
   2.606 +		exit(1);
   2.607 +	}
   2.608 +	if (ret)
   2.609 +		return; /* Still waiting */
   2.610 +
   2.611 +	fprintf(stderr, "FB: Waiting for KBD frontend initialization\n");
   2.612 +	xenfb_wait_for_frontend(&dev->xenfb->kbd, xenfb_frontend_initialized_kbd);
   2.613 +}
   2.614 +
   2.615 +/* Callback invoked while waiting for KBD frontend to change
   2.616 + * to the initialized state */
   2.617 +static void xenfb_frontend_initialized_kbd(void *opaque)
   2.618 +{
   2.619 +	struct xenfb_device *dev = (struct xenfb_device *)opaque;
   2.620 +	int ret = xenfb_frontend_initialized(dev);
   2.621 +	if (ret < 0) {
   2.622 +		xenfb_shutdown(dev->xenfb);
   2.623 +		exit(1);
   2.624 +	}
   2.625 +	if (ret)
   2.626 +		return; /* Still waiting */
   2.627 +
   2.628 +
   2.629 +        fprintf(stderr, "FB: Waiting for FB frontend initialization\n");
   2.630 +	xenfb_wait_for_frontend(&dev->xenfb->fb, xenfb_frontend_initialized_fb);
   2.631 +}
   2.632 +
   2.633 +/* Callback invoked while waiting for FB frontend to change
   2.634 + * to the initialized state */
   2.635 +static void xenfb_frontend_initialized_fb(void *opaque)
   2.636 +{
   2.637 +	struct xenfb_device *dev = (struct xenfb_device *)opaque;
   2.638 +	int ret = xenfb_frontend_initialized(dev);
   2.639 +	if (ret < 0) {
   2.640 +		xenfb_shutdown(dev->xenfb);
   2.641 +		exit(1);
   2.642 +	}
   2.643 +	if (ret)
   2.644 +		return; /* Still waiting */
   2.645 +
   2.646 +
   2.647 +	if (xenfb_read_frontend_fb_config(dev->xenfb)) {
   2.648 +		xenfb_shutdown(dev->xenfb);
   2.649 +	        exit(1);
   2.650 +	}
   2.651 +
   2.652 +        fprintf(stderr, "FB: Waiting for KBD frontend connection\n");
   2.653 +	xenfb_wait_for_frontend(&dev->xenfb->kbd, xenfb_frontend_connected_kbd);
   2.654 +}
   2.655 +
   2.656 +/* Callback invoked while waiting for KBD frontend to change
   2.657 + * to the connected state */
   2.658 +static void xenfb_frontend_connected_kbd(void *opaque)
   2.659 +{
   2.660 +	struct xenfb_device *dev = (struct xenfb_device *)opaque;
   2.661 +	int ret = xenfb_frontend_connected(dev);
   2.662 +	if (ret < 0) {
   2.663 +		xenfb_shutdown(dev->xenfb);
   2.664 +		exit(1);
   2.665 +	}
   2.666 +	if (ret)
   2.667 +		return; /* Still waiting */
   2.668 +
   2.669 +	if (xenfb_read_frontend_kbd_config(dev->xenfb) < 0) {
   2.670 +		xenfb_shutdown(dev->xenfb);
   2.671 +	        exit(1);
   2.672 +	}
   2.673 +
   2.674 +	xenfb_register_console(dev->xenfb);
   2.675 +}
   2.676 +
   2.677 +
   2.678 +/****************************************************************
   2.679 + *
   2.680 + * Helper functions for checking state of frontend/backend devices
   2.681 + *
   2.682 + ****************************************************************/
   2.683 +
   2.684 +/* Helper to determine if a frontend device is in Connected state */
   2.685 +static int xenfb_frontend_connected(struct xenfb_device *dev)
   2.686 +{
   2.687 +	unsigned int state;
   2.688 +	unsigned int dummy;
   2.689 +	char **vec;
   2.690 +	vec = xs_read_watch(dev->xenfb->xsh, &dummy);
   2.691 +	if (!vec)
   2.692 +		return -1;
   2.693 +	free(vec);
   2.694 +
   2.695 +	state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
   2.696 +	if (!((1 <<state) & ((1 << XenbusStateUnknown) |
   2.697 +			     (1 << XenbusStateConnected)))) {
   2.698 +		fprintf(stderr, "FB: Carry on waiting\n");
   2.699 +		return 1;
   2.700 +	}
   2.701 +
   2.702 +	/* Don't unwatch frontend - we need to detect shutdown */
   2.703 +	/*xs_unwatch(dev->xenfb->xsh, dev->otherend, "");*/
   2.704 +
   2.705 +	switch (state) {
   2.706 +	case XenbusStateConnected:
   2.707 +		break;
   2.708 +	default:
   2.709 +		return -1;
   2.710 +	}
   2.711 +	return 0;
   2.712 +}
   2.713 +
   2.714 +
   2.715 +/* Helper to determine if a frontend device is in Initialized state */
   2.716 +static int xenfb_frontend_initialized(struct xenfb_device *dev)
   2.717 +{
   2.718 +	unsigned int state;
   2.719 +	unsigned int dummy;
   2.720 +	char **vec;
   2.721 +	vec = xs_read_watch(dev->xenfb->xsh, &dummy);
   2.722 +	if (!vec)
   2.723 +		return -1;
   2.724 +	free(vec);
   2.725 +
   2.726 +	state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
   2.727 +
   2.728 +	if (!((1 << state) & ((1 << XenbusStateUnknown)
   2.729 +			      | (1 << XenbusStateInitialised)
   2.730 +#if 1 /* TODO fudging state to permit restarting; to be removed */
   2.731 +			      | (1 << XenbusStateConnected)
   2.732 +#endif
   2.733 +			      ))) {
   2.734 +		fprintf(stderr, "FB: Carry on waiting\n");
   2.735 +		return 1;
   2.736 +	}
   2.737 +
   2.738 +	xs_unwatch(dev->xenfb->xsh, dev->otherend, "");
   2.739 +
   2.740 +	switch (state) {
   2.741 +#if 1
   2.742 +	case XenbusStateConnected:
   2.743 +                printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */
   2.744 +#endif
   2.745 +        case XenbusStateInitialised:
   2.746 +                break;
   2.747 +        default:
   2.748 +                return -1;
   2.749 +        }
   2.750 +
   2.751 +	if (xenfb_bind(dev) < 0)
   2.752 +		return -1;
   2.753 +
   2.754 +	return 0;
   2.755 +}
   2.756 +
   2.757 +/* Helper to determine if a backend device is in Created state */
   2.758 +static int xenfb_backend_created(struct xenfb_device *dev)
   2.759 +{
   2.760 +	unsigned int state;
   2.761 +	unsigned int dummy;
   2.762 +	char **vec;
   2.763 +	vec = xs_read_watch(dev->xenfb->xsh, &dummy);
   2.764 +	if (!vec)
   2.765 +		return -1;
   2.766 +	free(vec);
   2.767 +
   2.768 +	state = xenfb_read_state(dev->xenfb->xsh, dev->nodename);
   2.769 +
   2.770 +	if (!((1 <<state) & ((1 << XenbusStateUnknown)
   2.771 +			     | (1 << XenbusStateInitialising)
   2.772 +			     | (1 << XenbusStateClosed)
   2.773 +#if 1 /* TODO fudging state to permit restarting; to be removed */
   2.774 +			     | (1 << XenbusStateInitWait)
   2.775 +			     | (1 << XenbusStateConnected)
   2.776 +			     | (1 << XenbusStateClosing)
   2.777 +#endif
   2.778 +			     ))) {
   2.779 +		fprintf(stderr, "FB: Carry on waiting\n");
   2.780 +		return 1;
   2.781 +	}
   2.782 +
   2.783 +	xs_unwatch(dev->xenfb->xsh, dev->nodename, "");
   2.784 +
   2.785 +        switch (state) {
   2.786 +#if 1
   2.787 +        case XenbusStateInitWait:
   2.788 +        case XenbusStateConnected:
   2.789 +                printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */
   2.790 +#endif
   2.791 +        case XenbusStateInitialising:
   2.792 +        case XenbusStateClosing:
   2.793 +        case XenbusStateClosed:
   2.794 +                break;
   2.795 +        default:
   2.796 +                fprintf(stderr, "Wrong state %d\n", state);
   2.797 +                return -1;
   2.798 +        }
   2.799 +        xenfb_switch_state(dev, XenbusStateInitWait);
   2.800 +        if (xenfb_hotplug(dev) < 0)
   2.801 +                return -1;
   2.802 +
   2.803 +        return 0;
   2.804 +}
   2.805 +
   2.806 +
   2.807 +/****************************************************************
   2.808 + * 
   2.809 + * QEMU device model integration functions
   2.810 + *
   2.811 + ****************************************************************/
   2.812 +
   2.813  /* 
   2.814   * Send a key event from the client to the guest OS
   2.815   * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
   2.816 @@ -996,6 +1073,32 @@ static void xenfb_invalidate(void *opaqu
   2.817  static void xenfb_screen_dump(void *opaque, const char *name) { }
   2.818  
   2.819  
   2.820 +/* Register a QEMU graphical console, and key/mouse handler,
   2.821 + * connecting up their events to the frontend */
   2.822 +static int xenfb_register_console(struct xenfb *xenfb) {
   2.823 +	/* Register our keyboard & mouse handlers */
   2.824 +	qemu_add_kbd_event_handler(xenfb_key_event, xenfb);
   2.825 +	qemu_add_mouse_event_handler(xenfb_mouse_event, xenfb,
   2.826 +  				     xenfb->abs_pointer_wanted,
   2.827 +  				     "Xen PVFB Mouse");
   2.828 +  
   2.829 +  	/* Tell QEMU to allocate a graphical console */
   2.830 +	graphic_console_init(xenfb->ds,
   2.831 +			     xenfb_update,
   2.832 +			     xenfb_invalidate,
   2.833 +			     xenfb_screen_dump,
   2.834 +			     xenfb);
   2.835 +	dpy_resize(xenfb->ds, xenfb->width, xenfb->height);
   2.836 +
   2.837 +	if (qemu_set_fd_handler2(xenfb->evt_xch, NULL, xenfb_dispatch_channel, NULL, xenfb) < 0)
   2.838 +	        return -1;
   2.839 +	if (qemu_set_fd_handler2(xs_fileno(xenfb->xsh), NULL, xenfb_dispatch_store, NULL, xenfb) < 0)
   2.840 +		return -1;
   2.841 +
   2.842 +        fprintf(stderr, "Xen Framebuffer registered\n");
   2.843 +        return 0;
   2.844 +}
   2.845 +
   2.846  /*
   2.847   * Local variables:
   2.848   *  c-indent-level: 8
     3.1 --- a/tools/ioemu/hw/xenfb.h	Thu Oct 25 14:40:19 2007 +0100
     3.2 +++ b/tools/ioemu/hw/xenfb.h	Thu Oct 25 14:41:35 2007 +0100
     3.3 @@ -7,10 +7,7 @@
     3.4  
     3.5  struct xenfb;
     3.6  
     3.7 -struct xenfb *xenfb_new(void);
     3.8 -void xenfb_delete(struct xenfb *xenfb);
     3.9 -void xenfb_teardown(struct xenfb *xenfb);
    3.10 -
    3.11 -int xenfb_attach_dom(struct xenfb *xenfb, int domid, DisplayState *ds);
    3.12 +struct xenfb *xenfb_new(int domid, DisplayState *ds);
    3.13 +void xenfb_shutdown(struct xenfb *xenfb);
    3.14  
    3.15  #endif