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
This commit is contained in:
iedowse 2004-11-03 01:52:50 +00:00
parent 595a6f02e8
commit cb7e434051
5 changed files with 194 additions and 94 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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 */