From patchwork Thu Sep 20 15:38:07 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 185424 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 856D72C0094 for ; Fri, 21 Sep 2012 01:37:14 +1000 (EST) Received: from localhost ([::1]:46295 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TEioS-0002OX-L9 for incoming@patchwork.ozlabs.org; Thu, 20 Sep 2012 11:37:12 -0400 Received: from eggs.gnu.org ([208.118.235.92]:54709) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TEioE-0002O8-9m for qemu-devel@nongnu.org; Thu, 20 Sep 2012 11:37:06 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TEio5-0001w8-KY for qemu-devel@nongnu.org; Thu, 20 Sep 2012 11:36:58 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37191) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TEio5-0001vx-CE for qemu-devel@nongnu.org; Thu, 20 Sep 2012 11:36:49 -0400 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q8KFamQY007545 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 20 Sep 2012 11:36:48 -0400 Received: from shalem.localdomain.com (vpn1-6-245.ams2.redhat.com [10.36.6.245]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q8KFalnV025752; Thu, 20 Sep 2012 11:36:47 -0400 From: Hans de Goede To: Gerd Hoffmann Date: Thu, 20 Sep 2012 17:38:07 +0200 Message-Id: <1348155487-9119-1-git-send-email-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.11 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: Hans de Goede , shawn.starr@rogers.com, qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH] ehci: Fix interrupt packet MULT handling X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org There are several issues with our handling of the MULT epcap field of interrupt qhs, which this patch fixes. 1) When we don't execute a transaction because of the transaction counter being 0, p->async stays EHCI_ASYNC_NONE, and the next time we process the same qtd we hit an assert in ehci_state_fetchqtd because of this. Even though I believe that this is caused by 3 below, this patch still removes the assert, as that can still happen without 3, when multiple packets are queued for the same interrupt ep. 2) We only *check* the transaction counter from ehci_state_execute, any packets queued up by fill_queue bypass this check. This is fixed by not calling fill_queue for interrupt packets. 3) Some versions of Windows set the MULT field of the qh to 0, which is a clear violation of the EHCI spec, but still they do it. This means that we will never execute a qtd for these, making interrupt ep-s on USB-2 devices not work, and after recent changes, triggering 1). So far we've stored the transaction counter in our copy of the mult field, but with this beginnig at 0 already when dealing with these version of windows this won't work. So this patch adds a transact_ctr field to our qh struct, and sets this to the MULT field value on fetchqh. When the MULT field value is 0, we set it to 4. Assuming that windows gets way with setting it to 0, by the actual hardware going horizontal on a 1 -> 0 transition, which will give it 4 transactions (MULT goes from 0 - 3). Note that we cannot stop on detecting the 1 -> 0 transition, as our decrement of the transaction counter, and checking for it are done in 2 different places. Reported-by: Shawn Starr Signed-off-by: Hans de Goede --- hw/usb/hcd-ehci.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 48a1b09..3acd881a 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -373,6 +373,7 @@ struct EHCIQueue { uint32_t seen; uint64_t ts; int async; + int transact_ctr; /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) @@ -1837,6 +1838,10 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } q->qh = qh; + q->transact_ctr = get_field(q->qh.epcap, QH_EPCAP_MULT); + if (q->transact_ctr == 0) /* Guest bug in some versions of windows */ + q->transact_ctr = 4; + if (q->dev == NULL) { q->dev = ehci_find_device(q->ehci, devaddr); } @@ -2014,11 +2019,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q) } else if (p != NULL) { switch (p->async) { case EHCI_ASYNC_NONE: - /* Should never happen packet should at least be initialized */ - assert(0); - break; case EHCI_ASYNC_INITIALIZED: - /* Previously nacked packet (likely interrupt ep) */ + /* Not yet executed (MULT), or previously nacked (int) packet */ ehci_set_state(q->ehci, q->async, EST_EXECUTE); break; case EHCI_ASYNC_INFLIGHT: @@ -2107,15 +2109,12 @@ static int ehci_state_execute(EHCIQueue *q) // TODO verify enough time remains in the uframe as in 4.4.1.1 // TODO write back ptr to async list when done or out of time - // TODO Windows does not seem to ever set the MULT field - if (!q->async) { - int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); - if (!transactCtr) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - goto out; - } + /* 4.10.3, bottom of page 82, go horizontal on transaction counter == 0 */ + if (!q->async && q->transact_ctr == 0) { + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + again = 1; + goto out; } if (q->async) { @@ -2132,7 +2131,11 @@ static int ehci_state_execute(EHCIQueue *q) trace_usb_ehci_packet_action(p->queue, p, "async"); p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; + if (q->async) { + again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; + } else { + again = 1; + } goto out; } @@ -2152,13 +2155,9 @@ static int ehci_state_executing(EHCIQueue *q) ehci_execute_complete(q); - // 4.10.3 - if (!q->async) { - int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); - transactCtr--; - set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT); - // 4.10.3, bottom of page 82, should exit this state when transaction - // counter decrements to 0 + /* 4.10.3 */ + if (!q->async && q->transact_ctr > 0) { + q->transact_ctr--; } /* 4.10.5 */