NetBSD Problem Report #42979

From yorickhardy@gmail.com  Mon Mar 15 22:15:44 2010
Return-Path: <yorickhardy@gmail.com>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by www.NetBSD.org (Postfix) with ESMTP id 5D7D163B11D
	for <gnats-bugs@gnats.netbsd.org>; Mon, 15 Mar 2010 22:15:44 +0000 (UTC)
Message-Id: <4b9ea239.8109cc0a.327b.2008@mx.google.com>
Date: Mon, 15 Mar 2010 14:10:17 -0700 (PDT)
From: yorickhardy@gmail.com
Reply-To: yhardy@NetBSD.org
To: gnats-bugs@gnats.NetBSD.org
Cc: yhardy@NetBSD.org
Subject: Data toggle issues in usb(4), slhci(4), uhci(4) and ehci(4)
X-Send-Pr-Version: 3.95

>Number:         42979
>Category:       kern
>Synopsis:       Data toggle issues in usb(4), slhci(4), uhci(4) and ehci(4)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    skrll
>State:          analyzed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Mar 15 22:20:00 +0000 2010
>Closed-Date:    
>Last-Modified:  Sun Sep 08 07:04:56 +0000 2013
>Originator:     Yorick Hardy
>Release:        NetBSD 5.99.11
>Organization:

>Environment:
	<The following information is extracted from your kernel. Please>
	<append output of "ldd", "ident" where relevant (multiple lines).>
System: NetBSD TOSHIBA 5.99.11 NetBSD 5.99.11 (TEST.amd64) #22: Mon Mar 15 21:14:46 SAST 2010 root@TOSHIBA:/root/build.amd64.20090512/obj/sys/arch/amd64/compile/TEST.amd64 amd64
Architecture: x86_64
Machine: amd64
>Description:
The USB standard states that data toggles for bulk and interrupt endpoints
are only reset by a configuration event or by clearing an endpoint stall.

usb(4) currently does not reset endpoint data toggles on configuration events.

slhci(4), uhci(4) and ehci(4) do not maintain the data toggle after a bulk or
interrupt pipe is closed, and the data toggle (apparently incorrectly) is
reset to zero on reopening a bulk or interrupt pipe. ohci(4) appears to work
correctly in this regard.

>How-To-Repeat:
>Fix:
The following patch changes the data toggle behaviour in slhci(4),
uhci(4) and ehci(4). It also adds a bus method to reset all data
toggles for bus configuration events. An alternative would be to
ensure that pipes are opened exactly once for any configuration.

I have tested the following patch with a limited number of uhci devices.

The ehci and slhci changes are untested.

Index: sys/dev/usb/ehci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ehci.c,v
retrieving revision 1.166
diff -u -r1.166 ehci.c
--- sys/dev/usb/ehci.c	24 Feb 2010 22:38:09 -0000	1.166
+++ sys/dev/usb/ehci.c	15 Mar 2010 21:00:54 -0000
@@ -91,7 +91,6 @@

 struct ehci_pipe {
 	struct usbd_pipe pipe;
-	int nexttoggle;

 	ehci_soft_qh_t *sqh;
 	union {
@@ -120,6 +119,31 @@
 	} u;
 };

+#define EHCI_NEXTTOGGLE(epipe) \
+	((ehci_softc_t*)((epipe)->pipe.device->bus->hci_private))->nexttoggle
+
+#define EHCI_PIPE_ENDPOINT_ADDR(epipe) \
+	UE_GET_ADDR((epipe)->pipe.endpoint->edesc->bEndpointAddress)
+
+#define EHCI_PIPE_ENDPOINT_DIR(epipe) \
+	UE_GET_DIR((epipe)->pipe.endpoint->edesc->bEndpointAddress)
+
+#define EHCI_NEXTTOGGLE_BIT(epipe) \
+	((EHCI_PIPE_ENDPOINT_DIR(epipe) == UE_DIR_OUT) ? \
+		(0x00000001 << EHCI_PIPE_ENDPOINT_ADDR(epipe)) : \
+		(0x00010000 << EHCI_PIPE_ENDPOINT_ADDR(epipe)))
+
+#define EHCI_NEXTTOGGLE_SET(epipe) \
+	SET(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
+
+#define EHCI_NEXTTOGGLE_ISSET(epipe) \
+	ISSET(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
+
+#define EHCI_NEXTTOGGLE_GET(epipe) ((EHCI_NEXTTOGGLE_ISSET(epipe))?1:0)
+
+#define EHCI_NEXTTOGGLE_CLR(epipe) \
+	CLR(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
+
 Static usbd_status	ehci_open(usbd_pipe_handle);
 Static void		ehci_poll(struct usbd_bus *);
 Static void		ehci_softintr(void *);
@@ -139,6 +163,8 @@
 Static usbd_xfer_handle	ehci_allocx(struct usbd_bus *);
 Static void		ehci_freex(struct usbd_bus *, usbd_xfer_handle);

+Static void		ehci_clear_all_toggle(struct usbd_bus *);
+
 Static usbd_status	ehci_root_ctrl_transfer(usbd_xfer_handle);
 Static usbd_status	ehci_root_ctrl_start(usbd_xfer_handle);
 Static void		ehci_root_ctrl_abort(usbd_xfer_handle);
@@ -252,6 +278,7 @@
 	ehci_freem,
 	ehci_allocx,
 	ehci_freex,
+	ehci_clear_all_toggle,
 };

 Static const struct usbd_pipe_methods ehci_root_ctrl_methods = {
@@ -533,6 +560,8 @@
 		return (USBD_IOERROR);
 	}

+	sc->nexttoggle = 0; /* initialize all data toggles to zero */
+
 	/* Enable interrupts */
 	DPRINTFN(1,("ehci_init: enabling\n"));
 	EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
@@ -953,7 +982,10 @@
 		ehci_dump_sqh(epipe->sqh);
 		ehci_dump_sqtds(ex->sqtdstart);
 #endif
-		epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus);
+		if (EHCI_QTD_GET_TOGGLE(nstatus))
+			EHCI_NEXTTOGGLE_SET(epipe);
+		else
+			EHCI_NEXTTOGGLE_CLR(epipe);
 	}

 	DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n",
@@ -1318,6 +1350,13 @@
 }

 Static void
+ehci_clear_all_toggle(struct usbd_bus *bus)
+{
+	struct ehci_softc *sc = bus->hci_private;
+	sc->nexttoggle = 0;
+}
+
+Static void
 ehci_device_clear_toggle(usbd_pipe_handle pipe)
 {
 	struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
@@ -1328,7 +1367,7 @@
 	if (ehcidebug)
 		usbd_dump_pipe(pipe);
 #endif
-	epipe->nexttoggle = 0;
+	EHCI_NEXTTOGGLE_CLR(epipe);
 }

 Static void
@@ -1549,8 +1588,6 @@
 	if (sc->sc_dying)
 		return (USBD_IOERROR);

-	epipe->nexttoggle = 0;
-
 	if (addr == sc->sc_addr) {
 		switch (ed->bEndpointAddress) {
 		case USB_CONTROL_ENDPOINT:
@@ -2597,7 +2634,7 @@
 	    /* BYTES set below */
 	    ;
 	mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
-	tog = epipe->nexttoggle;
+	tog = EHCI_NEXTTOGGLE_GET(epipe);
 	qtdstatus |= EHCI_QTD_SET_TOGGLE(tog);

 	cur = ehci_alloc_sqtd(sc);
@@ -2702,7 +2739,10 @@
 	usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd),
 	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
 	*ep = cur;
-	epipe->nexttoggle = tog;
+	if (tog)
+		EHCI_NEXTTOGGLE_SET(epipe);
+	else
+		EHCI_NEXTTOGGLE_CLR(epipe);

 	DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
 		     *sp, *ep));
@@ -2846,9 +2886,11 @@
 	ehci_soft_qtd_t *sqtd;
 	ehci_physaddr_t cur;
 	u_int32_t qhstatus;
+	u_int32_t nstatus;
 	int s;
 	int hit;
 	int wake;
+	int fixdt;

 	DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));

@@ -2902,11 +2944,19 @@
 	    sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status),
 	    sizeof(sqh->qh.qh_qtd.qtd_status),
 	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-	for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
+	for (sqtd = exfer->sqtdstart, fixdt = 1; ; sqtd = sqtd->nextqtd) {
 		usb_syncmem(&sqtd->dma,
 		    sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
 		    sizeof(sqtd->qtd.qtd_status),
 		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+		nstatus = le32toh(sqtd->qtd.qtd_status);
+		if (fixdt && (nstatus & EHCI_QTD_ACTIVE)) {
+			if (EHCI_QTD_GET_TOGGLE(nstatus))
+				EHCI_NEXTTOGGLE_SET(epipe);
+			else
+				EHCI_NEXTTOGGLE_CLR(epipe);
+			fixdt = 0;
+		}
 		sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED);
 		usb_syncmem(&sqtd->dma,
 		    sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
@@ -3260,7 +3310,7 @@
 		ehci_soft_qtd_t *end;

 		/* Start toggle at 1. */
-		epipe->nexttoggle = 1;
+		EHCI_NEXTTOGGLE_SET(epipe);
 		err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
 			  &next, &end);
 		if (err)
Index: sys/dev/usb/ehcivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ehcivar.h,v
retrieving revision 1.36
diff -u -r1.36 ehcivar.h
--- sys/dev/usb/ehcivar.h	24 Feb 2010 22:38:09 -0000	1.36
+++ sys/dev/usb/ehcivar.h	15 Mar 2010 21:00:55 -0000
@@ -167,6 +167,8 @@
 	device_t sc_child; /* /dev/usb# device */
 	char sc_dying;
 	struct usb_dma_reserve sc_dma_reserve;
+
+	u_int32_t nexttoggle;		/* data toggle for each endpoint */
 } ehci_softc_t;

 #define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
Index: sys/dev/usb/ohci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ohci.c,v
retrieving revision 1.206
diff -u -r1.206 ohci.c
--- sys/dev/usb/ohci.c	24 Feb 2010 22:38:09 -0000	1.206
+++ sys/dev/usb/ohci.c	15 Mar 2010 21:01:00 -0000
@@ -134,6 +134,8 @@
 Static usbd_xfer_handle	ohci_allocx(struct usbd_bus *);
 Static void		ohci_freex(struct usbd_bus *, usbd_xfer_handle);

+Static void		ohci_clear_all_toggle(struct usbd_bus *);
+
 Static usbd_status	ohci_root_ctrl_transfer(usbd_xfer_handle);
 Static usbd_status	ohci_root_ctrl_start(usbd_xfer_handle);
 Static void		ohci_root_ctrl_abort(usbd_xfer_handle);
@@ -273,6 +275,7 @@
 	ohci_freem,
 	ohci_allocx,
 	ohci_freex,
+	ohci_clear_all_toggle,
 };

 Static const struct usbd_pipe_methods ohci_root_ctrl_methods = {
@@ -983,6 +986,17 @@
 	SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
 }

+Static void
+ohci_clear_all_toggle(struct usbd_bus *bus)
+{
+	struct ohci_softc *sc = bus->hci_private;
+	struct ohci_soft_ed *ed = sc->sc_bulk_head;
+	while (ed != NULL) {
+		ed->ed.ed_headp &= HTOO32(~OHCI_TOGGLECARRY);
+		ed = ed->next;
+	}
+}
+
 /*
  * Shut down the controller when the system is going down.
  */
Index: sys/dev/usb/uhci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/uhci.c,v
retrieving revision 1.232
diff -u -r1.232 uhci.c
--- sys/dev/usb/uhci.c	24 Feb 2010 22:38:09 -0000	1.232
+++ sys/dev/usb/uhci.c	15 Mar 2010 21:01:14 -0000
@@ -91,7 +91,6 @@

 struct uhci_pipe {
 	struct usbd_pipe pipe;
-	int nexttoggle;

 	u_char aborting;
 	usbd_xfer_handle abortstart, abortend;
@@ -125,6 +124,31 @@
 	} u;
 };

