From e4a11fa81d4875a6778d4d0e8e4c24df9d40a861 Mon Sep 17 00:00:00 2001
From: Hans de Goede <hdegoede@redhat.com>
Date: Sun, 4 Mar 2012 11:57:38 +0100
Subject: [PATCH 16/35] usb-ehci: Handle ISO packets failing with an error
 other then NAK

RH-Author: Hans de Goede <hdegoede@redhat.com>
Message-id: <1330862278-22314-2-git-send-email-hdegoede@redhat.com>
Patchwork-id: 37916
O-Subject: [PATCH 01/21] usb-ehci: Handle ISO packets failing with an error other then NAK
Bugzilla: 758104
RH-Acked-by: Hans de Goede <hdegoede@redhat.com>
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
RH-Acked-by: Alon Levy <alevy@redhat.com>
RH-Acked-by: Markus Armbruster <armbru@redhat.com>

Before this patch the ehci code was not checking for any other errors other
then USB_RET_NAK. This causes 2 problems:
1) Other errors are not reported to the guest.
2) When transactions with the ITD_XACT_IOC bit set completing with another
   error would not result in USBSTS_INT getting set.

I hit this problem when unplugging devices while iso data was streaming from
the device to the guest. When this happens it takes a while for the guest to
process the unplugging and remove ISO transactions from the ehci schedule, in
the mean time these transactions would complete with a result of USB_RET_NODEV,
which was not handled. This lead to the Linux guest's usb subsystem "hanging",
that is it would no longer see new usb devices getting plugged in and running
for example lsusb would lead to a stuck (D state) lsusb process. This patch
fixes this.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
(cherry picked from commit df7871851eb327af0a612895738c0e1854fde00b)
---
 hw/usb-ehci.c |   22 +++++++++++++++++++---
 1 files changed, 19 insertions(+), 3 deletions(-)

Signed-off-by: Michal Novotny <minovotn@redhat.com>
---
 hw/usb-ehci.c |   22 +++++++++++++++++++---
 1 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index d78d9ee..cb6ec2f 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -1529,11 +1529,27 @@ static int ehci_process_itd(EHCIState *ehci,
                     }
                     set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
                 }
-
-                if (itd->transact[i] & ITD_XACT_IOC) {
-                    ehci_record_interrupt(ehci, USBSTS_INT);
+            } else {
+                switch (ret) {
+                default:
+                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
+                    /* Fall through */
+                case USB_RET_NODEV:
+                    /* 3.3.2: XACTERR is only allowed on IN transactions */
+                    if (dir) {
+                        itd->transact[i] |= ITD_XACT_XACTERR;
+                        ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    }
+                    break;
+                case USB_RET_BABBLE:
+                    itd->transact[i] |= ITD_XACT_BABBLE;
+                    ehci_record_interrupt(ehci, USBSTS_ERRINT);
+                    break;
                 }
             }
+            if (itd->transact[i] & ITD_XACT_IOC) {
+                ehci_record_interrupt(ehci, USBSTS_INT);
+            }
             itd->transact[i] &= ~ITD_XACT_ACTIVE;
         }
     }
-- 
1.7.7.6

