Module Name:    src
Committed By:   skrll
Date:           Sun May 24 07:42:51 UTC 2020

Modified Files:
        src/sys/dev/usb: ohci.c

Log Message:
Be more careful with OHCI_PAGE limitations in ohci_reset_std_chain and
ohci_device_isoc_enter

Enable USBMALLOC_MULTISEG


To generate a diff of this commit:
cvs rdiff -u -r1.307 -r1.308 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.307 src/sys/dev/usb/ohci.c:1.308
--- src/sys/dev/usb/ohci.c:1.307	Tue May 19 19:09:43 2020
+++ src/sys/dev/usb/ohci.c	Sun May 24 07:42:51 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: ohci.c,v 1.307 2020/05/19 19:09:43 jakllsch Exp $	*/
+/*	$NetBSD: ohci.c,v 1.308 2020/05/24 07:42:51 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.307 2020/05/19 19:09:43 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.308 2020/05/24 07:42:51 skrll Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -618,26 +618,28 @@ ohci_reset_std_chain(ohci_softc_t *sc, s
 			next = ox->ox_stds[j++];
 		KASSERT(next != cur);
 
-		curlen = 0;
-		const ohci_physaddr_t sdataphys = DMAADDR(dma, curoffs);
-		ohci_physaddr_t edataphys = DMAADDR(dma, curoffs + len - 1);
-
-		const ohci_physaddr_t sphyspg = OHCI_PAGE(sdataphys);
-		ohci_physaddr_t ephyspg = OHCI_PAGE(edataphys);
+		curlen = len;
 		/*
-		 * The OHCI hardware can handle at most one page
-		 * crossing per TD
+		 * The OHCI hardware can handle at most one page crossing per
+		 * TD.  That is, 2 * OHCI_PAGE_SIZE as a maximum.  Limit the
+		 * length in this TD accordingly.
 		 */
-		curlen = len;
-		if (sphyspg != ephyspg &&
-		    sphyspg + OHCI_PAGE_SIZE != ephyspg) {
-			/* must use multiple TDs, fill as much as possible. */
-			curlen = 2 * OHCI_PAGE_SIZE -
-			    OHCI_PAGE_OFFSET(sdataphys);
-			/* the length must be a multiple of the max size */
+		const ohci_physaddr_t sdataphys = DMAADDR(dma, curoffs);
+
+		int maxlen = (2 * OHCI_PAGE_SIZE) - OHCI_PAGE_OFFSET(sdataphys);
+		if (curlen > maxlen) {
+			curlen = maxlen;
+
+			/*
+			 * the length must be a multiple of
+			 * the max size
+			 */
 			curlen -= curlen % mps;
-			edataphys = DMAADDR(dma, curoffs + curlen - 1);
 		}
+
+		const int edataoffs = curoffs + curlen - 1;
+		const ohci_physaddr_t edataphys = DMAADDR(dma, edataoffs);
+
 		KASSERT(curlen != 0);
 		DPRINTFN(4, "sdataphys=0x%08jx edataphys=0x%08jx "
 		    "len=%jd curlen=%jd", sdataphys, edataphys, len, curlen);
@@ -811,6 +813,7 @@ ohci_init(ohci_softc_t *sc)
 	}
 	sc->sc_bus.ub_revision = USBREV_1_0;
 	sc->sc_bus.ub_usedma = true;
+	sc->sc_bus.ub_dmaflags = USBMALLOC_MULTISEG;
 
 	/* XXX determine alignment by R/W */
 	/* Allocate the HCCA area. */
