From cb7e4340514c332ef2b3ac808a06bb5470be16eb Mon Sep 17 00:00:00 2001
From: iedowse <iedowse@FreeBSD.org>
Date: Wed, 3 Nov 2004 01:52:50 +0000
Subject: [PATCH] Merge recent USB2/EHCI related changes from NetBSD:  o Reduce
 the interrupt delay to 2 microframes.  o Follow the spec more closely when
 updating the overlay qTD in the QH.  o No need to generate an interrupt at
 the data part of a control    transfer, it's generated by the status
 transfer.  o Make sure to update the data toggle on short transfers.  o Turn
 the printf about needing toggle update into a DPRINTF.  o Keep track of what
 high speed port (if any) a device belongs to    so we can set the transaction
 translator fields for the transfer.  o Verbosely refuse to open low/full
 speed pipes that depend on    unimplemented split transaction support.  o Fix
 various typos in comments.

Obtained from:	NetBSD
---
 sys/dev/usb/ehci.c     | 199 ++++++++++++++++++++++++-----------------
 sys/dev/usb/ehcireg.h  |   3 +-
 sys/dev/usb/uhub.c     |  48 +++++++++-
 sys/dev/usb/usb_subr.c |  31 +++++--
 sys/dev/usb/usbdivar.h |   7 +-
 5 files changed, 194 insertions(+), 94 deletions(-)

diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
index fec0d50ae0ac..c5bb5bfdaa9c 100644
--- a/sys/dev/usb/ehci.c
+++ b/sys/dev/usb/ehci.c
@@ -1,15 +1,4 @@
-/*	$NetBSD: ehci.c,v 1.46 2003/03/09 19:51:13 augustss Exp $	*/
-
-/* Also ported from NetBSD:
- *	$NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $
- *	$NetBSD: ehci.c,v 1.54 2004/01/17 13:15:05 jdolecek Exp $
- *	    up to
- *	$NetBSD: ehci.c,v 1.64 2004/06/23 06:45:56 mycroft Exp $
- *	$NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $
- *	$NetBSD: ehci.c,v 1.67 2004/07/06 04:18:05 mycroft Exp $
- *	$NetBSD: ehci.c,v 1.68 2004/07/09 05:07:06 mycroft Exp $
- *	$NetBSD: ehci.c,v 1.69 2004/07/17 20:12:02 mycroft Exp $
- */
+/*	$NetBSD: ehci.c,v 1.87 2004/10/25 10:29:49 augustss Exp $	*/
 
 /*
  * TODO
@@ -74,7 +63,7 @@ __FBSDID("$FreeBSD$");
  *    Interrupt transfers are not difficult, it's just not done. 
  *
  * 3) The meaty part to implement is the support for USB 2.0 hubs.
- *    They are quite compolicated since the need to be able to do
+ *    They are quite complicated since the need to be able to do
  *    "transaction translation", i.e., converting to/from USB 2 and USB 1.
  *    So the hub driver needs to handle and schedule these things, to
  *    assign place in frame where different devices get to go. See chapter
@@ -125,8 +114,8 @@ __FBSDID("$FreeBSD$");
 
 #ifdef USB_DEBUG
 #define EHCI_DEBUG USB_DEBUG
-#define DPRINTF(x)	if (ehcidebug) logprintf x
-#define DPRINTFN(n,x)	if (ehcidebug>(n)) logprintf x
+#define DPRINTF(x)	do { if (ehcidebug) logprintf x; } while (0)
+#define DPRINTFN(n,x)	do { if (ehcidebug>(n)) logprintf x; } while (0)
 int ehcidebug = 0;
 SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci");
 SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW,
@@ -256,7 +245,7 @@ Static void		ehci_abort_xfer(usbd_xfer_handle, usbd_status);
 
 #ifdef EHCI_DEBUG
 Static void		ehci_dump_regs(ehci_softc_t *);
-void			ehci_dump(void);
+Static void		ehci_dump(void);
 Static ehci_softc_t 	*theehci;
 Static void		ehci_dump_link(ehci_link_t, int);
 Static void		ehci_dump_sqtds(ehci_soft_qtd_t *);
@@ -412,13 +401,13 @@ ehci_init(ehci_softc_t *sc)
 
 	/* frame list size at default, read back what we got and use that */
 	switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) {
-	case 0: sc->sc_flsize = 1024*4; break;
-	case 1: sc->sc_flsize = 512*4; break;
-	case 2: sc->sc_flsize = 256*4; break;
+	case 0: sc->sc_flsize = 1024; break;
+	case 1: sc->sc_flsize = 512; break;
+	case 2: sc->sc_flsize = 256; break;
 	case 3: return (USBD_IOERROR);
 	}
