# HG changeset patch # User kfraser@localhost.localdomain # Node ID 60bbcf799384d779c2a561b9d9ba30f28e31d970 # Parent fb3cb6f52a2905be938559529ae43b6ba990c878 [HVM] qemu mouse: Adds support for USB mouse/tablet status check and restricts Universal Host Controller interrupt generating when received NAK in interrupt transfer. According to usb spec, USB mouse/tablet device returns NAK to host controller if its status does not alter in interrupt transfer. And UHC should leave a TD active when receiving NAK and execute this incompleted TD in a subseqent frame. UHC only generates an interrupt on complete after the TD with ICO bit is completed. This patch make UHC & USB mouse/tablet behave consistently with spec. Signed-off-by: Xinmei Huang Index: ioemu/hw/usb-hid.c =================================================================== --- ioemu.orig/hw/usb-hid.c 2007-05-09 14:11:27.000000000 +0100 +++ ioemu/hw/usb-hid.c 2007-05-09 14:12:06.000000000 +0100 @@ -39,6 +39,7 @@ int x, y; int kind; int mouse_grabbed; + int status_changed; QEMUPutMouseEntry *eh_entry; } USBMouseState; @@ -232,6 +233,7 @@ s->dy += dy1; s->dz += dz1; s->buttons_state = buttons_state; + s->status_changed = 1; } static void usb_tablet_event(void *opaque, @@ -243,6 +245,7 @@ s->y = y; s->dz += dz; s->buttons_state = buttons_state; + s->status_changed = 1; } static inline int int_clamp(int val, int vmin, int vmax) @@ -485,10 +488,16 @@ switch(p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, p->data, p->len); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, p->data, p->len); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, p->data, p->len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, p->data, p->len); + + if (!s->status_changed) + ret = USB_RET_NAK; + else + s->status_changed = 0; + } else { goto fail; } @@ -570,6 +579,7 @@ s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_TABLET; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); @@ -593,6 +603,7 @@ s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_MOUSE; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); Index: ioemu/hw/usb-uhci.c =================================================================== --- ioemu.orig/hw/usb-uhci.c 2007-05-09 14:12:05.000000000 +0100 +++ ioemu/hw/usb-uhci.c 2007-05-09 14:12:06.000000000 +0100 @@ -43,9 +43,15 @@ #define TD_CTRL_IOC (1 << 24) #define TD_CTRL_ACTIVE (1 << 23) #define TD_CTRL_STALL (1 << 22) +#define TD_CTRL_BUFFER (1 << 21) #define TD_CTRL_BABBLE (1 << 20) #define TD_CTRL_NAK (1 << 19) #define TD_CTRL_TIMEOUT (1 << 18) +#define TD_CTRL_BITSTUFF \ + (1 << 17) +#define TD_CTRL_MASK \ + (TD_CTRL_BITSTUFF | TD_CTRL_TIMEOUT | TD_CTRL_NAK \ + | TD_CTRL_BABBLE | TD_CTRL_BUFFER | TD_CTRL_STALL) #define UHCI_PORT_RESET (1 << 9) #define UHCI_PORT_LSDA (1 << 8) @@ -431,13 +437,13 @@ uint8_t pid; int len, max_len, err, ret; - /* ??? This is wrong for async completion. */ - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; + if (!(td->ctrl & TD_CTRL_ACTIVE)) { + ret = 1; + goto out; } - - if (!(td->ctrl & TD_CTRL_ACTIVE)) - return 1; + + /* Clear TD's status field explicitly */ + td->ctrl = td->ctrl & (~TD_CTRL_MASK); /* TD is active */ max_len = ((td->token >> 21) + 1) & 0x7ff; @@ -493,11 +499,13 @@ /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); - return -1; + ret = -1; + goto out; } } if (ret == USB_RET_ASYNC) { - return 2; + ret = 2; + goto out; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -509,10 +517,12 @@ len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ - return 1; + ret = 1; + goto out; } else { /* success */ - return 0; + ret = 0; + goto out; } } else { switch(ret) { @@ -531,23 +541,34 @@ } td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); - return 1; + ret = 1; + goto out; case USB_RET_NAK: td->ctrl |= TD_CTRL_NAK; if (pid == USB_TOKEN_SETUP) goto do_timeout; - return 1; + ret = 1; + goto out; case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; - return 1; + ret = 1; + goto out; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; /* frame interrupted */ - return -1; + ret = -1; + goto out; } } + +out: + /* If TD is inactive and IOC bit set to 1 then update int_mask */ + if ((td->ctrl & TD_CTRL_IOC) && (!(td->ctrl & TD_CTRL_ACTIVE))) { + *int_mask |= 0x01; + } + return ret; } static void uhci_async_complete_packet(USBPacket * packet, void *opaque)