5544c1b
From d6fe9953a8277a54ae7f4cefa192b49d9bf99e3d Mon Sep 17 00:00:00 2001
93b7e38
From: Hans de Goede <hdegoede@redhat.com>
93b7e38
Date: Thu, 20 Sep 2012 16:55:02 +0200
5544c1b
Subject: [PATCH] ehci: Fix interrupt packet MULT handling
93b7e38
93b7e38
There are several issues with our handling of the MULT epcap field
93b7e38
of interrupt qhs, which this patch fixes.
93b7e38
93b7e38
1) When we don't execute a transaction because of the transaction counter
93b7e38
being 0, p->async stays EHCI_ASYNC_NONE, and the next time we process the
93b7e38
same qtd we hit an assert in ehci_state_fetchqtd because of this. Even though
93b7e38
I believe that this is caused by 3 below, this patch still removes the assert,
93b7e38
as that can still happen without 3, when multiple packets are queued for the
93b7e38
same interrupt ep.
93b7e38
93b7e38
2) We only *check* the transaction counter from ehci_state_execute, any
93b7e38
packets queued up by fill_queue bypass this check. This is fixed by not calling
93b7e38
fill_queue for interrupt packets.
93b7e38
93b7e38
3) Some versions of Windows set the MULT field of the qh to 0, which is a
93b7e38
clear violation of the EHCI spec, but still they do it. This means that we
93b7e38
will never execute a qtd for these, making interrupt ep-s on USB-2 devices
93b7e38
not work, and after recent changes, triggering 1).
93b7e38
93b7e38
So far we've stored the transaction counter in our copy of the mult field,
93b7e38
but with this beginnig at 0 already when dealing with these version of windows
93b7e38
this won't work. So this patch adds a transact_ctr field to our qh struct,
93b7e38
and sets this to the MULT field value on fetchqh. When the MULT field value
93b7e38
is 0, we set it to 4. Assuming that windows gets way with setting it to 0,
93b7e38
by the actual hardware going horizontal on a 1 -> 0 transition, which will
93b7e38
give it 4 transactions (MULT goes from 0 - 3).
93b7e38
93b7e38
Note that we cannot stop on detecting the 1 -> 0 transition, as our decrement
93b7e38
of the transaction counter, and checking for it are done in 2 different places.
93b7e38
93b7e38
Reported-by: Shawn Starr <shawn.starr@rogers.com>
93b7e38
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
93b7e38
---
93b7e38
 hw/usb/hcd-ehci.c | 39 +++++++++++++++++++--------------------
93b7e38
 1 file changed, 19 insertions(+), 20 deletions(-)
93b7e38
93b7e38
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
5544c1b
index 6a5da84..46f6d99 100644
93b7e38
--- a/hw/usb/hcd-ehci.c
93b7e38
+++ b/hw/usb/hcd-ehci.c
93b7e38
@@ -373,6 +373,7 @@ struct EHCIQueue {
93b7e38
     uint32_t seen;
93b7e38
     uint64_t ts;
93b7e38
     int async;
93b7e38
+    int transact_ctr;
93b7e38
 
93b7e38
     /* cached data from guest - needs to be flushed
93b7e38
      * when guest removes an entry (doorbell, handshake sequence)
93b7e38
@@ -1837,6 +1838,10 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
93b7e38
     }
93b7e38
     q->qh = qh;
93b7e38
 
93b7e38
+    q->transact_ctr = get_field(q->qh.epcap, QH_EPCAP_MULT);
93b7e38
+    if (q->transact_ctr == 0) /* Guest bug in some versions of windows */
93b7e38
+        q->transact_ctr = 4;
93b7e38
+
93b7e38
     if (q->dev == NULL) {
93b7e38
         q->dev = ehci_find_device(q->ehci, devaddr);
93b7e38
     }
93b7e38
@@ -2014,11 +2019,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
93b7e38
     } else if (p != NULL) {
93b7e38
         switch (p->async) {
93b7e38
         case EHCI_ASYNC_NONE:
93b7e38
-            /* Should never happen packet should at least be initialized */
93b7e38
-            assert(0);
93b7e38
-            break;
93b7e38
         case EHCI_ASYNC_INITIALIZED:
93b7e38
-            /* Previously nacked packet (likely interrupt ep) */
93b7e38
+            /* Not yet executed (MULT), or previously nacked (int) packet */
93b7e38
             ehci_set_state(q->ehci, q->async, EST_EXECUTE);
93b7e38
             break;
93b7e38
         case EHCI_ASYNC_INFLIGHT:
93b7e38
@@ -2107,15 +2109,12 @@ static int ehci_state_execute(EHCIQueue *q)
93b7e38
 
93b7e38
     // TODO verify enough time remains in the uframe as in 4.4.1.1
93b7e38
     // TODO write back ptr to async list when done or out of time
93b7e38
-    // TODO Windows does not seem to ever set the MULT field
93b7e38
 
93b7e38
-    if (!q->async) {
93b7e38
-        int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
93b7e38
-        if (!transactCtr) {
93b7e38
-            ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
93b7e38
-            again = 1;
93b7e38
-            goto out;
93b7e38
-        }
93b7e38
+    /* 4.10.3, bottom of page 82, go horizontal on transaction counter == 0 */
93b7e38
+    if (!q->async && q->transact_ctr == 0) {
93b7e38
+        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
93b7e38
+        again = 1;
93b7e38
+        goto out;
93b7e38
     }
93b7e38
 
93b7e38
     if (q->async) {
93b7e38
@@ -2132,7 +2131,11 @@ static int ehci_state_execute(EHCIQueue *q)
93b7e38
         trace_usb_ehci_packet_action(p->queue, p, "async");
93b7e38
         p->async = EHCI_ASYNC_INFLIGHT;
93b7e38
         ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
93b7e38
-        again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
93b7e38
+        if (q->async) {
93b7e38
+            again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
93b7e38
+        } else {
93b7e38
+            again = 1;
93b7e38
+        }
93b7e38
         goto out;
93b7e38
     }
93b7e38
 
93b7e38
@@ -2152,13 +2155,9 @@ static int ehci_state_executing(EHCIQueue *q)
93b7e38
 
93b7e38
     ehci_execute_complete(q);
93b7e38
 
93b7e38
-    // 4.10.3
93b7e38
-    if (!q->async) {
93b7e38
-        int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
93b7e38
-        transactCtr--;
93b7e38
-        set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
93b7e38
-        // 4.10.3, bottom of page 82, should exit this state when transaction
93b7e38
-        // counter decrements to 0
93b7e38
+    /* 4.10.3 */
93b7e38
+    if (!q->async && q->transact_ctr > 0) {
93b7e38
+        q->transact_ctr--;
93b7e38
     }
93b7e38
 
93b7e38
     /* 4.10.5 */
93b7e38
-- 
5544c1b
1.7.12.1
93b7e38