-	err = usb_allocmem(&sc->sc_bus, sc->sc_flsize,
-			   EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
+	err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t),
+	    EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
 	if (err)
 		return (err);
 	DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize));
@@ -473,7 +462,7 @@ ehci_init(ehci_softc_t *sc)
 		sqh->sqtd = NULL;
 	}
 	/* Point the frame list at the last level (128ms). */
-	for (i = 0; i < sc->sc_flsize / 4; i++) {
+	for (i = 0; i < sc->sc_flsize; i++) {
 		sc->sc_flist[i] = htole32(EHCI_LINK_QH |
 		    sc->sc_islots[EHCI_IQHIDX(EHCI_IPOLLRATES - 1,
 		    i)].sqh->physaddr);
@@ -516,7 +505,7 @@ ehci_init(ehci_softc_t *sc)
 
 	/* Turn on controller */
 	EOWRITE4(sc, EHCI_USBCMD,
-		 EHCI_CMD_ITC_8 | /* 8 microframes */
+		 EHCI_CMD_ITC_2 | /* 2 microframes interrupt delay */
 		 (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
 		 EHCI_CMD_ASE |
 		 EHCI_CMD_PSE |
@@ -557,8 +546,12 @@ ehci_intr(void *v)
 
 	/* If we get an interrupt while polling, then just ignore it. */
 	if (sc->sc_bus.use_polling) {
+		u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+
+		if (intrs)
+			EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */
 #ifdef DIAGNOSTIC
-		printf("ehci_intr: ignored interrupt while polling\n");
+		DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n"));
 #endif
 		return (0);
 	}
@@ -576,7 +569,7 @@ ehci_intr1(ehci_softc_t *sc)
 	/* In case the interrupt occurs before initialization has completed. */
 	if (sc == NULL) {
 #ifdef DIAGNOSTIC
-		printf("ehci_intr: sc == NULL\n");
+		printf("ehci_intr1: sc == NULL\n");
 #endif
 		return (0);
 	}
@@ -586,7 +579,7 @@ ehci_intr1(ehci_softc_t *sc)
 		return (0);
 
 	eintrs = intrs & sc->sc_eintrs;
-	DPRINTFN(7, ("ehci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n",
+	DPRINTFN(7, ("ehci_intr1: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n",
 		     sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS),
 		     (u_int)eintrs));
 	if (!eintrs)
@@ -733,7 +726,7 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
 	lsqtd = ex->sqtdend;
 #ifdef DIAGNOSTIC
 	if (lsqtd == NULL) {
-		printf("ehci_check_intr: sqtd==0\n");
+		printf("ehci_check_intr: lsqtd==0\n");
 		return;
 	}
 #endif
@@ -771,9 +764,10 @@ ehci_idone(struct ehci_xfer *ex)
 {
 	usbd_xfer_handle xfer = &ex->xfer;
 	struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;
-	ehci_soft_qtd_t *sqtd;
-	u_int32_t status, nstatus;
+	ehci_soft_qtd_t *sqtd, *lsqtd;
+	u_int32_t status = 0, nstatus = 0;
 	int actlen;
+	u_int pkts_left;
 
 	DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex));
 #ifdef DIAGNOSTIC
@@ -807,10 +801,9 @@ ehci_idone(struct ehci_xfer *ex)
 #endif
 
 	/* The transfer is done, compute actual length and status. */
+	lsqtd = ex->sqtdend;
 	actlen = 0;
-	nstatus = 0;
-	status = 0;
-	for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) {
+	for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd=sqtd->nextqtd) {
 		nstatus = le32toh(sqtd->qtd.qtd_status);
 		if (nstatus & EHCI_QTD_ACTIVE)
 			break;
@@ -824,9 +817,14 @@ ehci_idone(struct ehci_xfer *ex)
 			actlen += sqtd->len - EHCI_QTD_GET_BYTES(status);
 	}
 
-	/* If there are left over TDs we need to update the toggle. */
-	if (sqtd != NULL) {
-		printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus);
+	/*
+	 * If there are left over TDs we need to update the toggle.
+	 * The default pipe doesn't need it since control transfers
+	 * start the toggle at 0 every time.
+	 */
+	if (sqtd != lsqtd->nextqtd &&
+	    xfer->pipe->device->default_pipe != xfer->pipe) {
+		DPRINTF(("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus));
 #if 0
 		ehci_dump_sqh(epipe->sqh);
 		ehci_dump_sqtds(ex->sqtdstart);
@@ -834,6 +832,14 @@ ehci_idone(struct ehci_xfer *ex)
 		epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus);
 	}
 
+	/*
+	 * For a short transfer we need to update the toggle for the missing
+	 * packets within the qTD.
+	 */
+	pkts_left = EHCI_QTD_GET_BYTES(status) /
+	    UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
+	epipe->nexttoggle ^= pkts_left % 2;
+
 	status &= EHCI_QTD_STATERRS;
 	DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n",
 			   xfer->length, actlen, status));
@@ -846,7 +852,7 @@ ehci_idone(struct ehci_xfer *ex)
 				 "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR"
 				 "\3MISSED", sbuf, sizeof(sbuf));
 
-		DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2,
+		DPRINTFN((status == EHCI_QTD_HALTED) ? 2 : 0,
 			 ("ehci_idone: error, addr=%d, endpt=0x%02x, "
 			  "status 0x%s\n",
 			  xfer->pipe->device->address,
@@ -995,7 +1001,8 @@ ehci_power(int why, void *v)
 
 #ifdef EHCI_DEBUG
 	DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
-	ehci_dump_regs(sc);
+	if (ehcidebug > 0)
+		ehci_dump_regs(sc);
 #endif
 
 	s = splhardusb();
@@ -1154,7 +1161,7 @@ ehci_allocx(struct usbd_bus *bus)
 		SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
 #ifdef DIAGNOSTIC
 		if (xfer->busy_free != XFER_FREE) {
-			printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer,
+			printf("ehci_allocx: xfer=%p not free, 0x%08x\n", xfer,
 			       xfer->busy_free);
 		}
 #endif
@@ -1162,7 +1169,7 @@ ehci_allocx(struct usbd_bus *bus)
 		xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
 	}
 	if (xfer != NULL) {
-		memset(xfer, 0, sizeof (struct ehci_xfer));
+		memset(xfer, 0, sizeof(struct ehci_xfer));
 #ifdef DIAGNOSTIC
 		EXFER(xfer)->isdone = 1;
 		xfer->busy_free = XFER_BUSY;
@@ -1269,7 +1276,7 @@ ehci_dump_sqtds(ehci_soft_qtd_t *sqtd)
 	stop = 0;
 	for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) {
 		ehci_dump_sqtd(sqtd);
-		stop = sqtd->qtd.qtd_next & EHCI_LINK_TERMINATE;
+		stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE);
 	}
 	if (sqtd)
 		printf("dump aborted, too many TDs\n");
@@ -1354,10 +1361,19 @@ ehci_open(usbd_pipe_handle pipe)
 	usbd_status err;
 	int s;
 	int ival, speed, naks;
+	int hshubaddr, hshubport;
 
 	DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n",
 		     pipe, addr, ed->bEndpointAddress, sc->sc_addr));
 