+#define UHCI_NEXTTOGGLE(upipe) \
+	((uhci_softc_t*)((upipe)->pipe.device->bus->hci_private))->nexttoggle
+
+#define UHCI_PIPE_ENDPOINT_ADDR(upipe) \
+	UE_GET_ADDR((upipe)->pipe.endpoint->edesc->bEndpointAddress)
+
+#define UHCI_PIPE_ENDPOINT_DIR(upipe) \
+	UE_GET_DIR((upipe)->pipe.endpoint->edesc->bEndpointAddress)
+
+#define UHCI_NEXTTOGGLE_BIT(upipe) \
+	((UHCI_PIPE_ENDPOINT_DIR(upipe) == UE_DIR_OUT) ? \
+		(0x00000001 << UHCI_PIPE_ENDPOINT_ADDR(upipe)) : \
+		(0x00010000 << UHCI_PIPE_ENDPOINT_ADDR(upipe)))
+
+#define UHCI_NEXTTOGGLE_SET(upipe) \
+	SET(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
+
+#define UHCI_NEXTTOGGLE_ISSET(upipe) \
+	ISSET(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
+
+#define UHCI_NEXTTOGGLE_GET(upipe) ((UHCI_NEXTTOGGLE_ISSET(upipe))?1:0)
+
+#define UHCI_NEXTTOGGLE_CLR(upipe) \
+	CLR(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
+
 Static void		uhci_globalreset(uhci_softc_t *);
 Static usbd_status	uhci_portreset(uhci_softc_t*, int);
 Static void		uhci_reset(uhci_softc_t *);
@@ -171,6 +195,8 @@
 Static usbd_xfer_handle	uhci_allocx(struct usbd_bus *);
 Static void		uhci_freex(struct usbd_bus *, usbd_xfer_handle);

+Static void		uhci_clear_all_toggle(struct usbd_bus *);
+
 Static usbd_status	uhci_device_ctrl_transfer(usbd_xfer_handle);
 Static usbd_status	uhci_device_ctrl_start(usbd_xfer_handle);
 Static void		uhci_device_ctrl_abort(usbd_xfer_handle);
@@ -287,6 +313,7 @@
 	uhci_freem,
 	uhci_allocx,
 	uhci_freex,
+	uhci_clear_all_toggle,
 };

 const struct usbd_pipe_methods uhci_root_ctrl_methods = {
@@ -529,6 +556,8 @@

 	UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */

+	sc->nexttoggle = 0; /* initialize all data toggles to zero */
+
 	DPRINTFN(1,("uhci_init: enabling\n"));

 	err =  uhci_run(sc, 1);		/* and here we go... */
@@ -683,6 +712,13 @@
 	SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
 }

+Static void
+uhci_clear_all_toggle(struct usbd_bus *bus)
+{
+	struct uhci_softc *sc = bus->hci_private;
+	sc->nexttoggle = 0;
+}
+
 /*
  * Handle suspend/resume.
  *
@@ -1564,8 +1600,12 @@
 		}
 	}
 	/* If there are left over TDs we need to update the toggle. */
-	if (std != NULL)
-		upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token));
+	if (std != NULL) {
+		if (UHCI_TD_GET_DT(le32toh(std->td.td_token)))
+			UHCI_NEXTTOGGLE_SET(upipe);
+		else
+			UHCI_NEXTTOGGLE_CLR(upipe);
+	}

 	status &= UHCI_TD_ERROR;
 	DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n",
@@ -1884,10 +1924,13 @@
 		DPRINTFN(-1,("uhci_alloc_std_chain: ntd=0\n"));
 		return (USBD_NORMAL_COMPLETION);
 	}
-	tog = upipe->nexttoggle;
+	tog = UHCI_NEXTTOGGLE_GET(upipe);
 	if (ntd % 2 == 0)
 		tog ^= 1;
-	upipe->nexttoggle = tog ^ 1;
+	if (tog ^ 1)
+		UHCI_NEXTTOGGLE_SET(upipe);
+	else
+		UHCI_NEXTTOGGLE_CLR(upipe);
 	lastp = NULL;
 	lastlink = UHCI_PTR_T;
 	ntd--;
@@ -1928,7 +1971,7 @@
 	}
 	*sp = lastp;
 	DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n",
-		      upipe->nexttoggle));
+		      UHCI_NEXTTOGGLE_GET(upipe)));
 	return (USBD_NORMAL_COMPLETION);
 }

@@ -1936,7 +1979,7 @@
 uhci_device_clear_toggle(usbd_pipe_handle pipe)
 {
 	struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
-	upipe->nexttoggle = 0;
+	UHCI_NEXTTOGGLE_CLR(upipe);
 }

 void
@@ -2077,6 +2120,7 @@
 	uhci_soft_td_t *std;
 	int s;
 	int wake;
+	int fixdt;

 	DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));

@@ -2120,11 +2164,18 @@
 	xfer->status = status;	/* make software ignore it */
 	usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
 	DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
-	for (std = ii->stdstart; std != NULL; std = std->link.std) {
+	for (std = ii->stdstart, fixdt = 1; std != NULL; std = std->link.std) {
 		usb_syncmem(&std->dma,
 		    std->offs + offsetof(uhci_td_t, td_status),
 		    sizeof(std->td.td_status),
 		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+		if (fixdt && (le32toh(std->td.td_status) & UHCI_TD_ACTIVE)) {
+			if (UHCI_TD_GET_DT(le32toh(std->td.td_token)))
+				UHCI_NEXTTOGGLE_SET(upipe);
+			else
+				UHCI_NEXTTOGGLE_CLR(upipe);
+			fixdt = 0;
+		}
 		std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
 		usb_syncmem(&std->dma,
 		    std->offs + offsetof(uhci_td_t, td_status),
@@ -2409,7 +2460,7 @@

 	/* Set up data transaction */
 	if (len != 0) {
-		upipe->nexttoggle = 1;
+		UHCI_NEXTTOGGLE_SET(upipe);
 		err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags,
 					   &xfer->dmabuf, &data, &dataend);
 		if (err)
@@ -3183,7 +3234,6 @@
 		     ed->bEndpointAddress, sc->sc_addr));

 	upipe->aborting = 0;
-	upipe->nexttoggle = 0;

 	if (pipe->device->address == sc->sc_addr) {
 		switch (ed->bEndpointAddress) {
Index: sys/dev/usb/uhcivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/uhcivar.h,v
retrieving revision 1.47
diff -u -r1.47 uhcivar.h
--- sys/dev/usb/uhcivar.h	24 Feb 2010 22:38:09 -0000	1.47
+++ sys/dev/usb/uhcivar.h	15 Mar 2010 21:01:15 -0000
@@ -181,6 +181,7 @@
 #ifdef __NetBSD__
 	struct usb_dma_reserve sc_dma_reserve;
 #endif
+	u_int32_t nexttoggle;		/* data toggle for each endpoint */
 } uhci_softc_t;

 usbd_status	uhci_init(uhci_softc_t *);
Index: sys/dev/usb/usb_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v
retrieving revision 1.167
diff -u -r1.167 usb_subr.c
--- sys/dev/usb/usb_subr.c	12 Nov 2009 20:11:35 -0000	1.167
+++ sys/dev/usb/usb_subr.c	15 Mar 2010 21:01:30 -0000
@@ -698,6 +698,9 @@
 		}
 	}

+	/* this is a configuration event, so reset all data toggles */
+	dev->bus->methods->clear_all_toggle(dev->bus);
+
 	return (USBD_NORMAL_COMPLETION);

  bad:
Index: sys/dev/usb/usbdi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.127
diff -u -r1.127 usbdi.c
--- sys/dev/usb/usbdi.c	16 Jan 2010 17:03:03 -0000	1.127
+++ sys/dev/usb/usbdi.c	15 Mar 2010 21:01:43 -0000
@@ -669,7 +669,13 @@
 	USETW(req.wValue, iface->idesc->bAlternateSetting);
 	USETW(req.wIndex, iface->idesc->bInterfaceNumber);
 	USETW(req.wLength, 0);
-	return (usbd_do_request(iface->device, &req, 0));
+	err = usbd_do_request(iface->device, &req, 0);
+
+	/* this is a configuration event, so reset all data toggles */
+	if (!err)
+		iface->device->bus->methods->clear_all_toggle(iface->device->bus);
+
+	return err;
 }

 int
Index: sys/dev/usb/usbdivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usbdivar.h,v
retrieving revision 1.91
diff -u -r1.91 usbdivar.h
--- sys/dev/usb/usbdivar.h	12 Nov 2009 20:11:35 -0000	1.91
+++ sys/dev/usb/usbdivar.h	15 Mar 2010 21:01:44 -0000
@@ -57,6 +57,7 @@
 	void		      (*freem)(struct usbd_bus *, usb_dma_t *);
 	struct usbd_xfer *    (*allocx)(struct usbd_bus *);
 	void		      (*freex)(struct usbd_bus *, struct usbd_xfer *);
+	void		      (*clear_all_toggle)(struct usbd_bus *);
 };

 struct usbd_pipe_methods {
Index: sys/dev/ic/sl811hs.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/sl811hs.c,v
retrieving revision 1.25
diff -u -r1.25 sl811hs.c
--- sys/dev/ic/sl811hs.c	25 Nov 2009 14:28:50 -0000	1.25
+++ sys/dev/ic/sl811hs.c	15 Mar 2010 21:01:56 -0000
@@ -280,7 +280,7 @@
 	struct gcq 	ap;		/* All pipes */
 	struct gcq 	to;		/* Timeout list */
 	struct gcq 	xq;		/* Xfer queues */
-	unsigned int	pflags;		/* Pipe flags */
+	unsigned int	*pflags;	/* Pipe flags */
 #define PF_GONE		(0x01)		/* Pipe is on disabled device */
 #define PF_TOGGLE 	(0x02)		/* Data toggle status */
 #define PF_LS		(0x04)		/* Pipe is low speed */
@@ -433,6 +433,7 @@
 void slhci_freem(struct usbd_bus *, usb_dma_t *);
 struct usbd_xfer * slhci_allocx(struct usbd_bus *);
 void slhci_freex(struct usbd_bus *, struct usbd_xfer *);
+void slhci_clear_all_toggle(struct usbd_bus *);

 usbd_status slhci_transfer(struct usbd_xfer *);
 usbd_status slhci_start(struct usbd_xfer *);
@@ -685,6 +686,7 @@
 	slhci_freem,
 	slhci_allocx,
 	slhci_freex,
+	slhci_clear_all_toggle,
 };

 const struct usbd_pipe_methods slhci_pipe_methods = {
@@ -849,6 +851,15 @@
 	free(xfer, M_USB);
 }

