Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/nick-nhusb]: src/sys/dev/usb Deal with ZLP (zero length packets) properly



details:   https://anonhg.NetBSD.org/src/rev/80acfc565991
branches:  nick-nhusb
changeset: 334466:80acfc565991
user:      skrll <skrll%NetBSD.org@localhost>
date:      Fri Apr 01 14:20:05 2016 +0000

description:
Deal with ZLP (zero length packets) properly

diffstat:

 sys/dev/usb/ohci.c |  107 ++++++++++++++++++++++++++++++++++------------------
 1 files changed, 69 insertions(+), 38 deletions(-)

diffs (211 lines):

diff -r 9589903d5e36 -r 80acfc565991 sys/dev/usb/ohci.c
--- a/sys/dev/usb/ohci.c        Fri Apr 01 14:17:24 2016 +0000
+++ b/sys/dev/usb/ohci.c        Fri Apr 01 14:20:05 2016 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ohci.c,v 1.254.2.67 2016/04/01 14:17:24 skrll Exp $    */
+/*     $NetBSD: ohci.c,v 1.254.2.68 2016/04/01 14:20:05 skrll Exp $    */
 
 /*
  * Copyright (c) 1998, 2004, 2005, 2012 The NetBSD Foundation, Inc.
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.254.2.67 2016/04/01 14:17:24 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.254.2.68 2016/04/01 14:20:05 skrll Exp $");
 
 #include "opt_usb.h"
 
@@ -546,8 +546,12 @@
 
        DPRINTFN(8, "xfer %p nstd %d mps %d", xfer, nstd, mps, 0);
 
+       /*
+        * Assign next for the len == 0 case where we don't go through the
+        * main loop.
+        */
        len = alen;
-       cur = ohci_alloc_std(sc);
+       cur = next = ohci_alloc_std(sc);
        if (cur == NULL)
                goto nomem;
 
@@ -557,16 +561,24 @@
            (rd ? OHCI_TD_IN : OHCI_TD_OUT) |
            OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR);
 