+	if (dev->myhsport) {
+		hshubaddr = dev->myhsport->parent->address;
+		hshubport = dev->myhsport->portno;
+	} else {
+		hshubaddr = 0;
+		hshubport = 0;
+	}
+
 	if (sc->sc_dying)
 		return (USBD_IOERROR);
 
@@ -1384,6 +1400,16 @@ ehci_open(usbd_pipe_handle pipe)
 	case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break;
 	default: panic("ehci_open: bad device speed %d", dev->speed);
 	}
+	if (speed != EHCI_QH_SPEED_HIGH) {
+		printf("%s: *** WARNING: opening low/full speed device, this "
+		    "does not work yet.\n",
+		    USBDEVNAME(sc->sc_bus.bdev));
+		DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
+		    hshubaddr, hshubport));
+		if (xfertype != UE_CONTROL)
+			return USBD_INVAL;
+	}
+
 	naks = 8;		/* XXX */
 	sqh = ehci_alloc_sqh(sc);
 	if (sqh == NULL)
@@ -1401,7 +1427,9 @@ ehci_open(usbd_pipe_handle pipe)
 		);
 	sqh->qh.qh_endphub = htole32(
 		EHCI_QH_SET_MULT(1) |
-		/* XXX TT stuff */
+		EHCI_QH_SET_HUBA(hshubaddr) |
+		EHCI_QH_SET_PORT(hshubport) |
+		EHCI_QH_SET_CMASK(0xf0) | /* XXX */
 		EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
 		);
 	sqh->qh.qh_curqtd = EHCI_NULL;
