Module Name:    src
Committed By:   skrll
Date:           Fri Apr  1 14:20:06 UTC 2016

Modified Files:
        src/sys/dev/usb [nick-nhusb]: ohci.c

Log Message:
Deal with ZLP (zero length packets) properly


To generate a diff of this commit:
cvs rdiff -u -r1.254.2.67 -r1.254.2.68 src/sys/dev/usb/ohci.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/ohci.c
diff -u src/sys/dev/usb/ohci.c:1.254.2.67 src/sys/dev/usb/ohci.c:1.254.2.68
--- src/sys/dev/usb/ohci.c:1.254.2.67	Fri Apr  1 14:17:24 2016
+++ src/sys/dev/usb/ohci.c	Fri Apr  1 14:20:05 2016
@@ -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 @@ ohci_alloc_std_chain(ohci_softc_t *sc, s
 
 	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 @@ ohci_alloc_std_chain(ohci_softc_t *sc, s
 	    (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;
+		curlen = 0;
+		dataphys = DMAADDR(dma, curoffs);
+		dataphysend = DMAADDR(dma, curoffs + len - 1);
 
-		/* 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 */
+		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 @@ ohci_alloc_std_chain(ohci_softc_t *sc, s
 			    (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 @@ ohci_alloc_std_chain(ohci_softc_t *sc, s
 
 		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 @@ ohci_reset_std_chain(ohci_softc_t *sc, s
 
 	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;
+	cur = next = sp;
 
-	dataphys = DMAADDR(dma, 0);
-	dataphysend = OHCI_PAGE(dataphys + len - 1);
 	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 @@ ohci_reset_std_chain(ohci_softc_t *sc, s
 			    (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 @@ ohci_reset_std_chain(ohci_softc_t *sc, s
 		    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 @@ ohci_reset_std_chain(ohci_softc_t *sc, s
 
 		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;

Reply via email to