+void slhci_clear_all_toggle(struct usbd_bus *bus)
+{
+	struct slhci_softc *sc = bus->hci_private;
+	int i;
+
+	for (i = 0; i < 32; i++)
+		sc->pflags[i] &= ~PF_TOGGLE;
+}
+
 usbd_status
 slhci_transfer(struct usbd_xfer *xfer)
 {
@@ -923,7 +934,7 @@
 	spipe->newlen[1] = min(xfer->length, max_packet);

 	if (spipe->ptype == PT_BULK || spipe->ptype == PT_INTR) {
-		if (spipe->pflags & PF_TOGGLE)
+		if (*(spipe->pflags) & PF_TOGGLE)
 			spipe->control |= SL11_EPCTRL_DATATOGGLE;
 		spipe->tregs[LEN] = spipe->newlen[1];
 		if (spipe->tregs[LEN]) 
@@ -971,10 +982,10 @@
 	 * same place shares constants. Index 0 is "short length" for bulk and 
 	 * ctrl data and 1 is "full length" for ctrl data (bulk/intr are 
 	 * already set to full length). */
-	if (spipe->pflags & PF_LS) {
+	if (*(spipe->pflags) & PF_LS) {
 		/* Setting PREAMBLE for directly connnected LS devices will
 		 * lock up the chip. */
-		if (spipe->pflags & PF_PREAMBLE)
+		if (*(spipe->pflags) & PF_PREAMBLE)
 			spipe->control |= SL11_EPCTRL_PREAMBLE;
 		if (max_packet <= 8) {
 			spipe->bustime = SLHCI_LS_CONST + 
@@ -1031,6 +1042,7 @@
 	usb_endpoint_descriptor_t *ed;
 	struct slhci_transfers *t;
 	unsigned int max_packet, pmaxpkt;
+	int epaddr;

 	dev = pipe->device;
 	sc = dev->bus->hci_private;
@@ -1041,7 +1053,13 @@
 	DLOG(D_TRACE, "slhci_open(addr=%d,ep=%d,rootaddr=%d)",
 		dev->address, ed->bEndpointAddress, t->rootaddr, 0);

-	spipe->pflags = 0;
+	epaddr = UE_GET_ADDR(ed->bEndpointAddress);
+	if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
+		spipe->pflags = &(sc->pflags[epaddr]);
+	else
+		spipe->pflags = &(sc->pflags[epaddr + 16]);
+
+	*(spipe->pflags) &= PF_TOGGLE; /* clear all flags except toggle */
 	spipe->frame = 0;
 	spipe->lastframe = 0;
 	spipe->xfer = NULL;
@@ -1058,9 +1076,9 @@
 	max_packet = UGETW(ed->wMaxPacketSize);

 	if (dev->speed == USB_SPEED_LOW) {
-		spipe->pflags |= PF_LS;
+		*(spipe->pflags) |= PF_LS;
 		if (dev->myhub->address != t->rootaddr) {
-			spipe->pflags |= PF_PREAMBLE;
+			*(spipe->pflags) |= PF_PREAMBLE;
 			if (!slhci_try_lsvh)
 				return slhci_lock_call(sc, &slhci_lsvh_warn, 
 				    spipe, NULL);
@@ -1291,7 +1309,7 @@
 	DLOG(D_TRACE, "%s toggle spipe %p", pnames(spipe->ptype), 
 	    spipe,0,0);

-	spipe->pflags &= ~PF_TOGGLE;
+	*(spipe->pflags) &= ~PF_TOGGLE;

 #ifdef DIAGNOSTIC
 	if (spipe->xfer != NULL) {
@@ -2085,7 +2103,7 @@
 status_setup:
 			/* CTRL_DATA swaps direction in PID then jumps here */
 			spipe->tregs[LEN] = 0;
-			if (spipe->pflags & PF_LS)
+			if (*(spipe->pflags) & PF_LS)
 				spipe->bustime = SLHCI_LS_CONST;
 			else
 				spipe->bustime = SLHCI_FS_CONST;
@@ -2140,9 +2158,9 @@
 			 * current setting will apply to the next 
 			 * transfer. */ 
 			if (spipe->control & SL11_EPCTRL_DATATOGGLE)
-				spipe->pflags |= PF_TOGGLE;
+				*(spipe->pflags) |= PF_TOGGLE;
 			else
-				spipe->pflags &= ~PF_TOGGLE;
+				*(spipe->pflags) &= ~PF_TOGGLE;

 			head = Q_CALLBACKS;
 		}
@@ -2373,7 +2391,7 @@
 	SLHCI_MAINLOCKASSERT(sc);

 	if (__predict_false(t->flags & F_DISABLED) || 
-	    __predict_false(spipe->pflags & PF_GONE)) {
+	    __predict_false(*(spipe->pflags) & PF_GONE)) {
 		DLOG(D_MSG, "slhci_enter_xfer: DISABLED or GONE", 0,0,0,0);
 		spipe->xfer->status = USBD_CANCELLED; 
 	}
@@ -2799,7 +2817,7 @@
 	/* Cancel all pipes.  Note that not all of these may be on the 
 	 * callback queue yet; some could be in slhci_start, for example. */
 	FOREACH_AP(q, t, spipe) {
-		spipe->pflags = PF_GONE;
+		*(spipe->pflags) = PF_GONE;
 		spipe->pipe.repeat = 0;
 		spipe->pipe.aborting = 1;
 		if (spipe->xfer != NULL)
@@ -2928,7 +2946,7 @@
 	t = &sc->sc_transfers;
 	max_packet = UGETW(spipe->pipe.endpoint->edesc->wMaxPacketSize);

-	if (spipe->pflags & PF_LS)
+	if (*(spipe->pflags) & PF_LS)
 		bustime = SLHCI_LS_CONST + SLHCI_LS_DATA_TIME(max_packet);
 	else
 		bustime = SLHCI_FS_CONST + SLHCI_FS_DATA_TIME(max_packet);
@@ -3612,7 +3630,7 @@
 	    "AP" : "", gcq_onlist(&spipe->to) ? "TO" : "", 
 	    gcq_onlist(&spipe->xq) ? "XQ" : "");
 	DDOLOG("spipe: xfer %p buffer %p pflags %#x ptype %s",
-	    spipe->xfer, spipe->buffer, spipe->pflags, pnames(spipe->ptype));
+	    spipe->xfer, spipe->buffer, *(spipe->pflags), pnames(spipe->ptype));
 }

 void
Index: sys/dev/ic/sl811hsvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/sl811hsvar.h,v
retrieving revision 1.6
diff -u -r1.6 sl811hsvar.h
--- sys/dev/ic/sl811hsvar.h	12 May 2009 14:25:18 -0000	1.6
+++ sys/dev/ic/sl811hsvar.h	15 Mar 2010 21:01:57 -0000
@@ -83,6 +83,8 @@

 	uint8_t			sc_ier; 	/* enabled interrupts */
 	uint8_t			sc_stride;	/* port stride */
+
+	unsigned int		pflags[32];		/* Pipe flags for each endpoint */
 };

 /* last preinit arguments are: max current (in mA, not mA/2), port stride */

>Release-Note:

>Audit-Trail:
From: Yorick Hardy <yorickhardy@gmail.com>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: kern/42979: Data toggle issues in usb(4), slhci(4), uhci(4) and ehci(4)
Date: Tue, 16 Mar 2010 07:35:44 +0200

 I just tested with uvideo(4) on ehci(4), and it seems to work fine.

 -- 
 Kind regards,

 Yorick Hardy

Responsible-Changed-From-To: kern-bug-people->martin
Responsible-Changed-By: martin@NetBSD.org
Responsible-Changed-When: Tue, 16 Mar 2010 08:19:46 +0000
Responsible-Changed-Why:
I'll handle it


State-Changed-From-To: open->analyzed
State-Changed-By: martin@NetBSD.org
State-Changed-When: Tue, 16 Mar 2010 08:19:46 +0000
State-Changed-Why:
Fix looks good


From: Yorick Hardy <yorickhardy@gmail.com>
To: gnats-bugs@NetBSD.org
Cc: martin@NetBSD.org, kern-bug-people@netbsd.org, netbsd-bugs@netbsd.org,
	gnats-admin@netbsd.org
Subject: Re: kern/42979 (Data toggle issues in usb(4), slhci(4), uhci(4) and ehci(4))
Date: Wed, 17 Mar 2010 01:37:30 +0200

 The slhci patch should use USB_MAX_ENDPOINTS instead of 16.
 The patch below should replace the slhci part originally submitted.
 I could rewrite it more in the style of the UHCI changes, if that
 is preferred.

 This patch is still untested (I don't have the hardware).

 -- 
 Kind regards,

 Yorick Hardy


 Index: sys/dev/ic/sl811hs.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/sl811hs.c,v
 retrieving revision 1.25
 diff -u -r1.25 sl811hs.c
 --- sys/dev/ic/sl811hs.c	25 Nov 2009 14:28:50 -0000	1.25
 +++ sys/dev/ic/sl811hs.c	16 Mar 2010 23:20:12 -0000
 @@ -280,7 +280,7 @@
  	struct gcq 	ap;		/* All pipes */
  	struct gcq 	to;		/* Timeout list */
  	struct gcq 	xq;		/* Xfer queues */
 -	unsigned int	pflags;		/* Pipe flags */
 +	unsigned int	*pflags;	/* Pipe flags */
  #define PF_GONE		(0x01)		/* Pipe is on disabled device */
  #define PF_TOGGLE 	(0x02)		/* Data toggle status */
  #define PF_LS		(0x04)		/* Pipe is low speed */
 @@ -433,6 +433,7 @@
  void slhci_freem(struct usbd_bus *, usb_dma_t *);
  struct usbd_xfer * slhci_allocx(struct usbd_bus *);
  void slhci_freex(struct usbd_bus *, struct usbd_xfer *);
 +void slhci_clear_all_toggle(struct usbd_bus *);

  usbd_status slhci_transfer(struct usbd_xfer *);
  usbd_status slhci_start(struct usbd_xfer *);
 @@ -685,6 +686,7 @@
  	slhci_freem,
  	slhci_allocx,
  	slhci_freex,
 +	slhci_clear_all_toggle,
  };

  const struct usbd_pipe_methods slhci_pipe_methods = {
 @@ -849,6 +851,15 @@
  	free(xfer, M_USB);
  }

 +void slhci_clear_all_toggle(struct usbd_bus *bus)
 +{
 +	struct slhci_softc *sc = bus->hci_private;
 +	int i;
 +
 +	for (i = 0; i < 32; i++)
 +		sc->pflags[i] &= ~PF_TOGGLE;
 +}
 +
  usbd_status
  slhci_transfer(struct usbd_xfer *xfer)
  {
 @@ -923,7 +934,7 @@
  	spipe->newlen[1] = min(xfer->length, max_packet);

  	if (spipe->ptype == PT_BULK || spipe->ptype == PT_INTR) {
 -		if (spipe->pflags & PF_TOGGLE)
 +		if (*(spipe->pflags) & PF_TOGGLE)
  			spipe->control |= SL11_EPCTRL_DATATOGGLE;
  		spipe->tregs[LEN] = spipe->newlen[1];
  		if (spipe->tregs[LEN]) 
 @@ -971,10 +982,10 @@
  	 * same place shares constants. Index 0 is "short length" for bulk and 
  	 * ctrl data and 1 is "full length" for ctrl data (bulk/intr are 
  	 * already set to full length). */
 -	if (spipe->pflags & PF_LS) {
 +	if (*(spipe->pflags) & PF_LS) {
  		/* Setting PREAMBLE for directly connnected LS devices will
  		 * lock up the chip. */
 -		if (spipe->pflags & PF_PREAMBLE)
 +		if (*(spipe->pflags) & PF_PREAMBLE)
  			spipe->control |= SL11_EPCTRL_PREAMBLE;
  		if (max_packet <= 8) {
  			spipe->bustime = SLHCI_LS_CONST + 
 @@ -1031,6 +1042,7 @@
  	usb_endpoint_descriptor_t *ed;
  	struct slhci_transfers *t;
  	unsigned int max_packet, pmaxpkt;
 +	int epaddr;

  	dev = pipe->device;
  	sc = dev->bus->hci_private;
 @@ -1041,7 +1053,13 @@
  	DLOG(D_TRACE, "slhci_open(addr=%d,ep=%d,rootaddr=%d)",
  		dev->address, ed->bEndpointAddress, t->rootaddr, 0);

 -	spipe->pflags = 0;
 +	epaddr = UE_GET_ADDR(ed->bEndpointAddress);
 +	if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
 +		spipe->pflags = &(sc->pflags[epaddr]);
 +	else
 +		spipe->pflags = &(sc->pflags[epaddr + USB_MAX_ENDPOINTS]);
 +
 +	*(spipe->pflags) &= PF_TOGGLE; /* clear all flags except toggle */
  	spipe->frame = 0;
  	spipe->lastframe = 0;
  	spipe->xfer = NULL;
 @@ -1058,9 +1076,9 @@
  	max_packet = UGETW(ed->wMaxPacketSize);

  	if (dev->speed == USB_SPEED_LOW) {
 -		spipe->pflags |= PF_LS;
 +		*(spipe->pflags) |= PF_LS;
  		if (dev->myhub->address != t->rootaddr) {
 -			spipe->pflags |= PF_PREAMBLE;
 +			*(spipe->pflags) |= PF_PREAMBLE;
  			if (!slhci_try_lsvh)
  				return slhci_lock_call(sc, &slhci_lsvh_warn, 
  				    spipe, NULL);
 @@ -1291,7 +1309,7 @@
  	DLOG(D_TRACE, "%s toggle spipe %p", pnames(spipe->ptype), 
  	    spipe,0,0);

 -	spipe->pflags &= ~PF_TOGGLE;
 +	*(spipe->pflags) &= ~PF_TOGGLE;

  #ifdef DIAGNOSTIC
  	if (spipe->xfer != NULL) {
 @@ -2085,7 +2103,7 @@
  status_setup:
  			/* CTRL_DATA swaps direction in PID then jumps here */
  			spipe->tregs[LEN] = 0;
 -			if (spipe->pflags & PF_LS)
 +			if (*(spipe->pflags) & PF_LS)
  				spipe->bustime = SLHCI_LS_CONST;
  			else
  				spipe->bustime = SLHCI_FS_CONST;
 @@ -2140,9 +2158,9 @@
  			 * current setting will apply to the next 
  			 * transfer. */ 
  			if (spipe->control & SL11_EPCTRL_DATATOGGLE)
 -				spipe->pflags |= PF_TOGGLE;
 +				*(spipe->pflags) |= PF_TOGGLE;
  			else
 -				spipe->pflags &= ~PF_TOGGLE;
 +				*(spipe->pflags) &= ~PF_TOGGLE;

  			head = Q_CALLBACKS;
  		}
 @@ -2373,7 +2391,7 @@
  	SLHCI_MAINLOCKASSERT(sc);

  	if (__predict_false(t->flags & F_DISABLED) || 
 -	    __predict_false(spipe->pflags & PF_GONE)) {
 +	    __predict_false(*(spipe->pflags) & PF_GONE)) {
  		DLOG(D_MSG, "slhci_enter_xfer: DISABLED or GONE", 0,0,0,0);
  		spipe->xfer->status = USBD_CANCELLED; 
  	}
 @@ -2799,7 +2817,7 @@
  	/* Cancel all pipes.  Note that not all of these may be on the 
  	 * callback queue yet; some could be in slhci_start, for example. */
  	FOREACH_AP(q, t, spipe) {
 -		spipe->pflags = PF_GONE;
 +		*(spipe->pflags) = PF_GONE;
  		spipe->pipe.repeat = 0;
  		spipe->pipe.aborting = 1;
  		if (spipe->xfer != NULL)
 @@ -2928,7 +2946,7 @@
  	t = &sc->sc_transfers;
  	max_packet = UGETW(spipe->pipe.endpoint->edesc->wMaxPacketSize);

 -	if (spipe->pflags & PF_LS)
 +	if (*(spipe->pflags) & PF_LS)
  		bustime = SLHCI_LS_CONST + SLHCI_LS_DATA_TIME(max_packet);
  	else
  		bustime = SLHCI_FS_CONST + SLHCI_FS_DATA_TIME(max_packet);
 @@ -3612,7 +3630,7 @@
  	    "AP" : "", gcq_onlist(&spipe->to) ? "TO" : "", 
  	    gcq_onlist(&spipe->xq) ? "XQ" : "");
  	DDOLOG("spipe: xfer %p buffer %p pflags %#x ptype %s",
 -	    spipe->xfer, spipe->buffer, spipe->pflags, pnames(spipe->ptype));
 +	    spipe->xfer, spipe->buffer, *(spipe->pflags), pnames(spipe->ptype));
  }

  void
 Index: sys/dev/ic/sl811hsvar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/sl811hsvar.h,v
 retrieving revision 1.6
 diff -u -r1.6 sl811hsvar.h
 --- sys/dev/ic/sl811hsvar.h	12 May 2009 14:25:18 -0000	1.6
 +++ sys/dev/ic/sl811hsvar.h	16 Mar 2010 23:20:13 -0000
 @@ -83,6 +83,8 @@

  	uint8_t			sc_ier; 	/* enabled interrupts */
  	uint8_t			sc_stride;	/* port stride */
 +
 +	unsigned int		pflags[2*USB_MAX_ENDPOINTS];	/* Pipe flags for each endpoint */
  };

  /* last preinit arguments are: max current (in mA, not mA/2), port stride */

From: Yorick Hardy <yorickhardy@gmail.com>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: kern/42979: Data toggle issues in usb(4), slhci(4), uhci(4) and ehci(4)
Date: Wed, 31 Mar 2010 14:55:01 +0200

 These changes seem to cause problems with umass, perhaps others.
 I have not yet been able to determine the problem.

 -- 
 Kind regards,

 Yorick Hardy

From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: kern/42979: Data toggle issues in usb(4), slhci(4), uhci(4)
	and ehci(4)
Date: Wed, 21 Apr 2010 01:30:52 +0000

 Not sent to gnats. (Recommended practice is to send all mail to just
 gnats-bugs; it resends everywhere else.)

    ------

 From: Yorick Hardy <yorickhardy@gmail.com>
 To: martin@NetBSD.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
 Subject: Re: kern/42979: Data toggle issues in usb(4), slhci(4), uhci(4) and
 	ehci(4)
 Date: Tue, 20 Apr 2010 20:31:20 +0200

 The problem was that the data toggle should be managed at per
 device, not per bus.

 A new patch which fixes this appears below. The approach below
 manages toggle state for all USB_MAX_DEVICES potential devices
 which seems wasteful.

 -- 
 Kind regards,

 Yorick Hardy

 Index: sys/dev/ic/sl811hs.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/sl811hs.c,v
 retrieving revision 1.25
 diff -u -r1.25 sl811hs.c
 --- sys/dev/ic/sl811hs.c	25 Nov 2009 14:28:50 -0000	1.25
 +++ sys/dev/ic/sl811hs.c	20 Apr 2010 07:23:24 -0000
 @@ -280,7 +280,7 @@
  	struct gcq 	ap;		/* All pipes */
  	struct gcq 	to;		/* Timeout list */
  	struct gcq 	xq;		/* Xfer queues */
 -	unsigned int	pflags;		/* Pipe flags */
 +	unsigned int	*pflags;	/* Pipe flags */
  #define PF_GONE		(0x01)		/* Pipe is on disabled device */
  #define PF_TOGGLE 	(0x02)		/* Data toggle status */
  #define PF_LS		(0x04)		/* Pipe is low speed */
 @@ -433,6 +433,7 @@
  void slhci_freem(struct usbd_bus *, usb_dma_t *);
  struct usbd_xfer * slhci_allocx(struct usbd_bus *);
  void slhci_freex(struct usbd_bus *, struct usbd_xfer *);
 +void slhci_clear_all_toggle(struct usbd_bus *, struct usbd_device *);

  usbd_status slhci_transfer(struct usbd_xfer *);
  usbd_status slhci_start(struct usbd_xfer *);
 @@ -685,6 +686,7 @@
  	slhci_freem,
  	slhci_allocx,
  	slhci_freex,
 +	slhci_clear_all_toggle,
  };

  const struct usbd_pipe_methods slhci_pipe_methods = {
 @@ -849,6 +851,15 @@
  	free(xfer, M_USB);
  }

 +void slhci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct slhci_softc *sc = bus->hci_private;
 +	int i;
 +
 +	for (i = 0; i < 2*USB_MAX_ENDPOINTS; i++)
 +		sc->pflags[dev->address][i] &= ~PF_TOGGLE;
 +}
 +
  usbd_status
  slhci_transfer(struct usbd_xfer *xfer)
  {
 @@ -923,7 +934,7 @@
  	spipe->newlen[1] = min(xfer->length, max_packet);

  	if (spipe->ptype == PT_BULK || spipe->ptype == PT_INTR) {
 -		if (spipe->pflags & PF_TOGGLE)
 +		if (*(spipe->pflags) & PF_TOGGLE)
  			spipe->control |= SL11_EPCTRL_DATATOGGLE;
  		spipe->tregs[LEN] = spipe->newlen[1];
  		if (spipe->tregs[LEN]) 
 @@ -971,10 +982,10 @@
  	 * same place shares constants. Index 0 is "short length" for bulk and 
  	 * ctrl data and 1 is "full length" for ctrl data (bulk/intr are 
  	 * already set to full length). */
 -	if (spipe->pflags & PF_LS) {
 +	if (*(spipe->pflags) & PF_LS) {
  		/* Setting PREAMBLE for directly connnected LS devices will
  		 * lock up the chip. */
 -		if (spipe->pflags & PF_PREAMBLE)
 +		if (*(spipe->pflags) & PF_PREAMBLE)
  			spipe->control |= SL11_EPCTRL_PREAMBLE;
  		if (max_packet <= 8) {
  			spipe->bustime = SLHCI_LS_CONST + 
 @@ -1031,6 +1042,7 @@
  	usb_endpoint_descriptor_t *ed;
  	struct slhci_transfers *t;
  	unsigned int max_packet, pmaxpkt;
 +	int epaddr;

  	dev = pipe->device;
  	sc = dev->bus->hci_private;
 @@ -1041,7 +1053,13 @@
  	DLOG(D_TRACE, "slhci_open(addr=%d,ep=%d,rootaddr=%d)",
  		dev->address, ed->bEndpointAddress, t->rootaddr, 0);

 -	spipe->pflags = 0;
 +	epaddr = UE_GET_ADDR(ed->bEndpointAddress);
 +	if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
 +		epaddr += USB_MAX_ENDPOINTS;
 +
 +	spipe->pflags = &(sc->pflags[dev->address][epaddr]);
 +
 +	*(spipe->pflags) &= PF_TOGGLE; /* clear all flags except toggle */
  	spipe->frame = 0;
  	spipe->lastframe = 0;
  	spipe->xfer = NULL;
 @@ -1058,9 +1076,9 @@
  	max_packet = UGETW(ed->wMaxPacketSize);

  	if (dev->speed == USB_SPEED_LOW) {
 -		spipe->pflags |= PF_LS;
 +		*(spipe->pflags) |= PF_LS;
  		if (dev->myhub->address != t->rootaddr) {
 -			spipe->pflags |= PF_PREAMBLE;
 +			*(spipe->pflags) |= PF_PREAMBLE;
  			if (!slhci_try_lsvh)
  				return slhci_lock_call(sc, &slhci_lsvh_warn, 
  				    spipe, NULL);
 @@ -1291,7 +1309,7 @@
  	DLOG(D_TRACE, "%s toggle spipe %p", pnames(spipe->ptype), 
  	    spipe,0,0);

 -	spipe->pflags &= ~PF_TOGGLE;
 +	*(spipe->pflags) &= ~PF_TOGGLE;

  #ifdef DIAGNOSTIC
  	if (spipe->xfer != NULL) {
 @@ -2085,7 +2103,7 @@
  status_setup:
  			/* CTRL_DATA swaps direction in PID then jumps here */
  			spipe->tregs[LEN] = 0;
 -			if (spipe->pflags & PF_LS)
 +			if (*(spipe->pflags) & PF_LS)
  				spipe->bustime = SLHCI_LS_CONST;
  			else
  				spipe->bustime = SLHCI_FS_CONST;
 @@ -2140,9 +2158,9 @@
  			 * current setting will apply to the next 
  			 * transfer. */ 
  			if (spipe->control & SL11_EPCTRL_DATATOGGLE)
 -				spipe->pflags |= PF_TOGGLE;
 +				*(spipe->pflags) |= PF_TOGGLE;
  			else
 -				spipe->pflags &= ~PF_TOGGLE;
 +				*(spipe->pflags) &= ~PF_TOGGLE;

  			head = Q_CALLBACKS;
  		}
 @@ -2373,7 +2391,7 @@
  	SLHCI_MAINLOCKASSERT(sc);

  	if (__predict_false(t->flags & F_DISABLED) || 
 -	    __predict_false(spipe->pflags & PF_GONE)) {
 +	    __predict_false(*(spipe->pflags) & PF_GONE)) {
  		DLOG(D_MSG, "slhci_enter_xfer: DISABLED or GONE", 0,0,0,0);
  		spipe->xfer->status = USBD_CANCELLED; 
  	}
 @@ -2799,7 +2817,7 @@
  	/* Cancel all pipes.  Note that not all of these may be on the 
  	 * callback queue yet; some could be in slhci_start, for example. */
  	FOREACH_AP(q, t, spipe) {
 -		spipe->pflags = PF_GONE;
 +		*(spipe->pflags) = PF_GONE;
  		spipe->pipe.repeat = 0;
  		spipe->pipe.aborting = 1;
  		if (spipe->xfer != NULL)
 @@ -2928,7 +2946,7 @@
  	t = &sc->sc_transfers;
  	max_packet = UGETW(spipe->pipe.endpoint->edesc->wMaxPacketSize);

 -	if (spipe->pflags & PF_LS)
 +	if (*(spipe->pflags) & PF_LS)
  		bustime = SLHCI_LS_CONST + SLHCI_LS_DATA_TIME(max_packet);
  	else
  		bustime = SLHCI_FS_CONST + SLHCI_FS_DATA_TIME(max_packet);
 @@ -3612,7 +3630,7 @@
  	    "AP" : "", gcq_onlist(&spipe->to) ? "TO" : "", 
  	    gcq_onlist(&spipe->xq) ? "XQ" : "");
  	DDOLOG("spipe: xfer %p buffer %p pflags %#x ptype %s",
 -	    spipe->xfer, spipe->buffer, spipe->pflags, pnames(spipe->ptype));
 +	    spipe->xfer, spipe->buffer, *(spipe->pflags), pnames(spipe->ptype));
  }

  void
 Index: sys/dev/ic/sl811hsvar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/sl811hsvar.h,v
 retrieving revision 1.6
 diff -u -r1.6 sl811hsvar.h
 --- sys/dev/ic/sl811hsvar.h	12 May 2009 14:25:18 -0000	1.6
 +++ sys/dev/ic/sl811hsvar.h	20 Apr 2010 07:23:24 -0000
 @@ -83,6 +83,9 @@

  	uint8_t			sc_ier; 	/* enabled interrupts */
  	uint8_t			sc_stride;	/* port stride */
 +
 +	/* Pipe flags for each endpoint for each device */
 +	unsigned int		pflags[USB_MAX_DEVICES][2*USB_MAX_ENDPOINTS];
  };

  /* last preinit arguments are: max current (in mA, not mA/2), port stride */
 Index: sys/dev/usb/ehci.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ehci.c,v
 retrieving revision 1.166
 diff -u -r1.166 ehci.c
 --- sys/dev/usb/ehci.c	24 Feb 2010 22:38:09 -0000	1.166
 +++ sys/dev/usb/ehci.c	20 Apr 2010 07:23:25 -0000
 @@ -91,7 +91,6 @@

  struct ehci_pipe {
  	struct usbd_pipe pipe;
 -	int nexttoggle;

  	ehci_soft_qh_t *sqh;
  	union {
 @@ -120,6 +119,32 @@
  	} u;
  };

 +#define EHCI_NEXTTOGGLE(epipe) \
 +	((ehci_softc_t*)((epipe)->pipe.device->bus->hci_private)) \
 +		->nexttoggle[(epipe)->pipe.device->address]
 +
 +#define EHCI_PIPE_ENDPOINT_ADDR(epipe) \
 +	UE_GET_ADDR((epipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define EHCI_PIPE_ENDPOINT_DIR(epipe) \
 +	UE_GET_DIR((epipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define EHCI_NEXTTOGGLE_BIT(epipe) \
 +	((EHCI_PIPE_ENDPOINT_DIR(epipe) == UE_DIR_OUT) ? \
 +		(0x00000001 << EHCI_PIPE_ENDPOINT_ADDR(epipe)) : \
 +		(0x00010000 << EHCI_PIPE_ENDPOINT_ADDR(epipe)))
 +
 +#define EHCI_NEXTTOGGLE_SET(epipe) \
 +	SET(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
 +
 +#define EHCI_NEXTTOGGLE_ISSET(epipe) \
 +	ISSET(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
 +
 +#define EHCI_NEXTTOGGLE_GET(epipe) ((EHCI_NEXTTOGGLE_ISSET(epipe))?1:0)
 +
 +#define EHCI_NEXTTOGGLE_CLR(epipe) \
 +	CLR(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
 +
  Static usbd_status	ehci_open(usbd_pipe_handle);
  Static void		ehci_poll(struct usbd_bus *);
  Static void		ehci_softintr(void *);
 @@ -139,6 +164,9 @@
  Static usbd_xfer_handle	ehci_allocx(struct usbd_bus *);
  Static void		ehci_freex(struct usbd_bus *, usbd_xfer_handle);

 +Static void		ehci_clear_all_toggle(struct usbd_bus *,
 +						struct usbd_device *);
 +
  Static usbd_status	ehci_root_ctrl_transfer(usbd_xfer_handle);
  Static usbd_status	ehci_root_ctrl_start(usbd_xfer_handle);
  Static void		ehci_root_ctrl_abort(usbd_xfer_handle);
 @@ -252,6 +280,7 @@
  	ehci_freem,
  	ehci_allocx,
  	ehci_freex,
 +	ehci_clear_all_toggle,
  };

  Static const struct usbd_pipe_methods ehci_root_ctrl_methods = {
 @@ -533,6 +562,10 @@
  		return (USBD_IOERROR);
  	}

 +	/* initialize all data toggles to zero */
 +	for (i = 0; i < USB_MAX_DEVICES; i++)
 +		sc->nexttoggle[i] = 0;
 +
  	/* Enable interrupts */
  	DPRINTFN(1,("ehci_init: enabling\n"));
  	EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
 @@ -953,7 +986,10 @@
  		ehci_dump_sqh(epipe->sqh);
  		ehci_dump_sqtds(ex->sqtdstart);
  #endif
 -		epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus);
 +		if (EHCI_QTD_GET_TOGGLE(nstatus))
 +			EHCI_NEXTTOGGLE_SET(epipe);
 +		else
 +			EHCI_NEXTTOGGLE_CLR(epipe);
  	}

  	DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n",
 @@ -1318,6 +1354,14 @@
  }

  Static void
 +ehci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct ehci_softc *sc = bus->hci_private;
 +
 +	sc->nexttoggle[dev->address] = 0;
 +}
 +
 +Static void
  ehci_device_clear_toggle(usbd_pipe_handle pipe)
  {
  	struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
 @@ -1328,7 +1372,7 @@
  	if (ehcidebug)
  		usbd_dump_pipe(pipe);
  #endif
 -	epipe->nexttoggle = 0;
 +	EHCI_NEXTTOGGLE_CLR(epipe);
  }

  Static void
 @@ -1549,8 +1593,6 @@
  	if (sc->sc_dying)
  		return (USBD_IOERROR);

 -	epipe->nexttoggle = 0;
 -
  	if (addr == sc->sc_addr) {
  		switch (ed->bEndpointAddress) {
  		case USB_CONTROL_ENDPOINT:
 @@ -2597,7 +2639,7 @@
  	    /* BYTES set below */
  	    ;
  	mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
 -	tog = epipe->nexttoggle;
 +	tog = EHCI_NEXTTOGGLE_GET(epipe);
  	qtdstatus |= EHCI_QTD_SET_TOGGLE(tog);

  	cur = ehci_alloc_sqtd(sc);
 @@ -2702,7 +2744,10 @@
  	usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd),
  	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
  	*ep = cur;
 -	epipe->nexttoggle = tog;
 +	if (tog)
 +		EHCI_NEXTTOGGLE_SET(epipe);
 +	else
 +		EHCI_NEXTTOGGLE_CLR(epipe);

  	DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
  		     *sp, *ep));
 @@ -2846,9 +2891,11 @@
  	ehci_soft_qtd_t *sqtd;
  	ehci_physaddr_t cur;
  	u_int32_t qhstatus;
 +	u_int32_t nstatus;
  	int s;
  	int hit;
  	int wake;
 +	int fixdt;

  	DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));

 @@ -2902,11 +2949,21 @@
  	    sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status),
  	    sizeof(sqh->qh.qh_qtd.qtd_status),
  	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
 -	for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
 +	for (sqtd = exfer->sqtdstart, fixdt = 1; ; sqtd = sqtd->nextqtd) {
  		usb_syncmem(&sqtd->dma,
  		    sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
  		    sizeof(sqtd->qtd.qtd_status),
  		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
 +		nstatus = le32toh(sqtd->qtd.qtd_status);
 +		if (fixdt && (nstatus & EHCI_QTD_ACTIVE)
 +		          && ((sqtd != exfer->sqtdend->nextqtd)
 +		              || EHCI_QTD_GET_BYTES(nstatus))) {
 +			if (EHCI_QTD_GET_TOGGLE(nstatus))
 +				EHCI_NEXTTOGGLE_SET(epipe);
 +			else
 +				EHCI_NEXTTOGGLE_CLR(epipe);
 +			fixdt = 0;
 +		}
  		sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED);
  		usb_syncmem(&sqtd->dma,
  		    sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
 @@ -3260,7 +3317,7 @@
  		ehci_soft_qtd_t *end;

  		/* Start toggle at 1. */
 -		epipe->nexttoggle = 1;
 +		EHCI_NEXTTOGGLE_SET(epipe);
  		err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
  			  &next, &end);
  		if (err)
 Index: sys/dev/usb/ehcivar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ehcivar.h,v
 retrieving revision 1.36
 diff -u -r1.36 ehcivar.h
 --- sys/dev/usb/ehcivar.h	24 Feb 2010 22:38:09 -0000	1.36
 +++ sys/dev/usb/ehcivar.h	20 Apr 2010 07:23:25 -0000
 @@ -167,6 +167,9 @@
  	device_t sc_child; /* /dev/usb# device */
  	char sc_dying;
  	struct usb_dma_reserve sc_dma_reserve;
 +
 +	/* data toggles for each endpoint of each device */
 +	u_int32_t nexttoggle[USB_MAX_DEVICES];
  } ehci_softc_t;

  #define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
 Index: sys/dev/usb/ohci.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ohci.c,v
 retrieving revision 1.206
 diff -u -r1.206 ohci.c
 --- sys/dev/usb/ohci.c	24 Feb 2010 22:38:09 -0000	1.206
 +++ sys/dev/usb/ohci.c	20 Apr 2010 07:23:26 -0000
 @@ -134,6 +134,9 @@
  Static usbd_xfer_handle	ohci_allocx(struct usbd_bus *);
  Static void		ohci_freex(struct usbd_bus *, usbd_xfer_handle);

 +Static void		ohci_clear_all_toggle(struct usbd_bus *,
 +						struct usbd_device *);
 +
  Static usbd_status	ohci_root_ctrl_transfer(usbd_xfer_handle);
  Static usbd_status	ohci_root_ctrl_start(usbd_xfer_handle);
  Static void		ohci_root_ctrl_abort(usbd_xfer_handle);
 @@ -273,6 +276,7 @@
  	ohci_freem,
  	ohci_allocx,
  	ohci_freex,
 +	ohci_clear_all_toggle,
  };

  Static const struct usbd_pipe_methods ohci_root_ctrl_methods = {
 @@ -983,6 +987,19 @@
  	SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
  }

 +Static void
 +ohci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct ohci_softc *sc = bus->hci_private;
 +	struct ohci_soft_ed *ed = sc->sc_bulk_head;
 +	while (ed != NULL) {
 +		if (OHCI_ED_GET_FA(ed->ed.ed_flags) == dev->address) {
 +			ed->ed.ed_headp &= HTOO32(~OHCI_TOGGLECARRY);
 +			ed = ed->next;
 +		}
 +	}
 +}
 +
  /*
   * Shut down the controller when the system is going down.
   */
 Index: sys/dev/usb/uhci.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/uhci.c,v
 retrieving revision 1.232
 diff -u -r1.232 uhci.c
 --- sys/dev/usb/uhci.c	24 Feb 2010 22:38:09 -0000	1.232
 +++ sys/dev/usb/uhci.c	20 Apr 2010 07:23:26 -0000
 @@ -91,7 +91,6 @@

  struct uhci_pipe {
  	struct usbd_pipe pipe;
 -	int nexttoggle;

  	u_char aborting;
  	usbd_xfer_handle abortstart, abortend;
 @@ -125,6 +124,32 @@
  	} u;
  };

 +#define UHCI_NEXTTOGGLE(upipe) \
 +	((uhci_softc_t*)((upipe)->pipe.device->bus->hci_private)) \
 +		->nexttoggle[(upipe)->pipe.device->address]
 +
 +#define UHCI_PIPE_ENDPOINT_ADDR(upipe) \
 +	UE_GET_ADDR((upipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define UHCI_PIPE_ENDPOINT_DIR(upipe) \
 +	UE_GET_DIR((upipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define UHCI_NEXTTOGGLE_BIT(upipe) \
 +	((UHCI_PIPE_ENDPOINT_DIR(upipe) == UE_DIR_OUT) ? \
 +		(0x00000001 << UHCI_PIPE_ENDPOINT_ADDR(upipe)) : \
 +		(0x00010000 << UHCI_PIPE_ENDPOINT_ADDR(upipe)))
 +
 +#define UHCI_NEXTTOGGLE_SET(upipe) \
 +	SET(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
 +
 +#define UHCI_NEXTTOGGLE_ISSET(upipe) \
 +	ISSET(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
 +
 +#define UHCI_NEXTTOGGLE_GET(upipe) ((UHCI_NEXTTOGGLE_ISSET(upipe))?1:0)
 +
 +#define UHCI_NEXTTOGGLE_CLR(upipe) \
 +	CLR(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
 +
  Static void		uhci_globalreset(uhci_softc_t *);
  Static usbd_status	uhci_portreset(uhci_softc_t*, int);
  Static void		uhci_reset(uhci_softc_t *);
 @@ -171,6 +196,9 @@
  Static usbd_xfer_handle	uhci_allocx(struct usbd_bus *);
  Static void		uhci_freex(struct usbd_bus *, usbd_xfer_handle);

 +Static void		uhci_clear_all_toggle(struct usbd_bus *,
 +						struct usbd_device *);
 +
  Static usbd_status	uhci_device_ctrl_transfer(usbd_xfer_handle);
  Static usbd_status	uhci_device_ctrl_start(usbd_xfer_handle);
  Static void		uhci_device_ctrl_abort(usbd_xfer_handle);
 @@ -287,6 +315,7 @@
  	uhci_freem,
  	uhci_allocx,
  	uhci_freex,
 +	uhci_clear_all_toggle,
  };

  const struct usbd_pipe_methods uhci_root_ctrl_methods = {
 @@ -529,6 +558,10 @@

  	UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */

 +	/* initialize all data toggles to zero */
 +	for (i = 0; i < USB_MAX_DEVICES; i++)
 +		sc->nexttoggle[i] = 0;
 +
  	DPRINTFN(1,("uhci_init: enabling\n"));

  	err =  uhci_run(sc, 1);		/* and here we go... */
 @@ -683,6 +716,14 @@
  	SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
  }

 +Static void
 +uhci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct uhci_softc *sc = bus->hci_private;
 +
 +	sc->nexttoggle[dev->address] = 0;
 +}
 +
  /*
   * Handle suspend/resume.
   *
 @@ -1564,8 +1605,12 @@
  		}
  	}
  	/* If there are left over TDs we need to update the toggle. */
 -	if (std != NULL)
 -		upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token));
 +	if (std != NULL) {
 +		if (UHCI_TD_GET_DT(le32toh(std->td.td_token)))
 +			UHCI_NEXTTOGGLE_SET(upipe);
 +		else
 +			UHCI_NEXTTOGGLE_CLR(upipe);
 +	}

  	status &= UHCI_TD_ERROR;
  	DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n",
 @@ -1884,10 +1929,13 @@
  		DPRINTFN(-1,("uhci_alloc_std_chain: ntd=0\n"));
  		return (USBD_NORMAL_COMPLETION);
  	}
 -	tog = upipe->nexttoggle;
 +	tog = UHCI_NEXTTOGGLE_GET(upipe);
  	if (ntd % 2 == 0)
  		tog ^= 1;
 -	upipe->nexttoggle = tog ^ 1;
 +	if (tog ^ 1)
 +		UHCI_NEXTTOGGLE_SET(upipe);
 +	else
 +		UHCI_NEXTTOGGLE_CLR(upipe);
  	lastp = NULL;
  	lastlink = UHCI_PTR_T;
  	ntd--;
 @@ -1928,7 +1976,7 @@
  	}
  	*sp = lastp;
  	DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n",
 -		      upipe->nexttoggle));
 +		      UHCI_NEXTTOGGLE_GET(upipe)));
  	return (USBD_NORMAL_COMPLETION);
  }

 @@ -1936,7 +1984,7 @@
  uhci_device_clear_toggle(usbd_pipe_handle pipe)
  {
  	struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
 -	upipe->nexttoggle = 0;
 +	UHCI_NEXTTOGGLE_CLR(upipe);
  }

  void
 @@ -2077,6 +2125,7 @@
  	uhci_soft_td_t *std;
  	int s;
  	int wake;
 +	int fixdt;

  	DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));

 @@ -2120,11 +2169,18 @@
  	xfer->status = status;	/* make software ignore it */
  	usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
  	DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
 -	for (std = ii->stdstart; std != NULL; std = std->link.std) {
 +	for (std = ii->stdstart, fixdt = 1; std != NULL; std = std->link.std) {
  		usb_syncmem(&std->dma,
  		    std->offs + offsetof(uhci_td_t, td_status),
  		    sizeof(std->td.td_status),
  		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
 +		if (fixdt && (le32toh(std->td.td_status) & UHCI_TD_ACTIVE)) {
 +			if (UHCI_TD_GET_DT(le32toh(std->td.td_token)))
 +				UHCI_NEXTTOGGLE_SET(upipe);
 +			else
 +				UHCI_NEXTTOGGLE_CLR(upipe);
 +			fixdt = 0;
 +		}
  		std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
  		usb_syncmem(&std->dma,
  		    std->offs + offsetof(uhci_td_t, td_status),
 @@ -2409,7 +2465,7 @@

  	/* Set up data transaction */
  	if (len != 0) {
 -		upipe->nexttoggle = 1;
 +		UHCI_NEXTTOGGLE_SET(upipe);
  		err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags,
  					   &xfer->dmabuf, &data, &dataend);
  		if (err)
 @@ -3183,7 +3239,6 @@
  		     ed->bEndpointAddress, sc->sc_addr));

  	upipe->aborting = 0;
 -	upipe->nexttoggle = 0;

  	if (pipe->device->address == sc->sc_addr) {
  		switch (ed->bEndpointAddress) {
 Index: sys/dev/usb/uhcivar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/uhcivar.h,v
 retrieving revision 1.47
 diff -u -r1.47 uhcivar.h
 --- sys/dev/usb/uhcivar.h	24 Feb 2010 22:38:09 -0000	1.47
 +++ sys/dev/usb/uhcivar.h	20 Apr 2010 07:23:26 -0000
 @@ -181,6 +181,8 @@
  #ifdef __NetBSD__
  	struct usb_dma_reserve sc_dma_reserve;
  #endif
 +	/* data toggles for each endpoint of each device */
 +	u_int32_t nexttoggle[USB_MAX_DEVICES];
  } uhci_softc_t;

  usbd_status	uhci_init(uhci_softc_t *);
 Index: sys/dev/usb/usb_subr.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v
 retrieving revision 1.167
 diff -u -r1.167 usb_subr.c
 --- sys/dev/usb/usb_subr.c	12 Nov 2009 20:11:35 -0000	1.167
 +++ sys/dev/usb/usb_subr.c	20 Apr 2010 07:23:26 -0000
 @@ -698,6 +698,9 @@
  		}
  	}

 +	/* this is a configuration event, so reset all data toggles */
 +	dev->bus->methods->clear_all_toggle(dev->bus, dev);
 +
  	return (USBD_NORMAL_COMPLETION);

   bad:
 Index: sys/dev/usb/usbdi.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/usbdi.c,v
 retrieving revision 1.127
 diff -u -r1.127 usbdi.c
 --- sys/dev/usb/usbdi.c	16 Jan 2010 17:03:03 -0000	1.127
 +++ sys/dev/usb/usbdi.c	20 Apr 2010 07:23:31 -0000
 @@ -669,7 +669,14 @@
  	USETW(req.wValue, iface->idesc->bAlternateSetting);
  	USETW(req.wIndex, iface->idesc->bInterfaceNumber);
  	USETW(req.wLength, 0);
 -	return (usbd_do_request(iface->device, &req, 0));
 +	err = usbd_do_request(iface->device, &req, 0);
 +
 +	/* this is a configuration event, so reset all data toggles */
 +	if (!err)
 +		iface->device->bus->methods
 +			->clear_all_toggle(iface->device->bus, iface->device);
 +
 +	return err;
  }

  int
 Index: sys/dev/usb/usbdivar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/usbdivar.h,v
 retrieving revision 1.91
 diff -u -r1.91 usbdivar.h
 --- sys/dev/usb/usbdivar.h	12 Nov 2009 20:11:35 -0000	1.91
 +++ sys/dev/usb/usbdivar.h	20 Apr 2010 07:23:31 -0000
 @@ -57,6 +57,8 @@
  	void		      (*freem)(struct usbd_bus *, usb_dma_t *);
  	struct usbd_xfer *    (*allocx)(struct usbd_bus *);
  	void		      (*freex)(struct usbd_bus *, struct usbd_xfer *);
 +	void		      (*clear_all_toggle)(struct usbd_bus *,
 +							struct usbd_device *);
  };

  struct usbd_pipe_methods {

From: Yorick Hardy <yorickhardy@gmail.com>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: kern/42979: Data toggle issues in usb(4), slhci(4), uhci(4) and ehci(4)
Date: Sat, 24 Apr 2010 07:16:26 +0200

 Updated patch to fix the ohci problem pointed out by
 Jonathan Kollasch on current-users.

 -- 
 Kind regards,

 Yorick Hardy

 Index: sys/dev/ic/sl811hs.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/sl811hs.c,v
 retrieving revision 1.25
 diff -u -r1.25 sl811hs.c
 --- sys/dev/ic/sl811hs.c	25 Nov 2009 14:28:50 -0000	1.25
 +++ sys/dev/ic/sl811hs.c	20 Apr 2010 07:23:24 -0000
 @@ -280,7 +280,7 @@
  	struct gcq 	ap;		/* All pipes */
  	struct gcq 	to;		/* Timeout list */
  	struct gcq 	xq;		/* Xfer queues */
 -	unsigned int	pflags;		/* Pipe flags */
 +	unsigned int	*pflags;	/* Pipe flags */
  #define PF_GONE		(0x01)		/* Pipe is on disabled device */
  #define PF_TOGGLE 	(0x02)		/* Data toggle status */
  #define PF_LS		(0x04)		/* Pipe is low speed */
 @@ -433,6 +433,7 @@
  void slhci_freem(struct usbd_bus *, usb_dma_t *);
  struct usbd_xfer * slhci_allocx(struct usbd_bus *);
  void slhci_freex(struct usbd_bus *, struct usbd_xfer *);
 +void slhci_clear_all_toggle(struct usbd_bus *, struct usbd_device *);

  usbd_status slhci_transfer(struct usbd_xfer *);
  usbd_status slhci_start(struct usbd_xfer *);
 @@ -685,6 +686,7 @@
  	slhci_freem,
  	slhci_allocx,
  	slhci_freex,
 +	slhci_clear_all_toggle,
  };

  const struct usbd_pipe_methods slhci_pipe_methods = {
 @@ -849,6 +851,15 @@
  	free(xfer, M_USB);
  }

 +void slhci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct slhci_softc *sc = bus->hci_private;
 +	int i;
 +
 +	for (i = 0; i < 2*USB_MAX_ENDPOINTS; i++)
 +		sc->pflags[dev->address][i] &= ~PF_TOGGLE;
 +}
 +
  usbd_status
  slhci_transfer(struct usbd_xfer *xfer)
  {
 @@ -923,7 +934,7 @@
  	spipe->newlen[1] = min(xfer->length, max_packet);

  	if (spipe->ptype == PT_BULK || spipe->ptype == PT_INTR) {
 -		if (spipe->pflags & PF_TOGGLE)
 +		if (*(spipe->pflags) & PF_TOGGLE)
  			spipe->control |= SL11_EPCTRL_DATATOGGLE;
  		spipe->tregs[LEN] = spipe->newlen[1];
  		if (spipe->tregs[LEN]) 
 @@ -971,10 +982,10 @@
  	 * same place shares constants. Index 0 is "short length" for bulk and 
  	 * ctrl data and 1 is "full length" for ctrl data (bulk/intr are 
  	 * already set to full length). */
 -	if (spipe->pflags & PF_LS) {
 +	if (*(spipe->pflags) & PF_LS) {
  		/* Setting PREAMBLE for directly connnected LS devices will
  		 * lock up the chip. */
 -		if (spipe->pflags & PF_PREAMBLE)
 +		if (*(spipe->pflags) & PF_PREAMBLE)
  			spipe->control |= SL11_EPCTRL_PREAMBLE;
  		if (max_packet <= 8) {
  			spipe->bustime = SLHCI_LS_CONST + 
 @@ -1031,6 +1042,7 @@
  	usb_endpoint_descriptor_t *ed;
  	struct slhci_transfers *t;
  	unsigned int max_packet, pmaxpkt;
 +	int epaddr;

  	dev = pipe->device;
  	sc = dev->bus->hci_private;
 @@ -1041,7 +1053,13 @@
  	DLOG(D_TRACE, "slhci_open(addr=%d,ep=%d,rootaddr=%d)",
  		dev->address, ed->bEndpointAddress, t->rootaddr, 0);

 -	spipe->pflags = 0;
 +	epaddr = UE_GET_ADDR(ed->bEndpointAddress);
 +	if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
 +		epaddr += USB_MAX_ENDPOINTS;
 +
 +	spipe->pflags = &(sc->pflags[dev->address][epaddr]);
 +
 +	*(spipe->pflags) &= PF_TOGGLE; /* clear all flags except toggle */
  	spipe->frame = 0;
  	spipe->lastframe = 0;
  	spipe->xfer = NULL;
 @@ -1058,9 +1076,9 @@
  	max_packet = UGETW(ed->wMaxPacketSize);

  	if (dev->speed == USB_SPEED_LOW) {
 -		spipe->pflags |= PF_LS;
 +		*(spipe->pflags) |= PF_LS;
  		if (dev->myhub->address != t->rootaddr) {
 -			spipe->pflags |= PF_PREAMBLE;
 +			*(spipe->pflags) |= PF_PREAMBLE;
  			if (!slhci_try_lsvh)
  				return slhci_lock_call(sc, &slhci_lsvh_warn, 
  				    spipe, NULL);
 @@ -1291,7 +1309,7 @@
  	DLOG(D_TRACE, "%s toggle spipe %p", pnames(spipe->ptype), 
  	    spipe,0,0);

 -	spipe->pflags &= ~PF_TOGGLE;
 +	*(spipe->pflags) &= ~PF_TOGGLE;

  #ifdef DIAGNOSTIC
  	if (spipe->xfer != NULL) {
 @@ -2085,7 +2103,7 @@
  status_setup:
  			/* CTRL_DATA swaps direction in PID then jumps here */
  			spipe->tregs[LEN] = 0;
 -			if (spipe->pflags & PF_LS)
 +			if (*(spipe->pflags) & PF_LS)
  				spipe->bustime = SLHCI_LS_CONST;
  			else
  				spipe->bustime = SLHCI_FS_CONST;
 @@ -2140,9 +2158,9 @@
  			 * current setting will apply to the next 
  			 * transfer. */ 
  			if (spipe->control & SL11_EPCTRL_DATATOGGLE)
 -				spipe->pflags |= PF_TOGGLE;
 +				*(spipe->pflags) |= PF_TOGGLE;
  			else
 -				spipe->pflags &= ~PF_TOGGLE;
 +				*(spipe->pflags) &= ~PF_TOGGLE;

  			head = Q_CALLBACKS;
  		}
 @@ -2373,7 +2391,7 @@
  	SLHCI_MAINLOCKASSERT(sc);

  	if (__predict_false(t->flags & F_DISABLED) || 
 -	    __predict_false(spipe->pflags & PF_GONE)) {
 +	    __predict_false(*(spipe->pflags) & PF_GONE)) {
  		DLOG(D_MSG, "slhci_enter_xfer: DISABLED or GONE", 0,0,0,0);
  		spipe->xfer->status = USBD_CANCELLED; 
  	}
 @@ -2799,7 +2817,7 @@
  	/* Cancel all pipes.  Note that not all of these may be on the 
  	 * callback queue yet; some could be in slhci_start, for example. */
  	FOREACH_AP(q, t, spipe) {
 -		spipe->pflags = PF_GONE;
 +		*(spipe->pflags) = PF_GONE;
  		spipe->pipe.repeat = 0;
  		spipe->pipe.aborting = 1;
  		if (spipe->xfer != NULL)
 @@ -2928,7 +2946,7 @@
  	t = &sc->sc_transfers;
  	max_packet = UGETW(spipe->pipe.endpoint->edesc->wMaxPacketSize);

 -	if (spipe->pflags & PF_LS)
 +	if (*(spipe->pflags) & PF_LS)
  		bustime = SLHCI_LS_CONST + SLHCI_LS_DATA_TIME(max_packet);
  	else
  		bustime = SLHCI_FS_CONST + SLHCI_FS_DATA_TIME(max_packet);
 @@ -3612,7 +3630,7 @@
  	    "AP" : "", gcq_onlist(&spipe->to) ? "TO" : "", 
  	    gcq_onlist(&spipe->xq) ? "XQ" : "");
  	DDOLOG("spipe: xfer %p buffer %p pflags %#x ptype %s",
 -	    spipe->xfer, spipe->buffer, spipe->pflags, pnames(spipe->ptype));
 +	    spipe->xfer, spipe->buffer, *(spipe->pflags), pnames(spipe->ptype));
  }

  void
 Index: sys/dev/ic/sl811hsvar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/sl811hsvar.h,v
 retrieving revision 1.6
 diff -u -r1.6 sl811hsvar.h
 --- sys/dev/ic/sl811hsvar.h	12 May 2009 14:25:18 -0000	1.6
 +++ sys/dev/ic/sl811hsvar.h	20 Apr 2010 07:23:24 -0000
 @@ -83,6 +83,9 @@

  	uint8_t			sc_ier; 	/* enabled interrupts */
  	uint8_t			sc_stride;	/* port stride */
 +
 +	/* Pipe flags for each endpoint for each device */
 +	unsigned int		pflags[USB_MAX_DEVICES][2*USB_MAX_ENDPOINTS];
  };

  /* last preinit arguments are: max current (in mA, not mA/2), port stride */
 Index: sys/dev/usb/ehci.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ehci.c,v
 retrieving revision 1.166
 diff -u -r1.166 ehci.c
 --- sys/dev/usb/ehci.c	24 Feb 2010 22:38:09 -0000	1.166
 +++ sys/dev/usb/ehci.c	20 Apr 2010 07:23:25 -0000
 @@ -91,7 +91,6 @@

  struct ehci_pipe {
  	struct usbd_pipe pipe;
 -	int nexttoggle;

  	ehci_soft_qh_t *sqh;
  	union {
 @@ -120,6 +119,32 @@
  	} u;
  };

 +#define EHCI_NEXTTOGGLE(epipe) \
 +	((ehci_softc_t*)((epipe)->pipe.device->bus->hci_private)) \
 +		->nexttoggle[(epipe)->pipe.device->address]
 +
 +#define EHCI_PIPE_ENDPOINT_ADDR(epipe) \
 +	UE_GET_ADDR((epipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define EHCI_PIPE_ENDPOINT_DIR(epipe) \
 +	UE_GET_DIR((epipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define EHCI_NEXTTOGGLE_BIT(epipe) \
 +	((EHCI_PIPE_ENDPOINT_DIR(epipe) == UE_DIR_OUT) ? \
 +		(0x00000001 << EHCI_PIPE_ENDPOINT_ADDR(epipe)) : \
 +		(0x00010000 << EHCI_PIPE_ENDPOINT_ADDR(epipe)))
 +
 +#define EHCI_NEXTTOGGLE_SET(epipe) \
 +	SET(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
 +
 +#define EHCI_NEXTTOGGLE_ISSET(epipe) \
 +	ISSET(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
 +
 +#define EHCI_NEXTTOGGLE_GET(epipe) ((EHCI_NEXTTOGGLE_ISSET(epipe))?1:0)
 +
 +#define EHCI_NEXTTOGGLE_CLR(epipe) \
 +	CLR(EHCI_NEXTTOGGLE(epipe), EHCI_NEXTTOGGLE_BIT(epipe))
 +
  Static usbd_status	ehci_open(usbd_pipe_handle);
  Static void		ehci_poll(struct usbd_bus *);
  Static void		ehci_softintr(void *);
 @@ -139,6 +164,9 @@
  Static usbd_xfer_handle	ehci_allocx(struct usbd_bus *);
  Static void		ehci_freex(struct usbd_bus *, usbd_xfer_handle);

 +Static void		ehci_clear_all_toggle(struct usbd_bus *,
 +						struct usbd_device *);
 +
  Static usbd_status	ehci_root_ctrl_transfer(usbd_xfer_handle);
  Static usbd_status	ehci_root_ctrl_start(usbd_xfer_handle);
  Static void		ehci_root_ctrl_abort(usbd_xfer_handle);
 @@ -252,6 +280,7 @@
  	ehci_freem,
  	ehci_allocx,
  	ehci_freex,
 +	ehci_clear_all_toggle,
  };

  Static const struct usbd_pipe_methods ehci_root_ctrl_methods = {
 @@ -533,6 +562,10 @@
  		return (USBD_IOERROR);
  	}

 +	/* initialize all data toggles to zero */
 +	for (i = 0; i < USB_MAX_DEVICES; i++)
 +		sc->nexttoggle[i] = 0;
 +
  	/* Enable interrupts */
  	DPRINTFN(1,("ehci_init: enabling\n"));
  	EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
 @@ -953,7 +986,10 @@
  		ehci_dump_sqh(epipe->sqh);
  		ehci_dump_sqtds(ex->sqtdstart);
  #endif
 -		epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus);
 +		if (EHCI_QTD_GET_TOGGLE(nstatus))
 +			EHCI_NEXTTOGGLE_SET(epipe);
 +		else
 +			EHCI_NEXTTOGGLE_CLR(epipe);
  	}

  	DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n",
 @@ -1318,6 +1354,14 @@
  }

  Static void
 +ehci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct ehci_softc *sc = bus->hci_private;
 +
 +	sc->nexttoggle[dev->address] = 0;
 +}
 +
 +Static void
  ehci_device_clear_toggle(usbd_pipe_handle pipe)
  {
  	struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
 @@ -1328,7 +1372,7 @@
  	if (ehcidebug)
  		usbd_dump_pipe(pipe);
  #endif
 -	epipe->nexttoggle = 0;
 +	EHCI_NEXTTOGGLE_CLR(epipe);
  }

  Static void
 @@ -1549,8 +1593,6 @@
  	if (sc->sc_dying)
  		return (USBD_IOERROR);

 -	epipe->nexttoggle = 0;
 -
  	if (addr == sc->sc_addr) {
  		switch (ed->bEndpointAddress) {
  		case USB_CONTROL_ENDPOINT:
 @@ -2597,7 +2639,7 @@
  	    /* BYTES set below */
  	    ;
  	mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize);
 -	tog = epipe->nexttoggle;
 +	tog = EHCI_NEXTTOGGLE_GET(epipe);
  	qtdstatus |= EHCI_QTD_SET_TOGGLE(tog);

  	cur = ehci_alloc_sqtd(sc);
 @@ -2702,7 +2744,10 @@
  	usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd),
  	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
  	*ep = cur;
 -	epipe->nexttoggle = tog;
 +	if (tog)
 +		EHCI_NEXTTOGGLE_SET(epipe);
 +	else
 +		EHCI_NEXTTOGGLE_CLR(epipe);

  	DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n",
  		     *sp, *ep));
 @@ -2846,9 +2891,11 @@
  	ehci_soft_qtd_t *sqtd;
  	ehci_physaddr_t cur;
  	u_int32_t qhstatus;
 +	u_int32_t nstatus;
  	int s;
  	int hit;
  	int wake;
 +	int fixdt;

  	DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));

 @@ -2902,11 +2949,21 @@
  	    sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status),
  	    sizeof(sqh->qh.qh_qtd.qtd_status),
  	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
 -	for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) {
 +	for (sqtd = exfer->sqtdstart, fixdt = 1; ; sqtd = sqtd->nextqtd) {
  		usb_syncmem(&sqtd->dma,
  		    sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
  		    sizeof(sqtd->qtd.qtd_status),
  		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
 +		nstatus = le32toh(sqtd->qtd.qtd_status);
 +		if (fixdt && (nstatus & EHCI_QTD_ACTIVE)
 +		          && ((sqtd != exfer->sqtdend->nextqtd)
 +		              || EHCI_QTD_GET_BYTES(nstatus))) {
 +			if (EHCI_QTD_GET_TOGGLE(nstatus))
 +				EHCI_NEXTTOGGLE_SET(epipe);
 +			else
 +				EHCI_NEXTTOGGLE_CLR(epipe);
 +			fixdt = 0;
 +		}
  		sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED);
  		usb_syncmem(&sqtd->dma,
  		    sqtd->offs + offsetof(ehci_qtd_t, qtd_status),
 @@ -3260,7 +3317,7 @@
  		ehci_soft_qtd_t *end;

  		/* Start toggle at 1. */
 -		epipe->nexttoggle = 1;
 +		EHCI_NEXTTOGGLE_SET(epipe);
  		err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
  			  &next, &end);
  		if (err)
 Index: sys/dev/usb/ehcivar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ehcivar.h,v
 retrieving revision 1.36
 diff -u -r1.36 ehcivar.h
 --- sys/dev/usb/ehcivar.h	24 Feb 2010 22:38:09 -0000	1.36
 +++ sys/dev/usb/ehcivar.h	20 Apr 2010 07:23:25 -0000
 @@ -167,6 +167,9 @@
  	device_t sc_child; /* /dev/usb# device */
  	char sc_dying;
  	struct usb_dma_reserve sc_dma_reserve;
 +
 +	/* data toggles for each endpoint of each device */
 +	u_int32_t nexttoggle[USB_MAX_DEVICES];
  } ehci_softc_t;

  #define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
 Index: sys/dev/usb/uhci.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/uhci.c,v
 retrieving revision 1.232
 diff -u -r1.232 uhci.c
 --- sys/dev/usb/uhci.c	24 Feb 2010 22:38:09 -0000	1.232
 +++ sys/dev/usb/uhci.c	20 Apr 2010 07:23:26 -0000
 @@ -91,7 +91,6 @@

  struct uhci_pipe {
  	struct usbd_pipe pipe;
 -	int nexttoggle;

  	u_char aborting;
  	usbd_xfer_handle abortstart, abortend;
 @@ -125,6 +124,32 @@
  	} u;
  };

 +#define UHCI_NEXTTOGGLE(upipe) \
 +	((uhci_softc_t*)((upipe)->pipe.device->bus->hci_private)) \
 +		->nexttoggle[(upipe)->pipe.device->address]
 +
 +#define UHCI_PIPE_ENDPOINT_ADDR(upipe) \
 +	UE_GET_ADDR((upipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define UHCI_PIPE_ENDPOINT_DIR(upipe) \
 +	UE_GET_DIR((upipe)->pipe.endpoint->edesc->bEndpointAddress)
 +
 +#define UHCI_NEXTTOGGLE_BIT(upipe) \
 +	((UHCI_PIPE_ENDPOINT_DIR(upipe) == UE_DIR_OUT) ? \
 +		(0x00000001 << UHCI_PIPE_ENDPOINT_ADDR(upipe)) : \
 +		(0x00010000 << UHCI_PIPE_ENDPOINT_ADDR(upipe)))
 +
 +#define UHCI_NEXTTOGGLE_SET(upipe) \
 +	SET(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
 +
 +#define UHCI_NEXTTOGGLE_ISSET(upipe) \
 +	ISSET(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
 +
 +#define UHCI_NEXTTOGGLE_GET(upipe) ((UHCI_NEXTTOGGLE_ISSET(upipe))?1:0)
 +
 +#define UHCI_NEXTTOGGLE_CLR(upipe) \
 +	CLR(UHCI_NEXTTOGGLE(upipe), UHCI_NEXTTOGGLE_BIT(upipe))
 +
  Static void		uhci_globalreset(uhci_softc_t *);
  Static usbd_status	uhci_portreset(uhci_softc_t*, int);
  Static void		uhci_reset(uhci_softc_t *);
 @@ -171,6 +196,9 @@
  Static usbd_xfer_handle	uhci_allocx(struct usbd_bus *);
  Static void		uhci_freex(struct usbd_bus *, usbd_xfer_handle);

 +Static void		uhci_clear_all_toggle(struct usbd_bus *,
 +						struct usbd_device *);
 +
  Static usbd_status	uhci_device_ctrl_transfer(usbd_xfer_handle);
  Static usbd_status	uhci_device_ctrl_start(usbd_xfer_handle);
  Static void		uhci_device_ctrl_abort(usbd_xfer_handle);
 @@ -287,6 +315,7 @@
  	uhci_freem,
  	uhci_allocx,
  	uhci_freex,
 +	uhci_clear_all_toggle,
  };

  const struct usbd_pipe_methods uhci_root_ctrl_methods = {
 @@ -529,6 +558,10 @@

  	UHCICMD(sc, UHCI_CMD_MAXP); /* Assume 64 byte packets at frame end */

 +	/* initialize all data toggles to zero */
 +	for (i = 0; i < USB_MAX_DEVICES; i++)
 +		sc->nexttoggle[i] = 0;
 +
  	DPRINTFN(1,("uhci_init: enabling\n"));

  	err =  uhci_run(sc, 1);		/* and here we go... */
 @@ -683,6 +716,14 @@
  	SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
  }

 +Static void
 +uhci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct uhci_softc *sc = bus->hci_private;
 +
 +	sc->nexttoggle[dev->address] = 0;
 +}
 +
  /*
   * Handle suspend/resume.
   *
 @@ -1564,8 +1605,12 @@
  		}
  	}
  	/* If there are left over TDs we need to update the toggle. */
 -	if (std != NULL)
 -		upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token));
 +	if (std != NULL) {
 +		if (UHCI_TD_GET_DT(le32toh(std->td.td_token)))
 +			UHCI_NEXTTOGGLE_SET(upipe);
 +		else
 +			UHCI_NEXTTOGGLE_CLR(upipe);
 +	}

  	status &= UHCI_TD_ERROR;
  	DPRINTFN(10, ("uhci_idone: actlen=%d, status=0x%x\n",
 @@ -1884,10 +1929,13 @@
  		DPRINTFN(-1,("uhci_alloc_std_chain: ntd=0\n"));
  		return (USBD_NORMAL_COMPLETION);
  	}
 -	tog = upipe->nexttoggle;
 +	tog = UHCI_NEXTTOGGLE_GET(upipe);
  	if (ntd % 2 == 0)
  		tog ^= 1;
 -	upipe->nexttoggle = tog ^ 1;
 +	if (tog ^ 1)
 +		UHCI_NEXTTOGGLE_SET(upipe);
 +	else
 +		UHCI_NEXTTOGGLE_CLR(upipe);
  	lastp = NULL;
  	lastlink = UHCI_PTR_T;
  	ntd--;
 @@ -1928,7 +1976,7 @@
  	}
  	*sp = lastp;
  	DPRINTFN(10, ("uhci_alloc_std_chain: nexttog=%d\n",
 -		      upipe->nexttoggle));
 +		      UHCI_NEXTTOGGLE_GET(upipe)));
  	return (USBD_NORMAL_COMPLETION);
  }

 @@ -1936,7 +1984,7 @@
  uhci_device_clear_toggle(usbd_pipe_handle pipe)
  {
  	struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;
 -	upipe->nexttoggle = 0;
 +	UHCI_NEXTTOGGLE_CLR(upipe);
  }

  void
 @@ -2077,6 +2125,7 @@
  	uhci_soft_td_t *std;
  	int s;
  	int wake;
 +	int fixdt;

  	DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));

 @@ -2120,11 +2169,18 @@
  	xfer->status = status;	/* make software ignore it */
  	usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
  	DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
 -	for (std = ii->stdstart; std != NULL; std = std->link.std) {
 +	for (std = ii->stdstart, fixdt = 1; std != NULL; std = std->link.std) {
  		usb_syncmem(&std->dma,
  		    std->offs + offsetof(uhci_td_t, td_status),
  		    sizeof(std->td.td_status),
  		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
 +		if (fixdt && (le32toh(std->td.td_status) & UHCI_TD_ACTIVE)) {
 +			if (UHCI_TD_GET_DT(le32toh(std->td.td_token)))
 +				UHCI_NEXTTOGGLE_SET(upipe);
 +			else
 +				UHCI_NEXTTOGGLE_CLR(upipe);
 +			fixdt = 0;
 +		}
  		std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));
  		usb_syncmem(&std->dma,
  		    std->offs + offsetof(uhci_td_t, td_status),
 @@ -2409,7 +2465,7 @@

  	/* Set up data transaction */
  	if (len != 0) {
 -		upipe->nexttoggle = 1;
 +		UHCI_NEXTTOGGLE_SET(upipe);
  		err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags,
  					   &xfer->dmabuf, &data, &dataend);
  		if (err)
 @@ -3183,7 +3239,6 @@
  		     ed->bEndpointAddress, sc->sc_addr));

  	upipe->aborting = 0;
 -	upipe->nexttoggle = 0;

  	if (pipe->device->address == sc->sc_addr) {
  		switch (ed->bEndpointAddress) {
 Index: sys/dev/usb/uhcivar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/uhcivar.h,v
 retrieving revision 1.47
 diff -u -r1.47 uhcivar.h
 --- sys/dev/usb/uhcivar.h	24 Feb 2010 22:38:09 -0000	1.47
 +++ sys/dev/usb/uhcivar.h	20 Apr 2010 07:23:26 -0000
 @@ -181,6 +181,8 @@
  #ifdef __NetBSD__
  	struct usb_dma_reserve sc_dma_reserve;
  #endif
 +	/* data toggles for each endpoint of each device */
 +	u_int32_t nexttoggle[USB_MAX_DEVICES];
  } uhci_softc_t;

  usbd_status	uhci_init(uhci_softc_t *);
 Index: sys/dev/usb/usb_subr.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v
 retrieving revision 1.167
 diff -u -r1.167 usb_subr.c
 --- sys/dev/usb/usb_subr.c	12 Nov 2009 20:11:35 -0000	1.167
 +++ sys/dev/usb/usb_subr.c	20 Apr 2010 07:23:26 -0000
 @@ -698,6 +698,9 @@
  		}
  	}

 +	/* this is a configuration event, so reset all data toggles */
 +	dev->bus->methods->clear_all_toggle(dev->bus, dev);
 +
  	return (USBD_NORMAL_COMPLETION);

   bad:
 Index: sys/dev/usb/usbdi.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/usbdi.c,v
 retrieving revision 1.127
 diff -u -r1.127 usbdi.c
 --- sys/dev/usb/usbdi.c	16 Jan 2010 17:03:03 -0000	1.127
 +++ sys/dev/usb/usbdi.c	20 Apr 2010 07:23:31 -0000
 @@ -669,7 +669,14 @@
  	USETW(req.wValue, iface->idesc->bAlternateSetting);
  	USETW(req.wIndex, iface->idesc->bInterfaceNumber);
  	USETW(req.wLength, 0);
 -	return (usbd_do_request(iface->device, &req, 0));
 +	err = usbd_do_request(iface->device, &req, 0);
 +
 +	/* this is a configuration event, so reset all data toggles */
 +	if (!err)
 +		iface->device->bus->methods
 +			->clear_all_toggle(iface->device->bus, iface->device);
 +
 +	return err;
  }

  int
 Index: sys/dev/usb/usbdivar.h
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/usbdivar.h,v
 retrieving revision 1.91
 diff -u -r1.91 usbdivar.h
 --- sys/dev/usb/usbdivar.h	12 Nov 2009 20:11:35 -0000	1.91
 +++ sys/dev/usb/usbdivar.h	20 Apr 2010 07:23:31 -0000
 @@ -57,6 +57,8 @@
  	void		      (*freem)(struct usbd_bus *, usb_dma_t *);
  	struct usbd_xfer *    (*allocx)(struct usbd_bus *);
  	void		      (*freex)(struct usbd_bus *, struct usbd_xfer *);
 +	void		      (*clear_all_toggle)(struct usbd_bus *,
 +							struct usbd_device *);
  };

  struct usbd_pipe_methods {
 Index: ./sys/dev/usb/ohci.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/usb/ohci.c,v
 retrieving revision 1.206
 diff -u -r1.206 ohci.c
 --- ./sys/dev/usb/ohci.c	24 Feb 2010 22:38:09 -0000	1.206
 +++ ./sys/dev/usb/ohci.c	24 Apr 2010 05:11:38 -0000
 @@ -134,6 +134,9 @@
  Static usbd_xfer_handle	ohci_allocx(struct usbd_bus *);
  Static void		ohci_freex(struct usbd_bus *, usbd_xfer_handle);

 +Static void		ohci_clear_all_toggle(struct usbd_bus *,
 +						struct usbd_device *);
 +
  Static usbd_status	ohci_root_ctrl_transfer(usbd_xfer_handle);
  Static usbd_status	ohci_root_ctrl_start(usbd_xfer_handle);
  Static void		ohci_root_ctrl_abort(usbd_xfer_handle);
 @@ -273,6 +276,7 @@
  	ohci_freem,
  	ohci_allocx,
  	ohci_freex,
 +	ohci_clear_all_toggle,
  };

  Static const struct usbd_pipe_methods ohci_root_ctrl_methods = {
 @@ -983,6 +987,18 @@
  	SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
  }

 +Static void
 +ohci_clear_all_toggle(struct usbd_bus *bus, struct usbd_device *dev)
 +{
 +	struct ohci_softc *sc = bus->hci_private;
 +	struct ohci_soft_ed *ed = sc->sc_bulk_head;
 +	while (ed != NULL) {
 +		if (OHCI_ED_GET_FA(ed->ed.ed_flags) == dev->address)
 +			ed->ed.ed_headp &= HTOO32(~OHCI_TOGGLECARRY);
 +		ed = ed->next;
 +	}
 +}
 +
  /*
   * Shut down the controller when the system is going down.
   */

Responsible-Changed-From-To: martin->skrll
Responsible-Changed-By: skrll@NetBSD.org
Responsible-Changed-When: Sun, 08 Sep 2013 07:04:56 +0000
Responsible-Changed-Why:
Steal off martin (he won't mind)


>Unformatted:
 	<Please check that the above is correct for the bug being reported,>
 	<and append source date of snapshot, if applicable (one line).>

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.