@@ -1497,13 +1525,24 @@ ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
 void
 ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd)
 {
-	/* Halt while we are messing. */
-	sqh->qh.qh_qtd.qtd_status |= htole32(EHCI_QTD_HALTED);
+	int i;
+	u_int32_t status;
+
+	/* Save toggle bit and ping status. */
+	status = sqh->qh.qh_qtd.qtd_status &
+	    htole32(EHCI_QTD_TOGGLE_MASK |
+	    EHCI_QTD_SET_STATUS(EHCI_QTD_PINGSTATE));
+	/* Set HALTED to make hw leave it alone. */
+	sqh->qh.qh_qtd.qtd_status =
+	    htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
 	sqh->qh.qh_curqtd = 0;
 	sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr);
+	sqh->qh.qh_qtd.qtd_altnext = 0;
+	for (i = 0; i < EHCI_QTD_NBUFFERS; i++)
+		sqh->qh.qh_qtd.qtd_buffer[i] = 0;
 	sqh->sqtd = sqtd;
-	/* Clear halt */
-	sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_HALTED);
+	/* Set !HALTED && !ACTIVE to start execution, preserve some fields */
+	sqh->qh.qh_qtd.qtd_status = status;
 }
 
 /*
@@ -1617,10 +1656,7 @@ Static usb_hub_descriptor_t ehci_hubd = {
 };
 
 Static int
-ehci_str(p, l, s)
-	usb_string_descriptor_t *p;
-	int l;
-	char *s;
+ehci_str(usb_string_descriptor_t *p, int l, char *s)
 {
 	int i;
 
@@ -1676,7 +1712,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 #endif
 	req = &xfer->request;
 
-	DPRINTFN(4,("ehci_root_ctrl_control type=0x%02x request=%02x\n",
+	DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n",
 		    req->bmRequestType, req->bRequest));
 
 	len = UGETW(req->wLength);
@@ -1703,7 +1739,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 		}
 		break;
 	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
-		DPRINTFN(8,("ehci_root_ctrl_control wValue=0x%04x\n", value));
+		DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value));
 		switch(value >> 8) {
 		case UDESC_DEVICE:
 			if ((value & 0xff) != 0) {
@@ -1757,6 +1793,9 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 			*(u_int8_t *)buf = 0;
 			totlen = 1;
 			switch (value & 0xff) {
+			case 0: /* Language table */
+				totlen = ehci_str(buf, len, "\001");
+				break;
 			case 1: /* Vendor */
 				totlen = ehci_str(buf, len, sc->sc_vendor);
 				break;
@@ -1818,7 +1857,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
 		break;
 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
