summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoriedowse <iedowse@FreeBSD.org>2004-11-03 01:52:50 +0000
committeriedowse <iedowse@FreeBSD.org>2004-11-03 01:52:50 +0000
commitcb7e4340514c332ef2b3ac808a06bb5470be16eb (patch)
treed9d339fddbadd971663de538f170ee9d422eda06
parent595a6f02e81be5272954071e994ea142beab3319 (diff)
downloadFreeBSD-src-cb7e4340514c332ef2b3ac808a06bb5470be16eb.zip
FreeBSD-src-cb7e4340514c332ef2b3ac808a06bb5470be16eb.tar.gz
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
-rw-r--r--sys/dev/usb/ehci.c199
-rw-r--r--sys/dev/usb/ehcireg.h3
-rw-r--r--sys/dev/usb/uhub.c48
-rw-r--r--sys/dev/usb/usb_subr.c31
-rw-r--r--sys/dev/usb/usbdivar.h7
5 files changed, 194 insertions, 94 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c
index fec0d50..c5bb5bf 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 634fc9e..2aaa4c8 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 54affde..5378458 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 905a86c..fa21645 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 dafdc01..4be8d64 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 */
OpenPOWER on IntegriCloud