Hi,
It seems the usb mouse and tablet are not enabled at all with
ioemu-remote. I looked into this problem a bit and found that the logic
in hw/usb-hid.c, which was quite different from that of upstream qemu
or of xen-unstable, was not working for some reason.
I replaced hw/usb-hid.c with that of the latest qemu and manually
applied xen-unstable cs17003 to it, then usb pointers looked working
properly. If you don't have good reason to keep using the unique
implementation, please apply the patch below. Otherwise, some fix is
needed anyway since usbdevice='tablet' is often used by people who want
to use grabless mouse.
Regards,
-- Yosuke Iwamatsu
------------------------------------------------------------------------
Replace hw/usb-hid.c with the file in upstream qemu tree.
Also add queued mouse events support based on xen-unstable changeset 17003.
# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1202723858 0
# Node ID 96b418cf047e6d869740efd3660ab175303a3148
# Parent c68ce89542c7fbba9d00fd3a7d4e190476554e55
qemu: Queue mouse clicks.
qemu doesn't enqueue mouse events, just records the latest mouse
state. This can cause some lost mouse double clicks if the events are
not processed fast enought. This patch implements a simple queue for
left mouse click events.
Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@xxxxxxxxxxxxx>
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 2421a07..fce142a 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -43,41 +43,30 @@
#define USB_TABLET 2
#define USB_KEYBOARD 3
-typedef struct USBPointerEvent {
- int xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
- int dz, buttons_state;
-} USBPointerEvent;
-
-#define QUEUELENSHIFT 4 /* 16 events should be enough for a triple-click */
-#define QUEUELEN (1u<<QUEUELENSHIFT)
-#define QUEUEINDEXMASK (QUEUELEN-1u)
-#define QUEUE_INCR(v) ((v)++, (v) &= QUEUEINDEXMASK)
-
-typedef struct USBPointerState {
- USBPointerEvent queue[QUEUELEN];
- unsigned head, tail; /* indices into circular queue */
-
- int mouse_grabbed, xyrel;
+typedef struct USBMouseState {
+ int dx, dy, dz, buttons_state;
+ int x, y;
+ int mouse_grabbed;
QEMUPutMouseEntry *eh_entry;
-} USBPointerState;
+} USBMouseState;
typedef struct USBKeyboardState {
uint16_t modifiers;
uint8_t leds;
uint8_t key[16];
int keys;
- int changed;
} USBKeyboardState;
typedef struct USBHIDState {
USBDevice dev;
union {
- USBPointerState ptr;
+ USBMouseState ptr;
USBKeyboardState kbd;
};
int kind;
int protocol;
int idle;
+ int changed;
} USBHIDState;
/* mostly the same values as the Bochs USB Mouse device */
@@ -267,53 +256,73 @@ static const uint8_t
qemu_keyboard_config_descriptor[] = {
};
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
- 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01,
- 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
- 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
- 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
- 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38,
- 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x03,
- 0x81, 0x06, 0xC0, 0xC0,
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
};
static const uint8_t qemu_tablet_hid_report_descriptor[] = {
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x01, /* Usage Pointer */
- 0xA1, 0x01, /* Collection Application */
- 0x09, 0x01, /* Usage Pointer */
- 0xA1, 0x00, /* Collection Physical */
- 0x05, 0x09, /* Usage Page Button */
- 0x19, 0x01, /* Usage Minimum Button 1 */
- 0x29, 0x03, /* Usage Maximum Button 3 */
- 0x15, 0x00, /* Logical Minimum 0 */
- 0x25, 0x01, /* Logical Maximum 1 */
- 0x95, 0x03, /* Report Count 3 */
- 0x75, 0x01, /* Report Size 1 */
- 0x81, 0x02, /* Input (Data, Var, Abs) */
- 0x95, 0x01, /* Report Count 1 */
- 0x75, 0x05, /* Report Size 5 */
- 0x81, 0x01, /* Input (Cnst, Array, Abs) */
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x30, /* Usage X */
- 0x09, 0x31, /* Usage Y */
- 0x15, 0x00, /* Logical Minimum 0 */
- 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */
- 0x35, 0x00, /* Physical Minimum 0 */
- 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */
- 0x75, 0x10, /* Report Size 16 */
- 0x95, 0x02, /* Report Count 2 */
- 0x81, 0x02, /* Input (Data, Var, Abs) */
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x38, /* Usage Wheel */
- 0x15, 0x81, /* Logical Minimum -127 */
- 0x25, 0x7F, /* Logical Maximum 127 */
- 0x35, 0x00, /* Physical Minimum 0 (same as logical) */
- 0x45, 0x00, /* Physical Maximum 0 (same as logical) */
- 0x75, 0x08, /* Report Size 8 */
- 0x95, 0x01, /* Report Count 1 */
- 0x81, 0x06, /* Input (Data, Var, Rel) */
- 0xC0, /* End Collection */
- 0xC0, /* End Collection */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xfe, 0x7f, /* Logical Maximum (0x7fff) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0xfe, 0x7f, /* Physical Maximum (0x7fff) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x35, 0x00, /* Physical Minimum (same as logical) */
+ 0x45, 0x00, /* Physical Maximum (same as logical) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
};
static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
@@ -393,49 +402,65 @@ static const uint8_t usb_hid_usage_keys[0x100] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
-static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) {
- e->xdx = e->ydy = e->dz = 0;
- e->buttons_state = buttons;
-}
-
-static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel,
- int x1, int y1, int z1) {
- if (xyrel) {
- e->xdx += x1;
- e->ydy += y1;
+static int currentbutton = 0;
+typedef struct _mouseclick {
+ int button_state;
+ struct _mouseclick *next;
+} mouseclick;
+static mouseclick mousequeue[20];
+static mouseclick *head = mousequeue;
+static mouseclick *tail = mousequeue;
+
+static void usb_mouse_event(void *opaque,
+ int dx1, int dy1, int dz1, int buttons_state)
+{
+ USBHIDState *hs = opaque;
+ USBMouseState *s = &hs->ptr;
+
+ if (hs->changed == 1){
+ //A mouse event is lost
+ if (buttons_state != currentbutton && tail->next != head) {
+ //A left click event is lost: let's add it to the queue
+ //counter++;
+ tail->button_state = buttons_state;
+ tail = tail->next;
+ }
}
- e->dz += z1;
+ else {
+ s->buttons_state = buttons_state;
+ }
+
+ s->dx += dx1;
+ s->dy += dy1;
+ s->dz += dz1;
+ currentbutton = buttons_state;
+ hs->changed = 1;
}
-static void usb_pointer_event(void *hs_v, int x1, int y1, int z1,
- int buttons_state) {
- /* We combine events where feasible to keep the queue small.
- * We shouldn't combine anything with the first event with
- * a particular button state, as that would change the
- * location of the button state change. */
- USBHIDState *hs= hs_v;
- USBPointerState *s = &hs->ptr;
- unsigned use_slot= (s->tail-1) & QUEUEINDEXMASK;
- unsigned previous_slot= (use_slot-1) & QUEUEINDEXMASK;
-
- if (s->tail == s->head || use_slot == s->head ||
- s->queue[use_slot].buttons_state != buttons_state ||
- s->queue[previous_slot].buttons_state != buttons_state) {
- /* can't or shouldn't combine this event with previous one */
- use_slot= s->tail;
- QUEUE_INCR(s->tail);
- if (use_slot == s->head) {
- /* queue full, oh well, discard something */
- s->head++; s->head &= QUEUEINDEXMASK;
- /* but we preserve the relative motions */
- usb_pointer_event_combine(&s->queue[s->head], s->xyrel,
- s->queue[use_slot].xdx,
- s->queue[use_slot].ydy,
- s->queue[use_slot].dz);
- }
- usb_pointer_event_clear(&s->queue[use_slot], buttons_state);
+static void usb_tablet_event(void *opaque,
+ int x, int y, int dz, int buttons_state)
+{
+ USBHIDState *hs = opaque;
+ USBMouseState *s = &hs->ptr;
+
+ if (hs->changed == 1){
+ //A mouse event is lost
+ if (buttons_state != currentbutton && tail->next != head) {
+ //A left click event is lost: let's add it to the queue
+ //counter++;
+ tail->button_state = buttons_state;
+ tail = tail->next;
+ }
+ }
+ else {
+ s->buttons_state = buttons_state;
}
- usb_pointer_event_combine(&s->queue[use_slot],s->xyrel, x1,y1,z1);
+
+ s->x = x;
+ s->y = y;
+ s->dz += dz;
+ currentbutton = buttons_state;
+ hs->changed = 1;
}
static void usb_keyboard_event(void *opaque, int keycode)
@@ -449,7 +474,7 @@ static void usb_keyboard_event(void *opaque, int
keycode)
hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
s->modifiers &= ~(1 << 8);
- s->changed = 1;
+ hs->changed = 1;
switch (hid_code) {
case 0x00:
@@ -496,87 +521,85 @@ static inline int int_clamp(int val, int vmin, int
vmax)
return val;
}
-static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len)
+static int usb_mouse_poll(USBHIDState *hs, uint8_t *buf, int len)
{
int dx, dy, dz, b, l;
- USBPointerState *s = &hs->ptr;
- USBPointerEvent *e;
-
- if (s->head == s->tail)
- return USB_RET_NAK;
+ USBMouseState *s = &hs->ptr;
if (!s->mouse_grabbed) {
- s->eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, hs,
- 0, "QEMU USB Pointer");
+ s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, hs,
+ 0, "QEMU USB Mouse");
s->mouse_grabbed = 1;
}
- e = &s->queue[s->head];
+ dx = int_clamp(s->dx, -127, 127);
+ dy = int_clamp(s->dy, -127, 127);
+ dz = int_clamp(s->dz, -127, 127);
- dx = int_clamp(e->xdx, -128, 127);
- dy = int_clamp(e->ydy, -128, 127);
- dz = int_clamp(e->dz, -128, 127);
+ s->dx -= dx;
+ s->dy -= dy;
+ s->dz -= dz;
- if (s->xyrel) {
- e->xdx -= dx;
- e->ydy -= dy;
- }
- e->dz -= dz;
-
- if (!(e->dz ||
- (s->xyrel && (e->xdx || e->ydy)))) {
- /* that deals with this event */
- QUEUE_INCR(s->head);
- }
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
b = 0;
- if (e->buttons_state & MOUSE_EVENT_LBUTTON)
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
b |= 0x01;
- if (e->buttons_state & MOUSE_EVENT_RBUTTON)
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
b |= 0x02;
- if (e->buttons_state & MOUSE_EVENT_MBUTTON)
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
b |= 0x04;
- switch (hs->kind) {
- case USB_MOUSE:
- l = 0;
- if (len > l)
- buf[l ++] = b;
- if (len > l)
- buf[l ++] = dx;
- if (len > l)
- buf[l ++] = dy;
- if (len > l)
- buf[l ++] = dz;
- break;
-
- case USB_TABLET:
- /* Appears we have to invert the wheel direction */
- dz = 0 - dz;
-
- buf[0] = b;
- buf[1] = dx & 0xff;
- buf[2] = dx >> 8;
- buf[3] = dy & 0xff;
- buf[4] = dy >> 8;
- buf[5] = dz;
- l = 6;
- break;
+ l = 0;
+ if (len > l)
+ buf[l ++] = b;
+ if (len > l)
+ buf[l ++] = dx;
+ if (len > l)
+ buf[l ++] = dy;
+ if (len > l)
+ buf[l ++] = dz;
+ return l;
+}
- default:
- abort();
+static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+ int dz, b, l;
+ USBMouseState *s = &hs->ptr;
+
+ if (!s->mouse_grabbed) {
+ s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, hs,
+ 1, "QEMU USB Tablet");
+ s->mouse_grabbed = 1;
}
+ dz = int_clamp(s->dz, -127, 127);
+ s->dz -= dz;
+
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ buf[0] = b;
+ buf[1] = s->x & 0xff;
+ buf[2] = s->x >> 8;
+ buf[3] = s->y & 0xff;
+ buf[4] = s->y >> 8;
+ buf[5] = dz;
+ l = 6;
+
return l;
}
static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len)
{
- if (!s->changed)
- return USB_RET_NAK;
-
- s->changed= 0;
-
if (len < 2)
return 0;
@@ -603,11 +626,16 @@ static int usb_keyboard_write(USBKeyboardState *s,
uint8_t *buf, int len)
return 0;
}
-static void usb_pointer_handle_reset(USBDevice *dev)
+static void usb_mouse_handle_reset(USBDevice *dev)
{
USBHIDState *s = (USBHIDState *)dev;
- s->ptr.head= s->ptr.tail = 0;
+ s->ptr.dx = 0;
+ s->ptr.dy = 0;
+ s->ptr.dz = 0;
+ s->ptr.x = 0;
+ s->ptr.y = 0;
+ s->ptr.buttons_state = 0;
s->protocol = 1;
}
@@ -753,8 +781,10 @@ static int usb_hid_handle_control(USBDevice *dev,
int request, int value,
}
break;
case GET_REPORT:
- if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
- ret = usb_pointer_poll(s, data, length);
+ if (s->kind == USB_MOUSE)
+ ret = usb_mouse_poll(s, data, length);
+ else if (s->kind == USB_TABLET)
+ ret = usb_tablet_poll(s, data, length);
else if (s->kind == USB_KEYBOARD)
ret = usb_keyboard_poll(&s->kbd, data, length);
break;
@@ -801,12 +831,25 @@ static int usb_hid_handle_data(USBDevice *dev,
USBPacket *p)
case USB_TOKEN_IN:
if (p->devep == 1) {
/* TODO: Implement finite idle delays. */
- if (!s->idle)
+ if (!(s->changed || s->idle))
return USB_RET_NAK;
- if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
- ret = usb_pointer_poll(s, p->data, p->len);
- else if (s->kind == USB_KEYBOARD)
+ if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
+ USBMouseState *ms = &s->ptr;
+ if (head != tail) {
+ ms->buttons_state = head->button_state;
+ head = head->next;
+ }
+ else
+ s->changed = 0;
+ if (s->kind == USB_MOUSE)
+ ret = usb_mouse_poll(s, p->data, p->len);
+ else
+ ret = usb_tablet_poll(s, p->data, p->len);
+ }
+ else if (s->kind == USB_KEYBOARD) {
+ s->changed = 0;
ret = usb_keyboard_poll(&s->kbd, p->data, p->len);
+ }
} else {
goto fail;
}
@@ -830,9 +873,17 @@ static void usb_hid_handle_destroy(USBDevice *dev)
qemu_free(s);
}
-static USBDevice *usb_pointer_init(int kind, int xyrel, const char
*devname)
+USBDevice *usb_tablet_init(void)
{
USBHIDState *s;
+ int i;
+
+ for (i = 0; i < 19; i++) {
+ mousequeue[i].button_state = 0;
+ mousequeue[i].next = &(mousequeue[i + 1]);
+ }
+ mousequeue[i].button_state = 0;
+ mousequeue[i].next = mousequeue;
s = qemu_mallocz(sizeof(USBHIDState));
if (!s)
@@ -840,31 +891,48 @@ static USBDevice *usb_pointer_init(int kind, int
xyrel, const char *devname)
s->dev.speed = USB_SPEED_FULL;
s->dev.handle_packet = usb_generic_handle_packet;
- s->dev.handle_reset = usb_pointer_handle_reset;
+ s->dev.handle_reset = usb_mouse_handle_reset;
s->dev.handle_control = usb_hid_handle_control;
s->dev.handle_data = usb_hid_handle_data;
s->dev.handle_destroy = usb_hid_handle_destroy;
-
- s->kind = kind;
- s->ptr.xyrel = xyrel;
- usb_pointer_handle_reset((USBDevice*)s);
-
+ s->kind = USB_TABLET;
/* Force poll routine to be run and grab input the first time. */
- usb_pointer_event_clear(&s->ptr.queue[0], 0);
- s->ptr.tail = 1;
+ s->changed = 1;
- pstrcpy(s->dev.devname, sizeof(s->dev.devname), devname);
- return (USBDevice *)s;
-}
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
-USBDevice *usb_tablet_init(void)
-{
- return usb_pointer_init(USB_TABLET, 0, "QEMU USB Tablet");
+ return (USBDevice *)s;
}
USBDevice *usb_mouse_init(void)
{
- return usb_pointer_init(USB_MOUSE, 1, "QEMU USB Mouse");
+ USBHIDState *s;
+ int i;
+
+ for (i = 0; i < 19; i++) {
+ mousequeue[i].button_state = 0;
+ mousequeue[i].next = &(mousequeue[i + 1]);
+ }
+ mousequeue[i].button_state = 0;
+ mousequeue[i].next = mousequeue;
+
+ s = qemu_mallocz(sizeof(USBHIDState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_mouse_handle_reset;
+ s->dev.handle_control = usb_hid_handle_control;
+ s->dev.handle_data = usb_hid_handle_data;
+ s->dev.handle_destroy = usb_hid_handle_destroy;
+ s->kind = USB_MOUSE;
+ /* Force poll routine to be run and grab input the first time. */
+ s->changed = 1;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
+
+ return (USBDevice *)s;
}
USBDevice *usb_keyboard_init(void)
@@ -882,7 +950,6 @@ USBDevice *usb_keyboard_init(void)
s->dev.handle_data = usb_hid_handle_data;
s->dev.handle_destroy = usb_hid_handle_destroy;
s->kind = USB_KEYBOARD;
- s->kbd.changed= 0;
pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard");
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|