-		DPRINTFN(8, ("ehci_root_ctrl_control: UR_CLEAR_PORT_FEATURE "
+		DPRINTFN(8, ("ehci_root_ctrl_start: UR_CLEAR_PORT_FEATURE "
 			     "port=%d feature=%d\n",
 			     index, value));
 		if (index < 1 || index > sc->sc_noport) {
@@ -1838,11 +1877,11 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 			EOWRITE4(sc, port, v &~ EHCI_PS_PP);
 			break;
 		case UHF_PORT_TEST:
-			DPRINTFN(2,("ehci_root_ctrl_transfer: clear port test "
+			DPRINTFN(2,("ehci_root_ctrl_start: clear port test "
 				    "%d\n", index));
 			break;
 		case UHF_PORT_INDICATOR:
-			DPRINTFN(2,("ehci_root_ctrl_transfer: clear port ind "
+			DPRINTFN(2,("ehci_root_ctrl_start: clear port ind "
 				    "%d\n", index));
 			EOWRITE4(sc, port, v &~ EHCI_PS_PIC);
 			break;
@@ -1891,7 +1930,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 		v = EOREAD4(sc, EHCI_HCSPARAMS);
 		USETW(hubd.wHubCharacteristics,
 		    EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH |
-		    EHCI_HCS_P_INCICATOR(EREAD4(sc, EHCI_HCSPARAMS))
+		    EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS))
 		        ? UHD_PORT_IND : 0);
 		hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */
 		for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8)
@@ -1910,7 +1949,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 		totlen = len;
 		break;
 	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
-		DPRINTFN(8,("ehci_root_ctrl_transfer: get port status i=%d\n",
+		DPRINTFN(8,("ehci_root_ctrl_start: get port status i=%d\n",
 			    index));
 		if (index < 1 || index > sc->sc_noport) {
 			err = USBD_IOERROR;
@@ -1921,7 +1960,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 			goto ret;
 		}
 		v = EOREAD4(sc, EHCI_PORTSC(index));
-		DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n",
+		DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n",
 			    v));
 		i = UPS_HIGH_SPEED;
 		if (v & EHCI_PS_CS)	i |= UPS_CURRENT_CONNECT_STATUS;
@@ -1961,7 +2000,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 			EOWRITE4(sc, port, v | EHCI_PS_SUSP);
 			break;
 		case UHF_PORT_RESET:
-			DPRINTFN(5,("ehci_root_ctrl_transfer: reset port %d\n",
+			DPRINTFN(5,("ehci_root_ctrl_start: reset port %d\n",
 				    index));
 			if (EHCI_PS_IS_LOWSPEED(v)) {
 				/* Low speed device, give up ownership. */
@@ -2002,16 +2041,16 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
 				 index, v));
 			break;
 		case UHF_PORT_POWER:
-			DPRINTFN(2,("ehci_root_ctrl_transfer: set port power "
+			DPRINTFN(2,("ehci_root_ctrl_start: set port power "
 				    "%d\n", index));
 			EOWRITE4(sc, port, v | EHCI_PS_PP);
 			break;
 		case UHF_PORT_TEST:
-			DPRINTFN(2,("ehci_root_ctrl_transfer: set port test "
+			DPRINTFN(2,("ehci_root_ctrl_start: set port test "
 				    "%d\n", index));
 			break;
 		case UHF_PORT_INDICATOR:
-			DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind "
+			DPRINTFN(2,("ehci_root_ctrl_start: set port ind "
 				    "%d\n", index));
 			EOWRITE4(sc, port, v | EHCI_PS_PIC);
 			break;
@@ -2672,7 +2711,7 @@ ehci_device_request(usbd_xfer_handle xfer)
 	isread = req->bmRequestType & UT_READ;
 	len = UGETW(req->wLength);
 
-	DPRINTFN(3,("ehci_device_control type=0x%02x, request=0x%02x, "
+	DPRINTFN(3,("ehci_device_request: type=0x%02x, request=0x%02x, "
 		    "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n",
 		    req->bmRequestType, req->bRequest, UGETW(req->wValue),
 		    UGETW(req->wIndex), len, addr,
@@ -2713,6 +2752,7 @@ ehci_device_request(usbd_xfer_handle xfer)
 			  &next, &end);
 		if (err)
 			goto bad3;
+		end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC);
 		end->nextqtd = stat;
 		end->qtd.qtd_next =
 		end->qtd.qtd_altnext = htole32(stat->physaddr);
@@ -2834,7 +2874,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
 	int len, isread, endpt;
 	int s;
 
-	DPRINTFN(2, ("ehci_device_bulk_transfer: xfer=%p len=%d flags=%d\n",
+	DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n",
 		     xfer, xfer->length, xfer->flags));
 
 	if (sc->sc_dying)
@@ -2842,7 +2882,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
 
 #ifdef DIAGNOSTIC
 	if (xfer->rqflags & URQ_REQUEST)
-		panic("ehci_device_bulk_transfer: a request");
+		panic("ehci_device_bulk_start: a request");
 #endif
 
 	len = xfer->length;
@@ -2855,7 +2895,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
 	err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
 				   &dataend);
 	if (err) {
-		DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n"));
+		DPRINTFN(-1,("ehci_device_bulk_start: no memory\n"));
 		xfer->status = err;
 		usb_transfer_complete(xfer);
 		return (err);
@@ -2863,7 +2903,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
 
 #ifdef EHCI_DEBUG
 	if (ehcidebug > 5) {
-		DPRINTF(("ehci_device_bulk_transfer: data(1)\n"));
+		DPRINTF(("ehci_device_bulk_start: data(1)\n"));
 		ehci_dump_sqh(sqh);
 		ehci_dump_sqtds(data);
 	}
@@ -2874,7 +2914,7 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
 	exfer->sqtdend = dataend;
 #ifdef DIAGNOSTIC
 	if (!exfer->isdone) {
-		printf("ehci_device_bulk_transfer: not done, ex=%p\n", exfer);
+		printf("ehci_device_bulk_start: not done, ex=%p\n", exfer);
 	}
 	exfer->isdone = 0;
 #endif
@@ -2891,9 +2931,9 @@ ehci_device_bulk_start(usbd_xfer_handle xfer)
 
 #ifdef EHCI_DEBUG
 	if (ehcidebug > 10) {
-		DPRINTF(("ehci_device_bulk_transfer: data(2)\n"));
+		DPRINTF(("ehci_device_bulk_start: data(2)\n"));
 		delay(10000);
-		DPRINTF(("ehci_device_bulk_transfer: data(3)\n"));
+		DPRINTF(("ehci_device_bulk_start: data(3)\n"));
 		ehci_dump_regs(sc);
 #if 0
 		printf("async_head:\n");
@@ -2963,17 +3003,16 @@ ehci_device_setintr(ehci_softc_t *sc, ehci_soft_qh_t *sqh, int ival)
 			break;
 
 	/* Pick an interrupt slot at the right level. */
-	/* XXX, could do better than picking at random. */
+	/* XXX could do better than picking at random. */
 	islot = EHCI_IQHIDX(lev, arc4random());
 
 	sqh->islot = islot;
-	isp = &sc->sc_islots[sqh->islot];
+	isp = &sc->sc_islots[islot];
 	ehci_add_qh(sqh, isp->sqh);
 
 	return (USBD_NORMAL_COMPLETION);
 }
 
-
 Static usbd_status
 ehci_device_intr_transfer(usbd_xfer_handle xfer)
 {
@@ -3025,7 +3064,7 @@ ehci_device_intr_start(usbd_xfer_handle xfer)
 	err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
 	    &dataend);
 	if (err) {
-		DPRINTFN(-1,("ehci_device_intr_start: no memory\n"));
+		DPRINTFN(-1, ("ehci_device_intr_start: no memory\n"));
 		xfer->status = err;
 		usb_transfer_complete(xfer);
 		return (err);
@@ -3081,9 +3120,9 @@ ehci_device_intr_start(usbd_xfer_handle xfer)
 Static void
 ehci_device_intr_abort(usbd_xfer_handle xfer)
 {
-	DPRINTFN(1,("ehci_device_intr_abort: xfer=%p\n", xfer));
+	DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer));
 	if (xfer->pipe->intrxfer == xfer) {
-		DPRINTFN(1,("ehci_device_intr_abort: remove\n"));
+		DPRINTFN(1, ("ehci_device_intr_abort: remove\n"));
 		xfer->pipe->intrxfer = NULL;
 	}
 	ehci_abort_xfer(xfer, USBD_CANCELLED);
@@ -3112,8 +3151,8 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
 	usbd_status err;
 	int len, isread, endpt, s;
 
-	DPRINTFN(10,("ehci_device_intr_done: xfer=%p, actlen=%d\n",
-		     xfer, xfer->actlen));
+	DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n",
+	    xfer, xfer->actlen));
 
 	if (xfer->pipe->repeat) {
 		ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL);
@@ -3124,10 +3163,10 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
 		isread = UE_GET_DIR(endpt) == UE_DIR_IN;
 		sqh = epipe->sqh;
 
-		err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data,
-		    &dataend);
+		err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer,
+		    &data, &dataend);
 		if (err) {
-			DPRINTFN(-1,("ehci_device_intr_done: no memory\n"));
+			DPRINTFN(-1, ("ehci_device_intr_done: no memory\n"));
 			xfer->status = err;
 			return;
 		}
diff --git a/sys/dev/usb/ehcireg.h b/sys/dev/usb/ehcireg.h
index 634fc9e693d9..2aaa4c8107ad 100644
--- a/sys/dev/usb/ehcireg.h
+++ b/sys/dev/usb/ehcireg.h
@@ -84,7 +84,7 @@
 
 #define EHCI_HCSPARAMS		0x04	/* RO Structural parameters */
 #define  EHCI_HCS_DEBUGPORT(x)	(((x) >> 20) & 0xf)
-#define  EHCI_HCS_P_INCICATOR(x) ((x) & 0x10000)
+#define  EHCI_HCS_P_INDICATOR(x) ((x) & 0x10000)
 #define  EHCI_HCS_N_CC(x)	(((x) >> 12) & 0xf) /* # of companion ctlrs */
 #define  EHCI_HCS_N_PCC(x)	(((x) >> 8) & 0xf) /* # of ports per comp. */
 #define  EHCI_HCS_PPC(x)	((x) & 0x10) /* port power control */
@@ -218,6 +218,7 @@ typedef struct {
 	ehci_link_t	qtd_altnext;
 	u_int32_t	qtd_status;
 #define EHCI_QTD_GET_STATUS(x)	(((x) >>  0) & 0xff)
+#define EHCI_QTD_SET_STATUS(x) ((x) <<  0)
 #define  EHCI_QTD_ACTIVE	0x80
 #define  EHCI_QTD_HALTED	0x40
 #define  EHCI_QTD_BUFERR	0x20
diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c
index 54affde4750c..53784584a04a 100644
--- a/sys/dev/usb/uhub.c
+++ b/sys/dev/usb/uhub.c
@@ -85,6 +85,9 @@ struct uhub_softc {
 	u_int8_t		sc_status[1];	/* XXX more ports */
 	u_char			sc_running;
 };
+#define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol)
+#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
+#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
 
 Static usbd_status uhub_explore(usbd_device_handle hub);
 Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
@@ -162,12 +165,13 @@ USB_ATTACH(uhub)
 	usbd_device_handle dev = uaa->device;
 	char *devinfo;
 	usbd_status err;
-	struct usbd_hub *hub;
+	struct usbd_hub *hub = NULL;
 	usb_device_request_t req;
 	usb_hub_descriptor_t hubdesc;
 	int p, port, nports, nremov, pwrdly;
 	usbd_interface_handle iface;
 	usb_endpoint_descriptor_t *ed;
+	struct usbd_tt *tts = NULL;
 
 	devinfo = malloc(1024, M_TEMP, M_NOWAIT);
 	if (devinfo == NULL) {
@@ -178,6 +182,12 @@ USB_ATTACH(uhub)
 	usbd_devinfo(dev, 1, devinfo);
 	USB_ATTACH_SETUP;
 
+	if (UHUB_IS_HIGH_SPEED(sc)) {
+		printf("%s: %s transaction translator%s\n",
+		    USBDEVNAME(sc->sc_dev),
+		    UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
+		    UHUB_IS_SINGLE_TT(sc) ? "" : "s");
+	}
 	err = usbd_set_config_index(dev, 0, 1);
 	if (err) {
 		DPRINTF(("%s: configuration failed, error=%s\n",
@@ -220,6 +230,11 @@ USB_ATTACH(uhub)
 	       USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "",
 	       nremov, dev->self_powered ? "self" : "bus");
 
+	if (nports == 0) {
+		printf("%s: no ports, hub ignored\n", USBDEVNAME(sc->sc_dev));
+		goto bad;
+	}
+
 	hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
 		     M_USBDEV, M_NOWAIT);
 	if (hub == NULL) {
@@ -299,10 +314,17 @@ USB_ATTACH(uhub)
 	 *        proceed with device attachment
 	 */
 
+	if (UHUB_IS_HIGH_SPEED(sc)) {
+		tts = malloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
+		    sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT);
+		if (!tts)
+			goto bad;
+	}
+
 	/* Set up data structures */
 	for (p = 0; p < nports; p++) {
 		struct usbd_port *up = &hub->ports[p];
-		up->device = 0;
+		up->device = NULL;
 		up->parent = dev;
 		up->portno = p+1;
 		if (dev->self_powered)
@@ -311,6 +333,12 @@ USB_ATTACH(uhub)
 		else
 			up->power = USB_MIN_POWER;
 		up->restartcnt = 0;
+		if (UHUB_IS_HIGH_SPEED(sc)) {
+			up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
+			up->tt->hub = hub;
+		} else {
+			up->tt = NULL;
+		}
 	}
 
 	/* XXX should check for none, individual, or ganged power? */
@@ -336,9 +364,10 @@ USB_ATTACH(uhub)
 	USB_ATTACH_SUCCESS_RETURN;
 
  bad:
-	free(hub, M_USBDEV);
+	if (hub)
+		free(hub, M_USBDEV);
 	free(devinfo, M_TEMP);
-	dev->hub = 0;
+	dev->hub = NULL;
 	USB_ATTACH_ERROR_RETURN;
 }
 
@@ -475,6 +504,15 @@ uhub_explore(usbd_device_handle dev)
 			continue;
 		}
 
+#if 0
+		if (UHUB_IS_HIGH_SPEED(sc) && !(status & UPS_HIGH_SPEED)) {
+			printf("%s: port %d, transaction translation not "
+			    "implemented, low/full speed device ignored\n",
+			    USBDEVNAME(sc->sc_dev), port);
+			continue;
+		}
+#endif
+
 		/* Figure out device speed */
 		if (status & UPS_HIGH_SPEED)
 			speed = USB_SPEED_HIGH;
@@ -574,6 +612,8 @@ USB_DETACH(uhub)
 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub,
 			   USBDEV(sc->sc_dev));
 
+	if (hub->ports[0].tt)
+		free(hub->ports[0].tt, M_USBDEV);
 	free(hub, M_USBDEV);
 	sc->sc_hub->hub = NULL;
 
diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c
index 905a86ca97de..fa21645137a0 100644
--- a/sys/dev/usb/usb_subr.c
+++ b/sys/dev/usb/usb_subr.c
@@ -1046,13 +1046,14 @@ usbd_status
 usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
 		int speed, int port, struct usbd_port *up)
 {
-	usbd_device_handle dev;
+	usbd_device_handle dev, adev;
 	struct usbd_device *hub;
 	usb_device_descriptor_t *dd;
 	usb_port_status_t ps;
 	usbd_status err;
 	int addr;
 	int i;
+	int p;
 
 	DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n",
 		 bus, port, depth, speed));
@@ -1086,11 +1087,27 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
 	dev->depth = depth;
 	dev->powersrc = up;
 	dev->myhub = up->parent;
-	for (hub = up->parent;
+
+	up->device = dev;
+
+	/* Locate port on upstream high speed hub */
+	for (adev = dev, hub = up->parent;
 	     hub != NULL && hub->speed != USB_SPEED_HIGH;
-	     hub = hub->myhub)
+	     adev = hub, hub = hub->myhub)
 		;
-	dev->myhighhub = hub;
+	if (hub) {
+		for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
+			if (hub->hub->ports[p].device == adev) {
+				dev->myhsport = &hub->hub->ports[p];
+				goto found;
+			}
+		}
+		panic("usbd_new_device: cannot find HS port\n");
+	found:
+		DPRINTFN(1,("usbd_new_device: high speed port %d\n", p));
+	} else {
+		dev->myhsport = NULL;
+	}
 	dev->speed = speed;
 	dev->langid = USBD_NOLANG;
 	dev->cookie.cookie = ++usb_cookie_no;
@@ -1103,8 +1120,6 @@ usbd_new_device(device_ptr_t parent, usbd_bus_handle bus, int depth,
 		return (err);
 	}
 
-	up->device = dev;
-
 	/* Set the address.  Do this early; some devices need that. */
 	/* Try a few times in case the device is slow (i.e. outside specs.) */
 	DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
@@ -1228,8 +1243,8 @@ usbd_remove_device(usbd_device_handle dev, struct usbd_port *up)
 
 	if (dev->default_pipe != NULL)
 		usbd_kill_pipe(dev->default_pipe);
-	up->device = 0;
-	dev->bus->devices[dev->address] = 0;
+	up->device = NULL;
+	dev->bus->devices[dev->address] = NULL;
 
 	free(dev, M_USB);
 }
diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h
index dafdc01b5e0f..4be8d648561e 100644
--- a/sys/dev/usb/usbdivar.h
+++ b/sys/dev/usb/usbdivar.h
@@ -73,6 +73,10 @@ struct usbd_pipe_methods {
 	void		      (*done)(usbd_xfer_handle xfer);
 };
 
+struct usbd_tt {
+	struct usbd_hub		*hub;
+};
+
 struct usbd_port {
 	usb_port_status_t	status;
 	u_int16_t		power;	/* mA of current on port */
@@ -81,6 +85,7 @@ struct usbd_port {
 #define USBD_RESTART_MAX 5
 	struct usbd_device     *device;	/* Connected device */
 	struct usbd_device     *parent;	/* The ports hub */
+	struct usbd_tt	       *tt; /* Transaction translator (if any) */
 };
 
 struct usbd_hub {
@@ -141,7 +146,7 @@ struct usbd_device {
 	usb_event_cookie_t	cookie;	       /* unique connection id */
 	struct usbd_port       *powersrc;      /* upstream hub port, or 0 */
 	struct usbd_device     *myhub;	       /* upstream hub */
-	struct usbd_device     *myhighhub;     /* closest high speed hub */
+	struct usbd_port       *myhsport;      /* closest high speed port */
 	struct usbd_endpoint	def_ep;	       /* for pipe 0 */
 	usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */
 	struct usbd_interface  *ifaces;        /* array of all interfaces */