@@ -3447,8 +3450,9 @@ ohci_device_isoc_enter(struct usbd_xfer 
 	ohci_softc_t *sc = OHCI_XFER2SC(xfer);
 	ohci_soft_ed_t *sed = opipe->sed;
 	ohci_soft_itd_t *sitd, *nsitd, *tail;
-	ohci_physaddr_t buf, offs, noffs, bp0;
+	ohci_physaddr_t buf, offs, bp0, bp1;
 	int i, ncur, nframes;
+	size_t boff, frlen;
 
 	OHCIHIST_FUNC(); OHCIHIST_CALLED();
 	DPRINTFN(5, "xfer=%#jx", (uintptr_t)xfer, 0, 0, 0);
@@ -3486,17 +3490,44 @@ ohci_device_isoc_enter(struct usbd_xfer 
 	opipe->tail.itd = ox->ox_sitds[0];
 	ox->ox_sitds[0] = sitd;
 
+	boff = 0;
 	buf = DMAADDR(&xfer->ux_dmabuf, 0);
-	bp0 = OHCI_PAGE(buf);
+	bp0 = bp1 = OHCI_PAGE(buf);
 	offs = OHCI_PAGE_OFFSET(buf);
+
+	ohci_physaddr_t end = bp0;	/* XXX stupid GCC */
+
 	nframes = xfer->ux_nframes;
 	xfer->ux_hcpriv = sitd;
 	size_t j = 1;
 	for (i = ncur = 0; i < nframes; i++, ncur++) {
-		noffs = offs + xfer->ux_frlengths[i];
-		if (ncur == OHCI_ITD_NOFFSET ||	/* all offsets used */
-		    OHCI_PAGE(buf + noffs) > bp0 + OHCI_PAGE_SIZE) { /* too many page crossings */
+		frlen = xfer->ux_frlengths[i];
+
+		DPRINTFN(1, "frame=%jd ux_frlengths[%jd]=%jd", i, i,
+		    xfer->ux_frlengths[i], 0);
+		/*
+		 * XXXNH: The loop assumes this is never true, because
+		 * incrementing 'i' assumes all the ux_frlengths[i] is covered.
+		 */
+		if (frlen > 2 * OHCI_PAGE_SIZE - offs)
+			frlen = 2 * OHCI_PAGE_SIZE - offs;
+
+		boff += frlen;
+		buf = DMAADDR(&xfer->ux_dmabuf, boff);
+		ohci_physaddr_t noffs = OHCI_PAGE_OFFSET(buf);
+
+		ohci_physaddr_t nend = DMAADDR(&xfer->ux_dmabuf, boff - 1);
+		const ohci_physaddr_t nep = OHCI_PAGE(nend);
+
+		/* Note the first page crossing in bp1 */
+		if (bp0 == bp1 && bp1 != nep)
+			bp1 = nep;
+
+		DPRINTFN(1, "ncur=%jd bp0=%#jx bp1=%#jx nend=%#jx",
+		    ncur, bp0, bp1, nend);
 
+		/* all offsets used or too many page crossings */
+		if (ncur == OHCI_ITD_NOFFSET || (bp0 != bp1 && bp1 != nep)) {
 			/* Allocate next ITD */
 			nsitd = ox->ox_sitds[j++];
 			KASSERT(nsitd != NULL);
@@ -3510,7 +3541,7 @@ ohci_device_isoc_enter(struct usbd_xfer 
 				OHCI_ITD_SET_FC(ncur));
 			sitd->itd.itd_bp0 = HTOO32(bp0);
 			sitd->itd.itd_nextitd = HTOO32(nsitd->physaddr);
-			sitd->itd.itd_be = HTOO32(bp0 + offs - 1);
+			sitd->itd.itd_be = HTOO32(end);
 			sitd->nextitd = nsitd;
 			sitd->xfer = xfer;
 			sitd->flags = 0;
@@ -3523,10 +3554,11 @@ ohci_device_isoc_enter(struct usbd_xfer 
 
 			sitd = nsitd;
 			isoc->next = isoc->next + ncur;
-			bp0 = OHCI_PAGE(buf + offs);
+			bp0 = bp1 = OHCI_PAGE(buf);
 			ncur = 0;
 		}
 		sitd->itd.itd_offset[ncur] = HTOO16(OHCI_ITD_MK_OFFS(offs));
+		end = nend;
 		offs = noffs;
 	}
 	KASSERT(j <= ox->ox_nsitd);
@@ -3547,7 +3579,7 @@ ohci_device_isoc_enter(struct usbd_xfer 
 		OHCI_ITD_SET_FC(ncur));
 	sitd->itd.itd_bp0 = HTOO32(bp0);
 	sitd->itd.itd_nextitd = HTOO32(tail->physaddr);
-	sitd->itd.itd_be = HTOO32(bp0 + offs - 1);
+	sitd->itd.itd_be = HTOO32(end);
 	sitd->nextitd = tail;
 	sitd->xfer = xfer;
 	sitd->flags = OHCI_CALL_DONE;

Reply via email to