diff --git a/hw/usb.h b/hw/usb.h index 435cd42a03..ead03c9f6b 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -45,6 +45,7 @@ #define USB_RET_IOERROR (-5) #define USB_RET_ASYNC (-6) #define USB_RET_ADD_TO_QUEUE (-7) +#define USB_RET_REMOVE_FROM_QUEUE (-8) #define USB_SPEED_LOW 0 #define USB_SPEED_FULL 1 diff --git a/hw/usb/core.c b/hw/usb/core.c index 014e3ac090..5a97a0e412 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -442,8 +442,14 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) usb_packet_check_state(p, USB_PACKET_ASYNC); usb_packet_complete_one(dev, p); - while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) { + while (!QTAILQ_EMPTY(&ep->queue)) { p = QTAILQ_FIRST(&ep->queue); + if (ep->halted) { + /* Empty the queue on a halt */ + p->result = USB_RET_REMOVE_FROM_QUEUE; + dev->port->ops->complete(dev->port, p); + continue; + } if (p->state == USB_PACKET_ASYNC) { break; } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d11311e87f..74a2587eba 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1457,8 +1457,15 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) } p = container_of(packet, EHCIPacket, packet); - trace_usb_ehci_packet_action(p->queue, p, "wakeup"); assert(p->async == EHCI_ASYNC_INFLIGHT); + + if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { + trace_usb_ehci_packet_action(p->queue, p, "remove"); + ehci_free_packet(p); + return; + } + + trace_usb_ehci_packet_action(p->queue, p, "wakeup"); p->async = EHCI_ASYNC_FINISHED; p->usb_status = packet->result; @@ -2216,19 +2223,6 @@ static int ehci_state_writeback(EHCIQueue *q) * bit is clear. */ if (q->qh.token & QTD_TOKEN_HALT) { - /* - * We should not do any further processing on a halted queue! - * This is esp. important for bulk endpoints with pipelining enabled - * (redirection to a real USB device), where we must cancel all the - * transfers after this one so that: - * 1) If they've completed already, they are not processed further - * causing more stalls, originating from the same failed transfer - * 2) If still in flight, they are cancelled before the guest does - * a clear stall, otherwise the guest and device can loose sync! - */ - while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { - ehci_free_packet(p); - } ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); again = 1; } else { diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 46e544b910..00dc9d538f 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -744,22 +744,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ return TD_RESULT_COMPLETE; out: - /* - * We should not do any further processing on a queue with errors! - * This is esp. important for bulk endpoints with pipelining enabled - * (redirection to a real USB device), where we must cancel all the - * transfers after this one so that: - * 1) If they've completed already, they are not processed further - * causing more stalls, originating from the same failed transfer - * 2) If still in flight, they are cancelled before the guest does - * a clear stall, otherwise the guest and device can loose sync! - */ - while (!QTAILQ_EMPTY(&async->queue->asyncs)) { - UHCIAsync *as = QTAILQ_FIRST(&async->queue->asyncs); - uhci_async_unlink(as); - uhci_async_cancel(as); - } - switch(ret) { case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; @@ -918,6 +902,12 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) UHCIAsync *async = container_of(packet, UHCIAsync, packet); UHCIState *s = async->queue->uhci; + if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { + uhci_async_unlink(async); + uhci_async_cancel(async); + return; + } + if (async->isoc) { UHCI_TD td; uint32_t link = async->td; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index e8929a0785..1f437cc15f 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2839,6 +2839,10 @@ static void xhci_complete(USBPort *port, USBPacket *packet) { XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); + if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { + xhci_ep_nuke_one_xfer(xfer); + return; + } xhci_complete_packet(xfer, packet->result); xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid); }