-       for (size_t j = 0;;) {
+       size_t curoffs = 0;
+       size_t j = 0;
+       for (; j < ox->ox_nstd && len != 0;) {
                ox->ox_stds[j++] = cur;
                next = ohci_alloc_std(sc);
                if (next == NULL)
                        goto nomem;
-
-               /* The OHCI hardware can handle at most one page crossing. */
-               if (OHCI_PAGE(dataphys) == dataphysend ||
-                   OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == dataphysend) {
-                       /* we can handle it in this TD */
+               curlen = 0;
+               dataphys = DMAADDR(dma, curoffs);
+               dataphysend = DMAADDR(dma, curoffs + len - 1);
+
+               ohci_physaddr_t sphyspg = OHCI_PAGE(dataphys);
+               ohci_physaddr_t ephyspg = OHCI_PAGE(dataphysend);
+               /*
+                * The OHCI hardware can handle at most one page
+                * crossing per TD
+                */
+               if (sphyspg == ephyspg || sphyspg + 1 == ephyspg) {
                        curlen = len;
                } else {
                        /* must use multiple TDs, fill as much as possible. */
@@ -574,8 +586,8 @@
                            (dataphys & (OHCI_PAGE_SIZE - 1));
                        /* the length must be a multiple of the max size */
                        curlen -= curlen % mps;
-                       KASSERT(curlen != 0);
                }
+               KASSERT(curlen != 0);
                DPRINTFN(4, "dataphys=0x%08x dataphysend=0x%08x "
                    "len=%d curlen=%d", dataphys, dataphysend, len, curlen);
                len -= curlen;
@@ -591,31 +603,37 @@
 
                DPRINTFN(10, "cbp=0x%08x be=0x%08x", dataphys,
                    dataphys + curlen - 1, 0, 0);
-               if (len == 0)
-                       break;
+
+               curoffs += curlen;
+               len -= curlen;
+
+               if (len != 0)
+                       cur = next;
+
                DPRINTFN(10, "extend chain", 0, 0, 0, 0);
-               dataphys += curlen;
-               cur = next;
        }
-       if (!rd && (flags & USBD_FORCE_SHORT_XFER) &&
-           alen % mps == 0) {
+
+       /* Allocate a TD for a 0 length transfer at the end. */
+       if (!rd && (flags & USBD_FORCE_SHORT_XFER)) {
                /* Force a 0 length transfer at the end. */
 
+               KASSERT(j < ox->ox_nstd);
+               KASSERT(next != NULL);
                cur = next;
-               next = ohci_alloc_std(sc);
-               if (next == NULL)
-                       goto nomem;
+               ox->ox_stds[j++] = cur;
 
                cur->td.td_flags = tdflags;
                cur->td.td_cbp = 0; /* indicate 0 length packet */
-               cur->td.td_nexttd = HTOO32(next->physaddr);
+               cur->td.td_nexttd = 0;
                cur->td.td_be = ~0;
-               cur->nexttd = next;
+               cur->nexttd = NULL;
                cur->len = 0;
                cur->flags = 0;
                cur->xfer = xfer;
 
                DPRINTFN(2, "add 0 xfer", 0, 0, 0, 0);
+       } else {
+               ohci_free_std(sc, next);
        }
 
        return USBD_NORMAL_COMPLETION;
@@ -667,28 +685,38 @@
 
        int mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize);
 
+       /*
+        * Assign next for the len == 0 case where we don't go through the
+        * main loop.
+        */
        len = alen;
-       cur = sp;
-
-       dataphys = DMAADDR(dma, 0);
-       dataphysend = OHCI_PAGE(dataphys + len - 1);
+       cur = next = sp;
+
        usb_syncmem(dma, 0, len,
            rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
        const uint32_t tdflags = HTOO32(
            (rd ? OHCI_TD_IN : OHCI_TD_OUT) |
            OHCI_TD_NOCC | OHCI_TD_TOGGLE_CARRY | OHCI_TD_NOINTR);
 
-       for (size_t j = 1;;) {
+       size_t curoffs = 0;
+       for (size_t j = 1; len != 0;) {
                if (j == ox->ox_nstd)
                        next = NULL;
                else
                        next = ox->ox_stds[j++];
                KASSERT(next != cur);
 
-               /* The OHCI hardware can handle at most one page crossing. */
-               if (OHCI_PAGE(dataphys) == dataphysend ||
-                   OHCI_PAGE(dataphys) + OHCI_PAGE_SIZE == dataphysend) {
-                       /* we can handle it in this TD */
+               curlen = 0;
+               dataphys = DMAADDR(dma, curoffs);
+               dataphysend = DMAADDR(dma, curoffs + len - 1);
+
+               ohci_physaddr_t sphyspg = OHCI_PAGE(dataphys);
+               ohci_physaddr_t ephyspg = OHCI_PAGE(dataphysend);
+               /*
+                * The OHCI hardware can handle at most one page
+                * crossing per TD
+                */
+               if (sphyspg == ephyspg || sphyspg + 1 == ephyspg) {
                        curlen = len;
                } else {
                        /* must use multiple TDs, fill as much as possible. */
@@ -696,8 +724,8 @@
                            (dataphys & (OHCI_PAGE_SIZE - 1));
                        /* the length must be a multiple of the max size */
                        curlen -= curlen % mps;
-                       KASSERT(curlen != 0);
                }
+               KASSERT(curlen != 0);
                DPRINTFN(4, "dataphys=0x%08x dataphysend=0x%08x "
                    "len=%d curlen=%d", dataphys, dataphysend, len, curlen);
                len -= curlen;
@@ -716,12 +744,15 @@
                    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
                DPRINTFN(10, "cbp=0x%08x be=0x%08x", dataphys,
                    dataphys + curlen - 1, 0, 0);
-               if (len == 0)
-                       break;
-               KASSERT(next != NULL);
-               DPRINTFN(10, "extend chain", 0, 0, 0, 0);
-               dataphys += curlen;
-               cur = next;
+
+               curoffs += curlen;
+               len -= curlen;
+
+               if (len != 0) {
+                       KASSERT(next != NULL);
+                       DPRINTFN(10, "extend chain", 0, 0, 0, 0);
+                       cur = next;
+               }
        }
        cur->td.td_flags |=
            (xfer->ux_flags & USBD_SHORT_XFER_OK ? OHCI_TD_R : 0);
@@ -736,7 +767,7 @@
 
                cur->td.td_flags = tdflags;
                cur->td.td_cbp = 0; /* indicate 0 length packet */
-               cur->td.td_nexttd = HTOO32(next->physaddr);
+               cur->td.td_nexttd = 0;
                cur->td.td_be = ~0;
                cur->nexttd = NULL;
                cur->len = 0;



Home | Main Index | Thread Index | Old Index