summaryrefslogtreecommitdiff
path: root/hw/usb/hcd-uhci.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-08-17 15:24:49 +0200
committerGerd Hoffmann <kraxel@redhat.com>2012-08-31 11:55:17 +0200
commit0132b4b6595423c92f54d7e0b172b5d73aaa8375 (patch)
treefe8b47682db22a2829fccd10f9b408689beaeb39 /hw/usb/hcd-uhci.c
parentda9fbe76a0639c529f9678aabcc052dfe4cd9cc4 (diff)
downloadqemu-0132b4b6595423c92f54d7e0b172b5d73aaa8375.tar.gz
usb: Halt ep queue en cancel pending packets on a packet error
For controllers which queue up more then 1 packet at a time, we must halt the ep queue, and inside the controller code cancel all pending packets on an error. There are multiple reasons for this: 1) Guests expect the controllers to halt ep queues on error, so that they get the opportunity to cancel transfers which the scheduled after the failing one, before processing continues 2) Not cancelling queued up packets after a failed transfer also messes up the controller state machine, in the case of EHCI causing the following assert to trigger: "assert(p->qtdaddr == q->qtdaddr)" at hcd-ehci.c:2075 3) For bulk endpoints with pipelining enabled (redirection to a real USB device), we must cancel all the transfers after this a failed one so that: a) If they've completed already, they are not processed further causing more stalls to be reported, originating from the same failed transfer b) If still in flight, they are cancelled before the guest does a clear stall, otherwise the guest and device can loose sync! Note this patch only touches the ehci and uhci controller changes, since AFAIK no other controllers actually queue up multiple transfer. If I'm wrong on this other controllers need to be updated too! Also note that this patch was heavily tested with the ehci code, where I had a reproducer for a device causing a transfer to fail. The uhci code is not tested with actually failing transfers and could do with a thorough review! Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb/hcd-uhci.c')
-rw-r--r--hw/usb/hcd-uhci.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 1ace2a41da..898773414e 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -748,6 +748,22 @@ 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;