On Thu, Sep 20, 2012 at 3:38 PM, Hans de Goede <hdego...@redhat.com> wrote:
> 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 <shawn.st...@rogers.com>
> Signed-off-by: Hans de Goede <hdego...@redhat.com>
> ---
>  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;

Missing braces, please use checkpatch.pl.

> +
>      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 */
> --
> 1.7.12
>
>

Reply via email to