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:
parent
595a6f02e8
commit
cb7e434051
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user