Bug #1010 » freebsd.usb.diff
/root/src/sys/dev/usb/ehci.c 2008-05-13 20:58:08 +0000 | ||
---|---|---|
/* $NetBSD: ehci.c,v 1.91 2005/02/27 00:27:51 perry Exp $ */
|
||
/* $FreeBSD: src/sys/dev/usb/ehci.c,v 1.36.2.3 2006/09/24 13:39:04 iedowse Exp $ */
|
||
/* $DragonFly: src/sys/bus/usb/ehci.c,v 1.32 2007/06/29 22:56:31 hasso Exp $ */
|
||
/*
|
||
/*-
|
||
* Copyright (c) 2004 The NetBSD Foundation, Inc.
|
||
* All rights reserved.
|
||
*
|
||
... | ... | |
* 3) Command failures are not recovered correctly.
|
||
*/
|
||
#include <sys/cdefs.h>
|
||
__FBSDID("$FreeBSD: src/sys/dev/usb/ehci.c,v 1.63 2008/05/13 20:58:08 marius Exp $");
|
||
#include <sys/param.h>
|
||
#include <sys/systm.h>
|
||
#include <sys/malloc.h>
|
||
... | ... | |
#include <sys/module.h>
|
||
#include <sys/bus.h>
|
||
#include <sys/lock.h>
|
||
#include <sys/lockmgr.h>
|
||
#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__)
|
||
#include <machine/cpu.h>
|
||
#endif
|
||
#include <sys/proc.h>
|
||
#include <sys/queue.h>
|
||
#include <sys/sysctl.h>
|
||
#include <sys/thread2.h>
|
||
#include <machine/cpu.h>
|
||
#include <machine/bus.h>
|
||
#include <machine/endian.h>
|
||
#include <bus/usb/usb.h>
|
||
#include <bus/usb/usbdi.h>
|
||
#include <bus/usb/usbdivar.h>
|
||
#include <bus/usb/usb_mem.h>
|
||
#include <bus/usb/usb_quirks.h>
|
||
#include <dev/usb/usb.h>
|
||
#include <dev/usb/usbdi.h>
|
||
#include <dev/usb/usbdivar.h>
|
||
#include <dev/usb/usb_mem.h>
|
||
#include <dev/usb/usb_quirks.h>
|
||
#include <bus/usb/ehcireg.h>
|
||
#include <bus/usb/ehcivar.h>
|
||
#include <dev/usb/ehcireg.h>
|
||
#include <dev/usb/ehcivar.h>
|
||
#define delay(d) DELAY(d)
|
||
#ifdef USB_DEBUG
|
||
#define EHCI_DEBUG USB_DEBUG
|
||
#define DPRINTF(x) do { if (ehcidebug) kprintf x; } while (0)
|
||
#define DPRINTFN(n,x) do { if (ehcidebug>(n)) kprintf x; } while (0)
|
||
#define DPRINTF(x) do { if (ehcidebug) printf x; } while (0)
|
||
#define DPRINTFN(n,x) do { if (ehcidebug>(n)) printf 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,
|
||
&ehcidebug, 0, "ehci debug level");
|
||
#define bitmask_snprintf(q,f,b,l) ksnprintf((b), (l), "%b", (q), (f))
|
||
#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f))
|
||
#else
|
||
#define DPRINTF(x)
|
||
#define DPRINTFN(n,x)
|
||
... | ... | |
* Table 2-9 in the EHCI spec says this will result
|
||
* in undefined behavior.
|
||
*/
|
||
kprintf("%s: stop timeout\n",
|
||
printf("%s: stop timeout\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
|
||
... | ... | |
if (!hcr)
|
||
return (USBD_NORMAL_COMPLETION);
|
||
}
|
||
kprintf("%s: reset timeout\n", device_get_nameunit(sc->sc_bus.bdev));
|
||
printf("%s: reset timeout\n", device_get_nameunit(sc->sc_bus.bdev));
|
||
return (USBD_IOERROR);
|
||
}
|
||
... | ... | |
sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH);
|
||
version = EREAD2(sc, EHCI_HCIVERSION);
|
||
kprintf("%s: EHCI version %x.%x\n", device_get_nameunit(sc->sc_bus.bdev),
|
||
printf("%s: EHCI version %x.%x\n", device_get_nameunit(sc->sc_bus.bdev),
|
||
version >> 8, version & 0xff);
|
||
sparams = EREAD4(sc, EHCI_HCSPARAMS);
|
||
... | ... | |
sc->sc_npcomp = EHCI_HCS_N_PCC(sparams);
|
||
ncomp = EHCI_HCS_N_CC(sparams);
|
||
if (ncomp != sc->sc_ncomp) {
|
||
kprintf("%s: wrong number of companions (%d != %d)\n",
|
||
printf("%s: wrong number of companions (%d != %d)\n",
|
||
device_get_nameunit(sc->sc_bus.bdev),
|
||
ncomp, sc->sc_ncomp);
|
||
if (ncomp < sc->sc_ncomp)
|
||
sc->sc_ncomp = ncomp;
|
||
}
|
||
if (sc->sc_ncomp > 0) {
|
||
kprintf("%s: companion controller%s, %d port%s each:",
|
||
printf("%s: companion controller%s, %d port%s each:",
|
||
device_get_nameunit(sc->sc_bus.bdev), sc->sc_ncomp!=1 ? "s" : "",
|
||
EHCI_HCS_N_PCC(sparams),
|
||
EHCI_HCS_N_PCC(sparams)!=1 ? "s" : "");
|
||
for (i = 0; i < sc->sc_ncomp; i++)
|
||
kprintf(" %s", device_get_nameunit(sc->sc_comps[i]->bdev));
|
||
kprintf("\n");
|
||
printf(" %s", device_get_nameunit(sc->sc_comps[i]->bdev));
|
||
printf("\n");
|
||
}
|
||
sc->sc_noport = EHCI_HCS_N_PORTS(sparams);
|
||
cparams = EREAD4(sc, EHCI_HCCPARAMS);
|
||
... | ... | |
case 3: return (USBD_IOERROR);
|
||
}
|
||
err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t),
|
||
EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
|
||
EHCI_FLALIGN_ALIGN, &sc->sc_fldma);
|
||
if (err)
|
||
return (err);
|
||
DPRINTF(("%s: flsize=%d\n", device_get_nameunit(sc->sc_bus.bdev),sc->sc_flsize));
|
||
... | ... | |
sc->sc_bus.methods = &ehci_bus_methods;
|
||
sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
|
||
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||
sc->sc_powerhook = powerhook_establish(ehci_power, sc);
|
||
sc->sc_shutdownhook = shutdownhook_establish(ehci_shutdown, sc);
|
||
#endif
|
||
sc->sc_eintrs = EHCI_NORMAL_INTRS;
|
||
/*
|
||
... | ... | |
sc->sc_async_head = sqh;
|
||
EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH);
|
||
callout_init(&sc->sc_tmo_pcd);
|
||
callout_init(&sc->sc_tmo_intrlist);
|
||
callout_init(&sc->sc_tmo_pcd, 0);
|
||
callout_init(&sc->sc_tmo_intrlist, 0);
|
||
lockinit(&sc->sc_doorbell_lock, "ehcidb", 0, 0);
|
||
lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0);
|
||
/* Enable interrupts */
|
||
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
|
||
... | ... | |
break;
|
||
}
|
||
if (hcr) {
|
||
kprintf("%s: run timeout\n", device_get_nameunit(sc->sc_bus.bdev));
|
||
printf("%s: run timeout\n", device_get_nameunit(sc->sc_bus.bdev));
|
||
return (USBD_IOERROR);
|
||
}
|
||
... | ... | |
/* In case the interrupt occurs before initialization has completed. */
|
||
if (sc == NULL) {
|
||
#ifdef DIAGNOSTIC
|
||
kprintf("ehci_intr1: sc == NULL\n");
|
||
printf("ehci_intr1: sc == NULL\n");
|
||
#endif
|
||
return (0);
|
||
}
|
||
... | ... | |
eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT);
|
||
}
|
||
if (eintrs & EHCI_STS_HSE) {
|
||
kprintf("%s: unrecoverable error, controller halted\n",
|
||
printf("%s: unrecoverable error, controller halted\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
/* XXX what else */
|
||
}
|
||
... | ... | |
/* Block unprocessed interrupts. */
|
||
sc->sc_eintrs &= ~eintrs;
|
||
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
|
||
kprintf("%s: blocking intrs 0x%x\n",
|
||
printf("%s: blocking intrs 0x%x\n",
|
||
device_get_nameunit(sc->sc_bus.bdev), eintrs);
|
||
}
|
||
... | ... | |
DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex));
|
||
if (ex->sqtdstart == NULL) {
|
||
kprintf("ehci_check_intr: sqtdstart=NULL\n");
|
||
printf("ehci_check_intr: sqtdstart=NULL\n");
|
||
return;
|
||
}
|
||
lsqtd = ex->sqtdend;
|
||
#ifdef DIAGNOSTIC
|
||
if (lsqtd == NULL) {
|
||
kprintf("ehci_check_intr: lsqtd==0\n");
|
||
printf("ehci_check_intr: lsqtd==0\n");
|
||
return;
|
||
}
|
||
#endif
|
||
... | ... | |
DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex));
|
||
#ifdef DIAGNOSTIC
|
||
{
|
||
crit_enter();
|
||
int s = splhigh();
|
||
if (ex->isdone) {
|
||
crit_exit();
|
||
splx(s);
|
||
#ifdef EHCI_DEBUG
|
||
kprintf("ehci_idone: ex is done!\n ");
|
||
printf("ehci_idone: ex is done!\n ");
|
||
ehci_dump_exfer(ex);
|
||
#else
|
||
kprintf("ehci_idone: ex=%p is done!\n", ex);
|
||
printf("ehci_idone: ex=%p is done!\n", ex);
|
||
#endif
|
||
return;
|
||
}
|
||
ex->isdone = 1;
|
||
crit_exit();
|
||
splx(s);
|
||
}
|
||
#endif
|
||
... | ... | |
callout_stop(&sc->sc_tmo_intrlist);
|
||
callout_stop(&sc->sc_tmo_pcd);
|
||
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||
if (sc->sc_powerhook != NULL)
|
||
powerhook_disestablish(sc->sc_powerhook);
|
||
if (sc->sc_shutdownhook != NULL)
|
||
shutdownhook_disestablish(sc->sc_shutdownhook);
|
||
#endif
|
||
usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
|
||
usb_freemem(&sc->sc_bus, &sc->sc_fldma);
|
||
... | ... | |
{
|
||
ehci_softc_t *sc = v;
|
||
u_int32_t cmd, hcr;
|
||
int i;
|
||
int s, i;
|
||
#ifdef EHCI_DEBUG
|
||
DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
|
||
... | ... | |
ehci_dump_regs(sc);
|
||
#endif
|
||
crit_enter();
|
||
s = splhardusb();
|
||
switch (why) {
|
||
case PWR_SUSPEND:
|
||
case PWR_STANDBY:
|
||
... | ... | |
usb_delay_ms(&sc->sc_bus, 1);
|
||
}
|
||
if (hcr != 0) {
|
||
kprintf("%s: reset timeout\n",
|
||
printf("%s: reset timeout\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
}
|
||
... | ... | |
usb_delay_ms(&sc->sc_bus, 1);
|
||
}
|
||
if (hcr != EHCI_STS_HCH) {
|
||
kprintf("%s: config timeout\n",
|
||
printf("%s: config timeout\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
}
|
||
... | ... | |
usb_delay_ms(&sc->sc_bus, 1);
|
||
}
|
||
if (hcr == EHCI_STS_HCH) {
|
||
kprintf("%s: config timeout\n",
|
||
printf("%s: config timeout\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
}
|
||
... | ... | |
case PWR_SOFTRESUME:
|
||
break;
|
||
}
|
||
crit_exit();
|
||
splx(s);
|
||
#ifdef EHCI_DEBUG
|
||
DPRINTF(("ehci_power: sc=%p\n", sc));
|
||
... | ... | |
err = usb_allocmem(bus, size, 0, dma);
|
||
#ifdef EHCI_DEBUG
|
||
if (err)
|
||
kprintf("ehci_allocm: usb_allocmem()=%d\n", err);
|
||
printf("ehci_allocm: usb_allocmem()=%d\n", err);
|
||
#endif
|
||
return (err);
|
||
}
|
||
... | ... | |
STAILQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
|
||
#ifdef DIAGNOSTIC
|
||
if (xfer->busy_free != XFER_FREE) {
|
||
kprintf("ehci_allocx: xfer=%p not free, 0x%08x\n", xfer,
|
||
printf("ehci_allocx: xfer=%p not free, 0x%08x\n", xfer,
|
||
xfer->busy_free);
|
||
}
|
||
#endif
|
||
} else {
|
||
xfer = kmalloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
|
||
xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
|
||
}
|
||
if (xfer != NULL) {
|
||
memset(xfer, 0, sizeof(struct ehci_xfer));
|
||
... | ... | |
#ifdef DIAGNOSTIC
|
||
if (xfer->busy_free != XFER_BUSY) {
|
||
kprintf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer,
|
||
printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer,
|
||
xfer->busy_free);
|
||
return;
|
||
}
|
||
xfer->busy_free = XFER_FREE;
|
||
if (!EXFER(xfer)->isdone) {
|
||
kprintf("ehci_freex: !isdone\n");
|
||
printf("ehci_freex: !isdone\n");
|
||
return;
|
||
}
|
||
#endif
|
||
... | ... | |
ehci_dump_regs(ehci_softc_t *sc)
|
||
{
|
||
int i;
|
||
kprintf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n",
|
||
printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n",
|
||
EOREAD4(sc, EHCI_USBCMD),
|
||
EOREAD4(sc, EHCI_USBSTS),
|
||
EOREAD4(sc, EHCI_USBINTR));
|
||
kprintf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n",
|
||
printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n",
|
||
EOREAD4(sc, EHCI_FRINDEX),
|
||
EOREAD4(sc, EHCI_CTRLDSSEGMENT),
|
||
EOREAD4(sc, EHCI_PERIODICLISTBASE),
|
||
EOREAD4(sc, EHCI_ASYNCLISTADDR));
|
||
for (i = 1; i <= sc->sc_noport; i++)
|
||
kprintf("port %d status=0x%08x\n", i,
|
||
printf("port %d status=0x%08x\n", i,
|
||
EOREAD4(sc, EHCI_PORTSC(i)));
|
||
}
|
||
... | ... | |
ehci_dump_link(ehci_link_t link, int type)
|
||
{
|
||
link = le32toh(link);
|
||
kprintf("0x%08x", link);
|
||
printf("0x%08x", link);
|
||
if (link & EHCI_LINK_TERMINATE)
|
||
kprintf("<T>");
|
||
printf("<T>");
|
||
else {
|
||
kprintf("<");
|
||
printf("<");
|
||
if (type) {
|
||
switch (EHCI_LINK_TYPE(link)) {
|
||
case EHCI_LINK_ITD: kprintf("ITD"); break;
|
||
case EHCI_LINK_QH: kprintf("QH"); break;
|
||
case EHCI_LINK_SITD: kprintf("SITD"); break;
|
||
case EHCI_LINK_FSTN: kprintf("FSTN"); break;
|
||
case EHCI_LINK_ITD: printf("ITD"); break;
|
||
case EHCI_LINK_QH: printf("QH"); break;
|
||
case EHCI_LINK_SITD: printf("SITD"); break;
|
||
case EHCI_LINK_FSTN: printf("FSTN"); break;
|
||
}
|
||
}
|
||
kprintf(">");
|
||
printf(">");
|
||
}
|
||
}
|
||
... | ... | |
stop = sqtd->qtd.qtd_next & htole32(EHCI_LINK_TERMINATE);
|
||
}
|
||
if (sqtd)
|
||
kprintf("dump aborted, too many TDs\n");
|
||
printf("dump aborted, too many TDs\n");
|
||
}
|
||
void
|
||
ehci_dump_sqtd(ehci_soft_qtd_t *sqtd)
|
||
{
|
||
kprintf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr);
|
||
printf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr);
|
||
ehci_dump_qtd(&sqtd->qtd);
|
||
}
|
||
... | ... | |
u_int32_t s;
|
||
char sbuf[128];
|
||
kprintf(" next="); ehci_dump_link(qtd->qtd_next, 0);
|
||
kprintf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0);
|
||
kprintf("\n");
|
||
printf(" next="); ehci_dump_link(qtd->qtd_next, 0);
|
||
printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0);
|
||
printf("\n");
|
||
s = le32toh(qtd->qtd_status);
|
||
bitmask_snprintf(EHCI_QTD_GET_STATUS(s),
|
||
"\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR"
|
||
"\3MISSED\2SPLIT\1PING", sbuf, sizeof(sbuf));
|
||
kprintf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n",
|
||
printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n",
|
||
s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s),
|
||
EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s));
|
||
kprintf(" cerr=%d pid=%d stat=0x%s\n", EHCI_QTD_GET_CERR(s),
|
||
printf(" cerr=%d pid=%d stat=0x%s\n", EHCI_QTD_GET_CERR(s),
|
||
EHCI_QTD_GET_PID(s), sbuf);
|
||
for (s = 0; s < 5; s++)
|
||
kprintf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s]));
|
||
printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s]));
|
||
}
|
||
void
|
||
... | ... | |
ehci_qh_t *qh = &sqh->qh;
|
||
u_int32_t endp, endphub;
|
||
kprintf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr);
|
||
kprintf(" sqtd=%p inactivesqtd=%p\n", sqh->sqtd, sqh->inactivesqtd);
|
||
kprintf(" link="); ehci_dump_link(qh->qh_link, 1); kprintf("\n");
|
||
printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr);
|
||
printf(" sqtd=%p inactivesqtd=%p\n", sqh->sqtd, sqh->inactivesqtd);
|
||
printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n");
|
||
endp = le32toh(qh->qh_endp);
|
||
kprintf(" endp=0x%08x\n", endp);
|
||
kprintf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n",
|
||
printf(" endp=0x%08x\n", endp);
|
||
printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n",
|
||
EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp),
|
||
EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp),
|
||
EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp));
|
||
kprintf(" mpl=0x%x ctl=%d nrl=%d\n",
|
||
printf(" mpl=0x%x ctl=%d nrl=%d\n",
|
||
EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp),
|
||
EHCI_QH_GET_NRL(endp));
|
||
endphub = le32toh(qh->qh_endphub);
|
||
kprintf(" endphub=0x%08x\n", endphub);
|
||
kprintf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n",
|
||
printf(" endphub=0x%08x\n", endphub);
|
||
printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n",
|
||
EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub),
|
||
EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub),
|
||
EHCI_QH_GET_MULT(endphub));
|
||
kprintf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); kprintf("\n");
|
||
kprintf("Overlay qTD:\n");
|
||
printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n");
|
||
printf("Overlay qTD:\n");
|
||
ehci_dump_qtd(&qh->qh_qtd);
|
||
}
|
||
... | ... | |
static void
|
||
ehci_dump_exfer(struct ehci_xfer *ex)
|
||
{
|
||
kprintf("ehci_dump_exfer: ex=%p\n", ex);
|
||
printf("ehci_dump_exfer: ex=%p\n", ex);
|
||
}
|
||
#endif
|
||
#endif
|
||
... | ... | |
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
|
||
ehci_soft_qh_t *sqh;
|
||
usbd_status err;
|
||
int s;
|
||
int ival, speed, naks;
|
||
int hshubaddr, hshubport;
|
||
... | ... | |
default: panic("ehci_open: bad device speed %d", dev->speed);
|
||
}
|
||
if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
|
||
kprintf("%s: *** WARNING: opening low/full speed device, this "
|
||
printf("%s: *** WARNING: opening low/full speed device, this "
|
||
"does not work yet.\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
|
||
... | ... | |
0, &epipe->u.ctl.reqdma);
|
||
#ifdef EHCI_DEBUG
|
||
if (err)
|
||
kprintf("ehci_open: usb_allocmem()=%d\n", err);
|
||
printf("ehci_open: usb_allocmem()=%d\n", err);
|
||
#endif
|
||
if (err)
|
||
goto bad1;
|
||
pipe->methods = &ehci_device_ctrl_methods;
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_add_qh(sqh, sc->sc_async_head);
|
||
crit_exit();
|
||
splx(s);
|
||
break;
|
||
case UE_BULK:
|
||
pipe->methods = &ehci_device_bulk_methods;
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_add_qh(sqh, sc->sc_async_head);
|
||
crit_exit();
|
||
splx(s);
|
||
break;
|
||
case UE_INTERRUPT:
|
||
pipe->methods = &ehci_device_intr_methods;
|
||
... | ... | |
}
|
||
/*
|
||
* Add an ED to the schedule. Called while in a critical section.
|
||
* Add an ED to the schedule. Called at splusb().
|
||
* If in the async schedule, it will always have a next.
|
||
* If in the intr schedule it may not.
|
||
*/
|
||
void
|
||
ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
|
||
{
|
||
SPLUSBCHECK;
|
||
sqh->next = head->next;
|
||
sqh->prev = head;
|
||
sqh->qh.qh_link = head->qh.qh_link;
|
||
... | ... | |
#ifdef EHCI_DEBUG
|
||
if (ehcidebug > 5) {
|
||
kprintf("ehci_add_qh:\n");
|
||
printf("ehci_add_qh:\n");
|
||
ehci_dump_sqh(sqh);
|
||
}
|
||
#endif
|
||
}
|
||
/*
|
||
* Remove an ED from the schedule. Called while in a critical section.
|
||
* Remove an ED from the schedule. Called at splusb().
|
||
* Will always have a 'next' if it's in the async list as it's circular.
|
||
*/
|
||
void
|
||
ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
|
||
{
|
||
SPLUSBCHECK;
|
||
/* XXX */
|
||
sqh->prev->qh.qh_link = sqh->qh.qh_link;
|
||
sqh->prev->next = sqh->next;
|
||
... | ... | |
if (EHCI_LINK_ADDR(le32toh(sqh->qh.qh_qtd.qtd_next)) !=
|
||
sqtd->physaddr) {
|
||
#ifdef EHCI_DEBUG
|
||
kprintf("ehci_activate_qh: unexpected next ptr\n");
|
||
printf("ehci_activate_qh: unexpected next ptr\n");
|
||
ehci_dump_sqh(sqh);
|
||
ehci_dump_sqtds(sqh->sqtd);
|
||
#endif
|
||
... | ... | |
void
|
||
ehci_sync_hc(ehci_softc_t *sc)
|
||
{
|
||
int error;
|
||
int s, error;
|
||
if (sc->sc_dying) {
|
||
DPRINTFN(2,("ehci_sync_hc: dying\n"));
|
||
... | ... | |
}
|
||
DPRINTFN(2,("ehci_sync_hc: enter\n"));
|
||
/* get doorbell */
|
||
lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE);
|
||
crit_enter();
|
||
lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL);
|
||
s = splhardusb();
|
||
/* ask for doorbell */
|
||
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
|
||
DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n",
|
||
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS)));
|
||
error = tsleep(&sc->sc_async_head, 0, "ehcidi", hz); /* bell wait */
|
||
error = tsleep(&sc->sc_async_head, PZERO, "ehcidi", hz); /* bell wait */
|
||
DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n",
|
||
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS)));
|
||
crit_exit();
|
||
splx(s);
|
||
/* release doorbell */
|
||
lockmgr(&sc->sc_doorbell_lock, LK_RELEASE);
|
||
lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL);
|
||
#ifdef DIAGNOSTIC
|
||
if (error)
|
||
kprintf("ehci_sync_hc: tsleep() = %d\n", error);
|
||
printf("ehci_sync_hc: tsleep() = %d\n", error);
|
||
#endif
|
||
DPRINTFN(2,("ehci_sync_hc: exit\n"));
|
||
}
|
||
... | ... | |
usb_device_request_t *req;
|
||
void *buf = NULL;
|
||
int port, i;
|
||
int len, value, index, l, totlen = 0;
|
||
int s, len, value, index, l, totlen = 0;
|
||
usb_port_status_t ps;
|
||
usb_hub_descriptor_t hubd;
|
||
usbd_status err;
|
||
... | ... | |
goto ret;
|
||
}
|
||
v = EOREAD4(sc, EHCI_PORTSC(index));
|
||
DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n", v));
|
||
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;
|
||
if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED;
|
||
... | ... | |
v = EOREAD4(sc, port);
|
||
DPRINTF(("ehci after reset, status=0x%08x\n", v));
|
||
if (v & EHCI_PS_PR) {
|
||
kprintf("%s: port reset timeout\n",
|
||
printf("%s: port reset timeout\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
return (USBD_TIMEOUT);
|
||
}
|
||
... | ... | |
err = USBD_NORMAL_COMPLETION;
|
||
ret:
|
||
xfer->status = err;
|
||
crit_enter();
|
||
s = splusb();
|
||
hacksync(xfer); /* XXX to compensate for usb_transfer_complete */
|
||
usb_transfer_complete(xfer);
|
||
crit_exit();
|
||
splx(s);
|
||
return (USBD_IN_PROGRESS);
|
||
}
|
||
... | ... | |
if (sc->sc_npcomp != 0) {
|
||
int i = (index-1) / sc->sc_npcomp;
|
||
if (i >= sc->sc_ncomp)
|
||
kprintf("%s: strange port\n",
|
||
printf("%s: strange port\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
else
|
||
kprintf("%s: handing over %s speed device on "
|
||
printf("%s: handing over %s speed device on "
|
||
"port %d to %s\n",
|
||
device_get_nameunit(sc->sc_bus.bdev),
|
||
lowspeed ? "low" : "full",
|
||
index, device_get_nameunit(sc->sc_comps[i]->bdev));
|
||
} else {
|
||
kprintf("%s: npcomp == 0\n", device_get_nameunit(sc->sc_bus.bdev));
|
||
printf("%s: npcomp == 0\n", device_get_nameunit(sc->sc_bus.bdev));
|
||
}
|
||
#endif
|
||
port = EHCI_PORTSC(index);
|
||
... | ... | |
static void
|
||
ehci_root_intr_abort(usbd_xfer_handle xfer)
|
||
{
|
||
int s;
|
||
if (xfer->pipe->intrxfer == xfer) {
|
||
DPRINTF(("ehci_root_intr_abort: remove\n"));
|
||
xfer->pipe->intrxfer = NULL;
|
||
}
|
||
xfer->status = USBD_CANCELLED;
|
||
crit_enter();
|
||
s = splusb();
|
||
usb_transfer_complete(xfer);
|
||
crit_exit();
|
||
splx(s);
|
||
}
|
||
/* Close the root pipe. */
|
||
... | ... | |
EHCI_PAGE_SIZE, &dma);
|
||
#ifdef EHCI_DEBUG
|
||
if (err)
|
||
kprintf("ehci_alloc_sqh: usb_allocmem()=%d\n", err);
|
||
printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err);
|
||
#endif
|
||
if (err)
|
||
return (NULL);
|
||
... | ... | |
usbd_status err;
|
||
int i, offs;
|
||
usb_dma_t dma;
|
||
int s;
|
||
if (sc->sc_freeqtds == NULL) {
|
||
DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n"));
|
||
... | ... | |
EHCI_PAGE_SIZE, &dma);
|
||
#ifdef EHCI_DEBUG
|
||
if (err)
|
||
kprintf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err);
|
||
printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err);
|
||
#endif
|
||
if (err)
|
||
return (NULL);
|
||
crit_enter();
|
||
s = splusb();
|
||
for(i = 0; i < EHCI_SQTD_CHUNK; i++) {
|
||
offs = i * EHCI_SQTD_SIZE;
|
||
sqtd = KERNADDR(&dma, offs);
|
||
... | ... | |
sqtd->nextqtd = sc->sc_freeqtds;
|
||
sc->sc_freeqtds = sqtd;
|
||
}
|
||
crit_exit();
|
||
splx(s);
|
||
}
|
||
crit_enter();
|
||
s = splusb();
|
||
sqtd = sc->sc_freeqtds;
|
||
sc->sc_freeqtds = sqtd->nextqtd;
|
||
sqtd->qtd.qtd_next = EHCI_NULL;
|
||
... | ... | |
}
|
||
sqtd->nextqtd = NULL;
|
||
sqtd->xfer = NULL;
|
||
crit_exit();
|
||
splx(s);
|
||
return (sqtd);
|
||
}
|
||
... | ... | |
void
|
||
ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
|
||
{
|
||
crit_enter();
|
||
int s;
|
||
s = splusb();
|
||
sqtd->nextqtd = sc->sc_freeqtds;
|
||
sc->sc_freeqtds = sqtd;
|
||
crit_exit();
|
||
splx(s);
|
||
}
|
||
usbd_status
|
||
... | ... | |
/*
|
||
* Must stop if there is any gap before or after
|
||
* the page boundary.
|
||
*/
|
||
*/
|
||
if (EHCI_PAGE_OFFSET(dataphys + pagelen) != 0)
|
||
break;
|
||
if (seg < dma->nsegs && EHCI_PAGE_OFFSET(segoff +
|
||
... | ... | |
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
|
||
ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus;
|
||
ehci_soft_qh_t *sqh = epipe->sqh;
|
||
int s;
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_rem_qh(sc, sqh, head);
|
||
crit_exit();
|
||
splx(s);
|
||
pipe->endpoint->savedtoggle =
|
||
EHCI_QTD_GET_TOGGLE(le32toh(sqh->qh.qh_qtd.qtd_status));
|
||
ehci_free_sqh(sc, epipe->sqh);
|
||
... | ... | |
/*
|
||
* Abort a device request.
|
||
* If this routine is called from a critical section it guarantees that the
|
||
* request will be removed from the hardware scheduling and that the callback
|
||
* If this routine is called at splusb() it guarantees that the request
|
||
* will be removed from the hardware scheduling and that the callback
|
||
* for it will be called with USBD_CANCELLED status.
|
||
* It's impossible to guarantee that the requested transfer will not
|
||
* have happened since the hardware runs concurrently.
|
||
... | ... | |
ehci_soft_qh_t *sqh = epipe->sqh;
|
||
ehci_soft_qtd_t *sqtd, *snext;
|
||
ehci_physaddr_t cur, us, next;
|
||
int s;
|
||
int hit, i;
|
||
/* int count = 0; */
|
||
ehci_soft_qh_t *psqh;
|
||
... | ... | |
if (sc->sc_dying) {
|
||
/* If we're dying, just do the software part. */
|
||
crit_enter();
|
||
s = splusb();
|
||
xfer->status = status; /* make software ignore it */
|
||
callout_stop(&xfer->timeout_handle);
|
||
usb_rem_task(epipe->pipe.device, &exfer->abort_task);
|
||
usb_transfer_complete(xfer);
|
||
crit_exit();
|
||
splx(s);
|
||
return;
|
||
}
|
||
if (xfer->device->bus->intr_context /* || !curproc REMOVED DFly */)
|
||
if (xfer->device->bus->intr_context || !curproc)
|
||
panic("ehci_abort_xfer: not in process context");
|
||
/*
|
||
... | ... | |
DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
|
||
exfer->ehci_xfer_flags |= EHCI_XFER_ABORTWAIT;
|
||
while (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING)
|
||
tsleep(&exfer->ehci_xfer_flags, 0, "ehciaw", 0);
|
||
tsleep(&exfer->ehci_xfer_flags, PZERO, "ehciaw", 0);
|
||
return;
|
||
}
|
||
/*
|
||
* Step 1: Make interrupt routine and timeouts ignore xfer.
|
||
*/
|
||
crit_enter();
|
||
s = splusb();
|
||
exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
|
||
xfer->status = status; /* make software ignore it */
|
||
callout_stop(&xfer->timeout_handle);
|
||
usb_rem_task(epipe->pipe.device, &exfer->abort_task);
|
||
crit_exit();
|
||
splx(s);
|
||
/*
|
||
* Step 2: Wait until we know hardware has finished any possible
|
||
... | ... | |
* The hardware has no reference to completed items (TDs).
|
||
* It's safe to remove them at any time.
|
||
*/
|
||
crit_enter();
|
||
s = splusb();
|
||
#ifdef USB_USE_SOFTINTR
|
||
sc->sc_softwake = 1;
|
||
#endif /* USB_USE_SOFTINTR */
|
||
usb_schedsoftintr(&sc->sc_bus);
|
||
#ifdef USB_USE_SOFTINTR
|
||
tsleep(&sc->sc_softwake, 0, "ehciab", 0);
|
||
tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
|
||
#endif /* USB_USE_SOFTINTR */
|
||
/*
|
||
... | ... | |
}
|
||
usb_transfer_complete(xfer);
|
||
/* kprintf("%s: %d TDs aborted\n", __func__, count); */
|
||
crit_exit();
|
||
/* printf("%s: %d TDs aborted\n", __func__, count); */
|
||
splx(s);
|
||
#undef exfer
|
||
}
|
||
... | ... | |
/* Execute the abort in a process context. */
|
||
usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task,
|
||
USB_TASKQ_HC);
|
||
USB_TASKQ_HC);
|
||
}
|
||
void
|
||
ehci_timeout_task(void *addr)
|
||
{
|
||
usbd_xfer_handle xfer = addr;
|
||
int s;
|
||
DPRINTF(("ehci_timeout_task: xfer=%p\n", xfer));
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_abort_xfer(xfer, USBD_TIMEOUT);
|
||
crit_exit();
|
||
splx(s);
|
||
}
|
||
/*
|
||
... | ... | |
ehci_intrlist_timeout(void *arg)
|
||
{
|
||
ehci_softc_t *sc = arg;
|
||
int s = splusb();
|
||
DPRINTFN(3, ("ehci_intrlist_timeout\n"));
|
||
usb_schedsoftintr(&sc->sc_bus);
|
||
splx(s);
|
||
}
|
||
/************************/
|
||
... | ... | |
#ifdef DIAGNOSTIC
|
||
if (!(xfer->rqflags & URQ_REQUEST)) {
|
||
/* XXX panic */
|
||
kprintf("ehci_device_ctrl_transfer: not a request\n");
|
||
printf("ehci_device_ctrl_transfer: not a request\n");
|
||
return (USBD_INVAL);
|
||
}
|
||
#endif
|
||
... | ... | |
int isread;
|
||
int len;
|
||
usbd_status err;
|
||
int s;
|
||
isread = req->bmRequestType & UT_READ;
|
||
len = UGETW(req->wLength);
|
||
... | ... | |
exfer->sqtdend = stat;
|
||
#ifdef DIAGNOSTIC
|
||
if (!exfer->isdone) {
|
||
kprintf("ehci_device_request: not done, exfer=%p\n", exfer);
|
||
printf("ehci_device_request: not done, exfer=%p\n", exfer);
|
||
}
|
||
exfer->isdone = 0;
|
||
#endif
|
||
/* Activate the new qTD in the QH list. */
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_activate_qh(sqh, setup);
|
||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||
callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
|
||
ehci_timeout, xfer);
|
||
ehci_timeout, xfer);
|
||
}
|
||
ehci_add_intr_list(sc, exfer);
|
||
xfer->status = USBD_IN_PROGRESS;
|
||
crit_exit();
|
||
splx(s);
|
||
#ifdef EHCI_DEBUG
|
||
if (ehcidebug > 10) {
|
||
... | ... | |
ehci_soft_qh_t *sqh;
|
||
usbd_status err;
|
||
int len, isread, endpt;
|
||
int s;
|
||
DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n",
|
||
xfer, xfer->length, xfer->flags));
|
||
... | ... | |
exfer->sqtdend = dataend;
|
||
#ifdef DIAGNOSTIC
|
||
if (!exfer->isdone) {
|
||
kprintf("ehci_device_bulk_start: not done, ex=%p\n", exfer);
|
||
printf("ehci_device_bulk_start: not done, ex=%p\n", exfer);
|
||
}
|
||
exfer->isdone = 0;
|
||
#endif
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_activate_qh(sqh, data);
|
||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||
callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
|
||
ehci_timeout, xfer);
|
||
ehci_timeout, xfer);
|
||
}
|
||
ehci_add_intr_list(sc, exfer);
|
||
xfer->status = USBD_IN_PROGRESS;
|
||
crit_exit();
|
||
splx(s);
|
||
#ifdef EHCI_DEBUG
|
||
if (ehcidebug > 10) {
|
||
... | ... | |
DPRINTF(("ehci_device_bulk_start: data(3)\n"));
|
||
ehci_dump_regs(sc);
|
||
#if 0
|
||
kprintf("async_head:\n");
|
||
printf("async_head:\n");
|
||
ehci_dump_sqh(sc->sc_async_head);
|
||
#endif
|
||
kprintf("sqh:\n");
|
||
printf("sqh:\n");
|
||
ehci_dump_sqh(sqh);
|
||
ehci_dump_sqtds(data);
|
||
}
|
||
... | ... | |
/* Pick an interrupt slot at the right level. */
|
||
/* XXX could do better than picking at random. */
|
||
islot = EHCI_IQHIDX(lev, karc4random());
|
||
islot = EHCI_IQHIDX(lev, arc4random());
|
||
sqh->islot = islot;
|
||
isp = &sc->sc_islots[islot];
|
||
... | ... | |
ehci_soft_qh_t *sqh;
|
||
usbd_status err;
|
||
int len, isread, endpt;
|
||
int s;
|
||
DPRINTFN(2, ("ehci_device_intr_start: xfer=%p len=%d flags=%d\n",
|
||
xfer, xfer->length, xfer->flags));
|
||
... | ... | |
exfer->sqtdend = dataend;
|
||
#ifdef DIAGNOSTIC
|
||
if (!exfer->isdone) {
|
||
kprintf("ehci_device_intr_start: not done, ex=%p\n", exfer);
|
||
printf("ehci_device_intr_start: not done, ex=%p\n", exfer);
|
||
}
|
||
exfer->isdone = 0;
|
||
#endif
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_activate_qh(sqh, data);
|
||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||
callout_reset(&xfer->timeout_handle, MS_TO_TICKS(xfer->timeout),
|
||
... | ... | |
}
|
||
ehci_add_intr_list(sc, exfer);
|
||
xfer->status = USBD_IN_PROGRESS;
|
||
crit_exit();
|
||
splx(s);
|
||
#ifdef EHCI_DEBUG
|
||
if (ehcidebug > 10) {
|
||
... | ... | |
delay(10000);
|
||
DPRINTF(("ehci_device_intr_start: data(3)\n"));
|
||
ehci_dump_regs(sc);
|
||
kprintf("sqh:\n");
|
||
printf("sqh:\n");
|
||
ehci_dump_sqh(sqh);
|
||
ehci_dump_sqtds(data);
|
||
}
|
||
... | ... | |
ehci_soft_qtd_t *data, *dataend, *newinactive;
|
||
ehci_soft_qh_t *sqh;
|
||
usbd_status err;
|
||
int len, isread, endpt;
|
||
int len, isread, endpt, s;
|
||
DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n",
|
||
xfer, xfer->actlen));
|
||
... | ... | |
if (xfer->pipe->repeat) {
|
||
ehci_free_sqtd_chain(sc, sqh, ex->sqtdstart,
|
||
ex->sqtdend->nextqtd);
|
||
|
||
len = epipe->u.intr.length;
|
||
xfer->length = len;
|
||
endpt = epipe->pipe.endpoint->edesc->bEndpointAddress;
|
||
... | ... | |
exfer->sqtdend = dataend;
|
||
#ifdef DIAGNOSTIC
|
||
if (!exfer->isdone) {
|
||
kprintf("ehci_device_intr_done: not done, ex=%p\n",
|
||
printf("ehci_device_intr_done: not done, ex=%p\n",
|
||
exfer);
|
||
}
|
||
exfer->isdone = 0;
|
||
#endif
|
||
crit_enter();
|
||
s = splusb();
|
||
ehci_activate_qh(sqh, data);
|
||
if (xfer->timeout && !sc->sc_bus.use_polling) {
|
||
callout_reset(&xfer->timeout_handle,
|
||
MS_TO_TICKS(xfer->timeout), ehci_timeout, xfer);
|
||
}
|
||
crit_exit();
|
||
splx(s);
|
||
xfer->status = USBD_IN_PROGRESS;
|
||
} else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) {
|
||
-- ehci_pci.c 2008-05-19 10:32:14 +0000
|
||
++ /root/src/sys/dev/usb/ehci_pci.c 2008-04-11 05:50:53 +0000
|
||
... | ... | |
/*
|
||
... | ... | |
/*-
|
||
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
||
* All rights reserved.
|
||
*
|
||
... | ... | |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
* POSSIBILITY OF SUCH DAMAGE.
|
||
*
|
||
* $FreeBSD: src/sys/dev/usb/ehci_pci.c,v 1.18.2.1 2006/01/26 01:43:13 iedowse Exp $
|
||
* $DragonFly: src/sys/bus/usb/ehci_pci.c,v 1.18 2007/08/14 20:06:13 dillon Exp $
|
||
*/
|
||
#include <sys/cdefs.h>
|
||
__FBSDID("$FreeBSD: src/sys/dev/usb/ehci_pci.c,v 1.32 2008/04/11 05:50:53 benno Exp $");
|
||
/*
|
||
* USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
|
||
*
|
||
... | ... | |
#include <sys/systm.h>
|
||
#include <sys/kernel.h>
|
||
#include <sys/module.h>
|
||
#include <sys/lock.h>
|
||
#include <sys/mutex.h>
|
||
#include <sys/bus.h>
|
||
#include <sys/queue.h>
|
||
#include <sys/lock.h>
|
||
#include <sys/lockmgr.h>
|
||
#include <machine/bus.h>
|
||
#include <sys/rman.h>
|
||
#include <machine/resource.h>
|
||
#include <bus/pci/pcivar.h>
|
||
#include <bus/pci/pcireg.h>
|
||
#include <dev/pci/pcivar.h>
|
||
#include <dev/pci/pcireg.h>
|
||
#include <bus/usb/usb.h>
|
||
#include <bus/usb/usbdi.h>
|
||
#include <bus/usb/usbdivar.h>
|
||
#include <bus/usb/usb_mem.h>
|
||
#include <dev/usb/usb.h>
|
||
#include <dev/usb/usbdi.h>
|
||
#include <dev/usb/usbdivar.h>
|
||
#include <dev/usb/usb_mem.h>
|
||
#include <bus/usb/ehcireg.h>
|
||
#include <bus/usb/ehcivar.h>
|
||
#include <dev/usb/ehcireg.h>
|
||
#include <dev/usb/ehcivar.h>
|
||
#define PCI_EHCI_VENDORID_ACERLABS 0x10b9
|
||
#define PCI_EHCI_VENDORID_AMD 0x1022
|
||
... | ... | |
/* AMD */
|
||
#define PCI_EHCI_DEVICEID_8111 0x10227463
|
||
#define PCI_EHCI_DEVICEID_CS5536 0x20951022
|
||
static const char *ehci_device_8111 = "AMD 8111 USB 2.0 controller";
|
||
static const char *ehci_device_CS5536 = "AMD CS5536 USB 2.0 controller";
|
||
/* ATI */
|
||
#define PCI_EHCI_DEVICEID_SB200 0x43451002
|
||
... | ... | |
#define PCI_EHCI_DEVICEID_ISP156X 0x15621131
|
||
static const char *ehci_device_isp156x = "Philips ISP156x USB 2.0 controller";
|
||
/* VIA */
|
||
#define PCI_EHCI_DEVICEID_VIA 0x31041106
|
||
static const char *ehci_device_via = "VIA VT6202 USB 2.0 controller";
|
||
/* Generic */
|
||
static const char *ehci_device_generic = "EHCI (generic) USB 2.0 controller";
|
||
#define PCI_EHCI_BASE_REG 0x10
|
||
#ifdef USB_DEBUG
|
||
#define EHCI_DEBUG USB_DEBUG
|
||
#define DPRINTF(x) do { if (ehcidebug) kprintf x; } while (0)
|
||
#define DPRINTF(x) do { if (ehcidebug) printf x; } while (0)
|
||
extern int ehcidebug;
|
||
#else
|
||
#define DPRINTF(x)
|
||
... | ... | |
return (ehci_device_m5239);
|
||
case PCI_EHCI_DEVICEID_8111:
|
||
return (ehci_device_8111);
|
||
case PCI_EHCI_DEVICEID_CS5536:
|
||
return (ehci_device_CS5536);
|
||
case PCI_EHCI_DEVICEID_SB200:
|
||
return (ehci_device_sb200);
|
||
case PCI_EHCI_DEVICEID_SB400:
|
||
... | ... | |
device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
|
||
switch (pci_get_vendor(self)) {
|
||
case PCI_EHCI_VENDORID_ACERLABS:
|
||
ksprintf(sc->sc_vendor, "AcerLabs");
|
||
sprintf(sc->sc_vendor, "AcerLabs");
|
||
break;
|
||
case PCI_EHCI_VENDORID_AMD:
|
||
ksprintf(sc->sc_vendor, "AMD");
|
||
sprintf(sc->sc_vendor, "AMD");
|
||
break;
|
||
case PCI_EHCI_VENDORID_APPLE:
|
||
ksprintf(sc->sc_vendor, "Apple");
|
||
sprintf(sc->sc_vendor, "Apple");
|
||
break;
|
||
case PCI_EHCI_VENDORID_ATI:
|
||
ksprintf(sc->sc_vendor, "ATI");
|
||
sprintf(sc->sc_vendor, "ATI");
|
||
break;
|
||
case PCI_EHCI_VENDORID_CMDTECH:
|
||
ksprintf(sc->sc_vendor, "CMDTECH");
|
||
sprintf(sc->sc_vendor, "CMDTECH");
|
||
break;
|
||
case PCI_EHCI_VENDORID_INTEL:
|
||
ksprintf(sc->sc_vendor, "Intel");
|
||
sprintf(sc->sc_vendor, "Intel");
|
||
break;
|
||
case PCI_EHCI_VENDORID_NEC:
|
||
ksprintf(sc->sc_vendor, "NEC");
|
||
sprintf(sc->sc_vendor, "NEC");
|
||
break;
|
||
case PCI_EHCI_VENDORID_OPTI:
|
||
ksprintf(sc->sc_vendor, "OPTi");
|
||
sprintf(sc->sc_vendor, "OPTi");
|
||
break;
|
||
case PCI_EHCI_VENDORID_SIS:
|
||
ksprintf(sc->sc_vendor, "SiS");
|
||
sprintf(sc->sc_vendor, "SiS");
|
||
break;
|
||
case PCI_EHCI_VENDORID_NVIDIA:
|
||
case PCI_EHCI_VENDORID_NVIDIA2:
|
||
ksprintf(sc->sc_vendor, "nVidia");
|
||
sprintf(sc->sc_vendor, "nVidia");
|
||
break;
|
||
case PCI_EHCI_VENDORID_VIA:
|
||
ksprintf(sc->sc_vendor, "VIA");
|
||
sprintf(sc->sc_vendor, "VIA");
|
||
break;
|
||
default:
|
||
if (bootverbose)
|
||
device_printf(self, "(New EHCI DeviceId=0x%08x)\n",
|
||
pci_get_devid(self));
|
||
ksprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
|
||
sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
|
||
}
|
||
err = bus_setup_intr(self, sc->irq_res, 0,
|
||
(driver_intr_t *) ehci_intr, sc, &sc->ih, NULL);
|
||
err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO,
|
||
NULL, (driver_intr_t *)ehci_intr, sc, &sc->ih);
|
||
if (err) {
|
||
device_printf(self, "Could not setup irq, %d\n", err);
|
||
sc->ih = NULL;
|
||
... | ... | |
if (res != 0)
|
||
continue;
|
||
if (buscount != 1) {
|
||
kfree(nbus, M_TEMP);
|
||
free(nbus, M_TEMP);
|
||
continue;
|
||
}
|
||
if (device_get_devclass(nbus[0]) != dc) {
|
||
kfree(nbus, M_TEMP);
|
||
free(nbus, M_TEMP);
|
||
continue;
|
||
}
|
||
bsc = device_get_softc(nbus[0]);
|
||
kfree(nbus, M_TEMP);
|
||
free(nbus, M_TEMP);
|
||
DPRINTF(("ehci_pci_attach: companion %s\n",
|
||
device_get_nameunit(bsc->bdev)));
|
||
sc->sc_comps[ncomp++] = bsc;
|
||
... | ... | |
sc->sc_ncomp = ncomp;
|
||
/* Allocate a parent dma tag for DMA maps */
|
||
err = bus_dma_tag_create(/*XXX: bus_get_dma_tag(self)*/NULL, 1, 0,
|
||
err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0,
|
||
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
||
BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
|
||
&sc->sc_bus.parent_dmatag);
|
||
NULL, NULL, &sc->sc_bus.parent_dmatag);
|
||
if (err) {
|
||
device_printf(self, "Could not allocate parent DMA tag (%d)\n",
|
||
err);
|
||
... | ... | |
err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0,
|
||
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
||
BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0,
|
||
&sc->sc_bus.buffer_dmatag);
|
||
busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag);
|
||
if (err) {
|
||
device_printf(self, "Could not allocate buffer DMA tag (%d)\n",
|
||
err);
|
||
... | ... | |
bus_dma_tag_destroy(sc->sc_bus.parent_dmatag);
|
||
if (sc->sc_bus.buffer_dmatag != NULL)
|
||
bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag);
|
||
|
||
if (sc->irq_res && sc->ih) {
|
||
int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
|
||
... | ... | |
/* Synchronise with the BIOS if it owns the controller. */
|
||
for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
|
||
eecp = EHCI_EECP_NEXT(eec)) {
|
||
eecp = EHCI_EECP_NEXT(eec)) {
|
||
eec = pci_read_config(self, eecp, 4);
|
||
if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
|
||
continue;
|
||
... | ... | |
if (legsup & EHCI_LEGSUP_BIOSOWNED) {
|
||
pci_write_config(self, eecp,
|
||
legsup | EHCI_LEGSUP_OSOWNED, 4);
|
||
kprintf("%s: waiting for BIOS to give up control\n",
|
||
printf("%s: waiting for BIOS to give up control\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
for (i = 0; i < 5000; i++) {
|
||
legsup = pci_read_config(self, eecp, 4);
|
||
... | ... | |
DELAY(1000);
|
||
}
|
||
if (legsup & EHCI_LEGSUP_BIOSOWNED)
|
||
kprintf("%s: timed out waiting for BIOS\n",
|
||
printf("%s: timed out waiting for BIOS\n",
|
||
device_get_nameunit(sc->sc_bus.bdev));
|
||
}
|
||
}
|
||
... | ... | |
cparams = EREAD4(sc, EHCI_HCCPARAMS);
|
||
for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
|
||
eecp = EHCI_EECP_NEXT(eec)) {
|
||
eecp = EHCI_EECP_NEXT(eec)) {
|
||
eec = pci_read_config(self, eecp, 4);
|
||
if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
|
||
continue;
|
||
-- ehcireg.h 2007-06-27 12:27:59 +0000
|
||
++ /root/src/sys/dev/usb/ehcireg.h 2005-09-18 11:45:39 +0000
|
||
... | ... | |
/* $NetBSD: ehcireg.h,v 1.18 2004/10/22 10:38:17 augustss Exp $ */
|
||
/* $FreeBSD: src/sys/dev/usb/ehcireg.h,v 1.7.2.1 2006/01/26 01:43:13 iedowse Exp $ */
|
||
/* $DragonFly: src/sys/bus/usb/ehcireg.h,v 1.7 2007/06/27 12:27:59 hasso Exp $ */
|
||
/* $FreeBSD: src/sys/dev/usb/ehcireg.h,v 1.8 2005/09/18 11:45:39 netchild Exp $ */
|
||
/*
|
||
/*-
|
||
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
||
* All rights reserved.
|
||
*
|
||
... | ... | |
#define EHCI_PAGE_SIZE 0x1000
|
||
#define EHCI_PAGE(x) ((x) &~ 0xfff)
|
||
#define EHCI_PAGE_OFFSET(x) ((x) & 0xfff)
|
||
#if defined(__FreeBSD__)
|
||
#define EHCI_PAGE_MASK(x) ((x) & 0xfff)
|
||
#endif
|
||
typedef u_int32_t ehci_link_t;
|
||
#define EHCI_LINK_TERMINATE 0x00000001
|
||
... | ... | |
#define EHCI_QTD_SET_TOGGLE(x) ((x) << 31)
|
||
#define EHCI_QTD_TOGGLE_MASK 0x80000000
|
||
ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS];
|
||
ehci_physaddr_t qtd_buffer_hi[EHCI_QTD_NBUFFERS];
|
||
ehci_physaddr_t qtd_buffer_hi[EHCI_QTD_NBUFFERS];
|
||
} ehci_qtd_t;
|
||
#define EHCI_QTD_ALIGN 32
|
||
-- ehcivar.h 2008-05-19 10:17:44 +0000
|
||
++ /root/src/sys/dev/usb/ehcivar.h 2007-06-14 16:23:31 +0000
|
||
... | ... | |
/* $NetBSD: ehcivar.h,v 1.19 2005/04/29 15:04:29 augustss Exp $ */
|
||
/* $FreeBSD: src/sys/dev/usb/ehcivar.h,v 1.9.2.1 2006/01/26 01:43:13 iedowse Exp $ */
|
||
/* $DragonFly: src/sys/bus/usb/ehcivar.h,v 1.11 2007/06/30 20:39:22 hasso Exp $ */
|
||
/* $FreeBSD: src/sys/dev/usb/ehcivar.h,v 1.17 2007/06/14 16:23:31 imp Exp $ */
|
||
/*
|
||
/*-
|
||
* Copyright (c) 2001 The NetBSD Foundation, Inc.
|
||
* All rights reserved.
|
||
*
|
||
... | ... | |
* Information about an entry in the interrupt list.
|
||
*/
|
||
struct ehci_soft_islot {
|
||
ehci_soft_qh_t *sqh; /* Queue Head. */
|
||
ehci_soft_qh_t *sqh; /* Queue Head. */
|
||
};
|
||
#define EHCI_FRAMELIST_MAXCOUNT 1024
|
||
#define EHCI_IPOLLRATES 8 /* Poll rates (1ms, 2, 4, 8 ... 128) */
|
||
#define EHCI_INTRQHS ((1 << EHCI_IPOLLRATES) - 1)
|
||
#define EHCI_MAX_POLLRATE (1 << (EHCI_IPOLLRATES - 1))
|
||
#define EHCI_IQHIDX(lev, pos) \
|
||
((((pos) & ((1 << (lev)) - 1)) | (1 << (lev))) - 1)
|
||
#define EHCI_IQHIDX(lev, pos) \
|
||
((((pos) & ((1 << (lev)) - 1)) | (1 << (lev))) - 1)
|
||
#define EHCI_ILEV_IVAL(lev) (1 << (lev))
|
||
#define EHCI_HASH_SIZE 128
|
||
#define EHCI_COMPANION_MAX 8
|
||
... | ... | |
typedef struct ehci_softc {
|
||
struct usbd_bus sc_bus; /* base device */
|
||
int sc_flags;
|
||
bus_space_tag_t iot;
|
||
bus_space_handle_t ioh;
|
||
bus_size_t sc_size;
|
||
#if defined(__FreeBSD__)
|
||
void *ih;
|
||
struct resource *io_res;
|
||
struct resource *irq_res;
|
||
#endif
|
||
u_int sc_offs; /* offset to operational regs */
|
||
int sc_flags; /* misc flags */
|
||
char sc_vendor[32]; /* vendor string for root hub */
|
||
int sc_id_vendor; /* vendor ID for root hub */
|
||
u_int32_t sc_cmd; /* shadow of cmd reg during suspend */
|
||
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||
void *sc_powerhook; /* cookie from power hook */
|
||
void *sc_shutdownhook; /* cookie from shutdown hook */
|
||
#endif
|
||
u_int sc_ncomp;
|
||
u_int sc_npcomp;
|
||
... | ... | |
usb_dma_t sc_fldma;
|
||
ehci_link_t *sc_flist;
|
||
u_int sc_flsize;
|
||
#ifndef __FreeBSD__
|
||
u_int sc_rand; /* XXX need proper intr scheduling */
|
||
#endif
|
||
struct ehci_soft_islot sc_islots[EHCI_INTRQHS];
|
||
... | ... | |
struct callout sc_tmo_intrlist;
|
||
char sc_dying;
|
||
#if defined(__NetBSD__)
|
||
struct usb_dma_reserve sc_dma_reserve;
|
||
#endif
|
||
} ehci_softc_t;
|
||
#define EREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
|
||
... | ... | |
usbd_status ehci_init(ehci_softc_t *);
|
||
int ehci_intr(void *);
|
||
int ehci_detach(ehci_softc_t *, int);
|
||
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
||
int ehci_activate(device_t, enum devact);
|
||
#endif
|
||
void ehci_power(int state, void *priv);
|
||
void ehci_shutdown(void *v);
|
||
-- hid.c 2008-05-18 17:51:14 +0000
|
||
++ /root/src/sys/dev/usb/hid.c 2008-05-18 21:28:20 +0000
|
||
... | ... | |
/*
|
||
* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $
|
||
* $FreeBSD: src/sys/dev/usb/hid.c,v 1.23 2003/08/24 17:55:54 obrien Exp $
|
||
* $DragonFly: src/sys/bus/usb/hid.c,v 1.13 2007/06/28 13:55:12 hasso Exp $
|
||
*/
|
||
/*
|
||
... | ... | |
/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */
|
||
#include <sys/cdefs.h>
|
||
__FBSDID("$FreeBSD: src/sys/dev/usb/hid.c,v 1.29 2007/06/20 05:10:52 imp Exp $");
|
||
/*-
|
||
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
||
* All rights reserved.
|
||
*
|
||
... | ... | |
#include <sys/systm.h>
|
||
#include <sys/malloc.h>
|
||
#include <bus/usb/usb.h>
|
||
#include <bus/usb/usbhid.h>
|
||
#include <dev/usb/usb.h>
|
||
#include <dev/usb/usbhid.h>
|
||
#include <bus/usb/hid.h>
|
||
#include <dev/usb/hid.h>
|
||
#ifdef USB_DEBUG
|
||
#define DPRINTF(x) if (usbdebug) kprintf x
|
||
#define DPRINTFN(n,x) if (usbdebug>(n)) kprintf x
|
||
#define DPRINTF(x) if (usbdebug) printf x
|
||
#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
|
||
extern int usbdebug;
|
||
#else
|
||
#define DPRINTF(x)
|
||
... | ... | |
{
|
||
struct hid_data *s;
|
- « Previous
- 1
- 2
- Next »