Use device speed in setup.

Simplify async list handling.
This commit is contained in:
augustss 2001-11-20 13:49:07 +00:00
parent 7e4459fe10
commit b38958f960
1 changed files with 167 additions and 136 deletions

View File

@ -1,4 +1,11 @@
/* $NetBSD: ehci.c,v 1.10 2001/11/19 02:57:16 augustss Exp $ */
/* TODO
Add intrinfo.
USB 2 reset is 50 ms?
Frame lengths of control and bulk are 64, 512?
Indicator light bit.
Check 7.1.7.3
*/
/* $NetBSD: ehci.c,v 1.11 2001/11/20 13:49:07 augustss Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@ -47,7 +54,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.10 2001/11/19 02:57:16 augustss Exp $");
__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.11 2001/11/20 13:49:07 augustss Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -110,6 +117,8 @@ Static void ehci_power(int, void *);
Static usbd_status ehci_open(usbd_pipe_handle);
Static void ehci_poll(struct usbd_bus *);
Static void ehci_softintr(void *);
Static int ehci_intr1(ehci_softc_t *);
Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t);
Static void ehci_freem(struct usbd_bus *, usb_dma_t *);
@ -168,12 +177,10 @@ Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *);
Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *);
Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *);
Static void ehci_hash_add_qtd(ehci_softc_t *, ehci_soft_qtd_t *);
Static void ehci_hash_rem_qtd(ehci_softc_t *, ehci_soft_qtd_t *);
Static ehci_soft_qtd_t *ehci_hash_find_qtd(ehci_softc_t *, ehci_physaddr_t);
Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *);
Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *,
ehci_soft_qh_t *);
Static void ehci_sync_hc(ehci_softc_t *);
Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *);
Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status);
@ -188,6 +195,8 @@ Static void ehci_dump_qtd(ehci_qtd_t *);
Static void ehci_dump_sqh(ehci_soft_qh_t *);
#endif
#define EHCI_NULL htole32(EHCI_LINK_TERMINATE)
#define EHCI_INTR_ENDPT 1
Static struct usbd_bus_methods ehci_bus_methods = {
@ -260,6 +269,7 @@ ehci_init(ehci_softc_t *sc)
u_int32_t version, sparams, cparams, hcr;
u_int i;
usbd_status err;
ehci_soft_qh_t *sqh;
DPRINTF(("ehci_init: start\n"));
#ifdef EHCI_DEBUG
@ -296,9 +306,6 @@ ehci_init(ehci_softc_t *sc)
sc->sc_bus.usbrev = USBREV_2_0;
for (i = 0; i < EHCI_HASH_SIZE; i++)
LIST_INIT(&sc->sc_hash_qtds[i]);
/* Reset the controller */
DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev)));
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
@ -337,48 +344,34 @@ ehci_init(ehci_softc_t *sc)
sc->sc_eintrs = EHCI_NORMAL_INTRS;
/* Allocate dummy QH that starts the bulk list. */
sc->sc_bulk_head = ehci_alloc_sqh(sc);
if (sc->sc_bulk_head == NULL) {
/* Allocate dummy QH that starts the async list. */
sqh = ehci_alloc_sqh(sc);
if (sqh == NULL) {
err = USBD_NOMEM;
goto bad1;
}
memset(&sc->sc_bulk_head->qh, 0, sizeof(ehci_qtd_t));
sc->sc_bulk_head->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
sc->sc_bulk_head->qh.qh_qtd.qtd_status =
/* Fill the QH */
sqh->qh.qh_endp =
htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL);
sqh->qh.qh_link =
htole32(sqh->physaddr | EHCI_LINK_QH);
sqh->qh.qh_curqtd = EHCI_NULL;
sqh->next = NULL;
/* Fill the overlay qTD */
sqh->qh.qh_qtd.qtd_next = EHCI_NULL;
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
sqh->qh.qh_qtd.qtd_status =
htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
sc->sc_bulk_head->qh.qh_link =
htole32(EHCI_LINK_TERMINATE); /* XXX no bw reclaimation */
sc->sc_bulk_head->next = NULL;
sqh->sqtd = NULL;
#ifdef EHCI_DEBUG
if (ehcidebug) {
ehci_dump_sqh(sc->sc_bulk_head);
}
#endif
/* Allocate dummy QH that starts the control list. */
sc->sc_ctrl_head = ehci_alloc_sqh(sc);
if (sc->sc_ctrl_head == NULL) {
err = USBD_NOMEM;
goto bad2;
}
memset(&sc->sc_ctrl_head->qh, 0, sizeof(ehci_qtd_t));
sc->sc_ctrl_head->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
sc->sc_ctrl_head->qh.qh_qtd.qtd_status =
htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
sc->sc_ctrl_head->qh.qh_endp = htole32(EHCI_QH_HRECL);
sc->sc_ctrl_head->qh.qh_link =
htole32(sc->sc_bulk_head->physaddr | EHCI_LINK_QH);
sc->sc_ctrl_head = sc->sc_bulk_head;
#ifdef EHCI_DEBUG
if (ehcidebug) {
ehci_dump_sqh(sc->sc_ctrl_head);
ehci_dump_sqh(sc->sc_async_head);
}
#endif
/* Point to async list */
EOWRITE4(sc, EHCI_ASYNCLISTADDR,
sc->sc_ctrl_head->physaddr | EHCI_LINK_QH);
sc->sc_async_head = sqh;
EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH);
usb_callout_init(sc->sc_tmo_pcd);
@ -413,17 +406,15 @@ ehci_init(ehci_softc_t *sc)
#if 0
bad3:
ehci_free_sqh(sc, sc->sc_bulk_head);
#endif
bad2:
ehci_free_sqh(sc, sc->sc_ctrl_head);
bad2:
ehci_free_sqtd(sc, sc->sc_bulk_head->sqtd);
#endif
bad1:
usb_freemem(&sc->sc_bus, &sc->sc_fldma);
return (err);
}
Static int ehci_intr1(ehci_softc_t *);
int
ehci_intr(void *v)
{
@ -472,7 +463,7 @@ ehci_intr1(ehci_softc_t *sc)
sc->sc_bus.no_intrs++;
if (eintrs & EHCI_STS_IAA) {
DPRINTF(("ehci_intr1: door bell\n"));
wakeup(&sc->sc_bulk_head);
wakeup(&sc->sc_async_head);
eintrs &= ~EHCI_STS_INT;
}
if (eintrs & EHCI_STS_INT) {
@ -859,7 +850,6 @@ ehci_open(usbd_pipe_handle pipe)
u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;
ehci_soft_qh_t *sqh;
ehci_soft_qtd_t *sqtd;
usbd_status err;
#if 0
ehci_soft_itd_t *sitd;
@ -887,7 +877,12 @@ ehci_open(usbd_pipe_handle pipe)
return (USBD_NORMAL_COMPLETION);
}
speed = EHCI_QH_SPEED_HIGH; /* XXX */
switch (dev->speed) {
case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break;
case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break;
case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break;
default: panic("ehci_open: bad device speed %d\n", dev->speed);
}
naks = 8; /* XXX */
sqh = ehci_alloc_sqh(sc);
if (sqh == NULL)
@ -905,39 +900,33 @@ ehci_open(usbd_pipe_handle pipe)
);
sqh->qh.qh_endphub = htole32(
EHCI_QH_SET_MULT(1)
/* XXX TT stuff */
/* XXX interrupt mask */
);
sqh->qh.qh_curqtd = htole32(EHCI_LINK_TERMINATE);
sqh->qh.qh_curqtd = EHCI_NULL;
/* Fill the overlay qTD */
sqh->qh.qh_qtd.qtd_next = EHCI_NULL;
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
sqh->qh.qh_qtd.qtd_status =
htole32(EHCI_QTD_SET_STATUS(EHCI_QTD_HALTED));
epipe->sqh = sqh;
#if 0
if (xfertype == UE_CONTROL || xfertype == UE_BULK) {
sqtd = ehci_alloc_sqtd(sc);
if (sqtd == NULL) {
ehci_free_sqtd(sc, sqtd);
goto bad1;
}
epipe->tail.qtd = sqtd;
tdphys = sqtd->physaddr;
} else
sqtd = NULL;
#endif
switch (xfertype) {
case UE_CONTROL:
pipe->methods = &ehci_device_ctrl_methods;
err = usb_allocmem(&sc->sc_bus,
sizeof(usb_device_request_t),
err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t),
0, &epipe->u.ctl.reqdma);
if (err)
goto bad;
goto bad1;
pipe->methods = &ehci_device_ctrl_methods;
s = splusb();
ehci_add_qh(sqh, sc->sc_ctrl_head);
ehci_add_qh(sqh, sc->sc_async_head);
splx(s);
break;
case UE_BULK:
pipe->methods = &ehci_device_bulk_methods;
s = splusb();
ehci_add_qh(sqh, sc->sc_bulk_head);
ehci_add_qh(sqh, sc->sc_async_head);
splx(s);
break;
default:
@ -945,12 +934,8 @@ ehci_open(usbd_pipe_handle pipe)
}
return (USBD_NORMAL_COMPLETION);
bad:
if (sqtd != NULL)
ehci_free_sqtd(sc, sqtd);
/*bad1:*/
if (sqh != NULL)
ehci_free_sqh(sc, sqh);
bad1:
ehci_free_sqh(sc, sqh);
bad0:
return (USBD_NOMEM);
}
@ -983,7 +968,6 @@ void
ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
{
ehci_soft_qh_t *p;
int s;
SPLUSBCHECK;
/* XXX */
@ -994,17 +978,25 @@ ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head)
p->next = sqh->next;
p->qh.qh_link = sqh->qh.qh_link;
/*
* Now we must ensure that the HC has released all references to the
* QH. We do this by asking for a Async Advance Doorbell interrupt
* and then we wait for the interrupt.
* To make this easier we first obtain exclusive use ofthe doorbell.
*/
ehci_sync_hc(sc);
}
/*
* Ensure that the HC has released all references to the QH. We do this
* by asking for a Async Advance Doorbell interrupt and then we wait for
* the interrupt.
* To make this easier we first obtain exclusive use of the doorbell.
*/
void
ehci_sync_hc(ehci_softc_t *sc)
{
int s;
lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL); /* get doorbell */
s = splhardusb();
/* ask for doorbell */
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD);
tsleep(&sc->sc_bulk_head, PZERO, "ehcidi", 0); /* wait for doorbell */
tsleep(&sc->sc_async_head, PZERO, "ehcidi", 0); /* wait for doorbell */
splx(s);
lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL); /* release doorbell */
}
@ -1020,13 +1012,25 @@ Static usb_device_descriptor_t ehci_devd = {
{0x00, 0x02}, /* USB version */
UDCLASS_HUB, /* class */
UDSUBCLASS_HUB, /* subclass */
0, /* protocol */
UDPROTO_HSHUBSTT, /* protocol */
64, /* max packet */
{0},{0},{0x00,0x01}, /* device id */
1,2,0, /* string indicies */
1 /* # of configurations */
};
Static usb_device_qualifier_t ehci_odevd = {
USB_DEVICE_DESCRIPTOR_SIZE,
UDESC_DEVICE_QUALIFIER, /* type */
{0x00, 0x02}, /* USB version */
UDCLASS_HUB, /* class */
UDSUBCLASS_HUB, /* subclass */
UDPROTO_FSHUB, /* protocol */
64, /* max packet */
1, /* # of configurations */
0
};
Static usb_config_descriptor_t ehci_confd = {
USB_CONFIG_DESCRIPTOR_SIZE,
UDESC_CONFIG,
@ -1048,7 +1052,7 @@ Static usb_interface_descriptor_t ehci_ifcd = {
1,
UICLASS_HUB,
UISUBCLASS_HUB,
0,
UIPROTO_HSHUBSTT,
0
};
@ -1169,6 +1173,23 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
USETW(ehci_devd.idVendor, sc->sc_id_vendor);
memcpy(buf, &ehci_devd, l);
break;
/*
* We can't really operate at another speed, but the spec says
* we need this descriptor.
*/
case UDESC_DEVICE_QUALIFIER:
if ((value & 0xff) != 0) {
err = USBD_IOERROR;
goto ret;
}
totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
memcpy(buf, &ehci_odevd, l);
break;
/*
* We can't really operate at another speed, but the spec says
* we need this descriptor.
*/
case UDESC_OTHER_SPEED_CONFIGURATION:
case UDESC_CONFIG:
if ((value & 0xff) != 0) {
err = USBD_IOERROR;
@ -1176,6 +1197,8 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
}
totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
memcpy(buf, &ehci_confd, l);
((usb_config_descriptor_t *)buf)->bDescriptorType =
value >> 8;
buf = (char *)buf + l;
len -= l;
l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
@ -1348,7 +1371,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
v = EOREAD4(sc, EHCI_PORTSC(index));
DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n",
v));
i = 0;
i = UPS_HIGH_SPEED;
if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS;
if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED;
if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND;
@ -1423,11 +1446,25 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer)
"%d\n", index));
EOWRITE4(sc, port, v | EHCI_PS_PP);
break;
case UHF_PORT_TEST:
DPRINTFN(2,("ehci_root_ctrl_transfer: set port test "
"%d\n", index));
break;
case UHF_PORT_INDICATOR:
DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind "
"%d\n", index));
/* XXX */
break;
default:
err = USBD_IOERROR;
goto ret;
}
break;
case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
break;
default:
err = USBD_IOERROR;
goto ret;
@ -1567,10 +1604,10 @@ ehci_alloc_sqh(ehci_softc_t *sc)
err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK,
EHCI_PAGE_SIZE, &dma);
if (err)
return (0);
return (NULL);
for(i = 0; i < EHCI_SQH_CHUNK; i++) {
offs = i * EHCI_SQH_SIZE;
sqh = (ehci_soft_qh_t *)((char *)KERNADDR(&dma) +offs);
sqh = (ehci_soft_qh_t *)((char *)KERNADDR(&dma) + offs);
sqh->physaddr = DMAADDR(&dma) + offs;
sqh->next = sc->sc_freeqhs;
sc->sc_freeqhs = sqh;
@ -1579,7 +1616,7 @@ ehci_alloc_sqh(ehci_softc_t *sc)
sqh = sc->sc_freeqhs;
sc->sc_freeqhs = sqh->next;
memset(&sqh->qh, 0, sizeof(ehci_qh_t));
sqh->next = 0;
sqh->next = NULL;
return (sqh);
}
@ -1622,7 +1659,6 @@ ehci_alloc_sqtd(ehci_softc_t *sc)
memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t));
sqtd->nextqtd = NULL;
sqtd->xfer = NULL;
ehci_hash_add_qtd(sc, sqtd);
splx(s);
return (sqtd);
@ -1634,57 +1670,11 @@ ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
int s;
s = splusb();
ehci_hash_rem_qtd(sc, sqtd);
sqtd->nextqtd = sc->sc_freeqtds;
sc->sc_freeqtds = sqtd;
splx(s);
}
/*
* When a transfer is completed the TD is added to the done queue by
* the host controller. This queue is the processed by software.
* Unfortunately the queue contains the physical address of the TD
* and we have no simple way to translate this back to a kernel address.
* To make the translation possible (and fast) we use a hash table of
* TDs currently in the schedule. The physical address is used as the
* hash value.
*/
#define HASH(a) (((a) >> 4) % EHCI_HASH_SIZE)
/* Called at splusb() */
void
ehci_hash_add_qtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
{
int h = HASH(sqtd->physaddr);
SPLUSBCHECK;
LIST_INSERT_HEAD(&sc->sc_hash_qtds[h], sqtd, hnext);
}
/* Called at splusb() */
void
ehci_hash_rem_qtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd)
{
SPLUSBCHECK;
LIST_REMOVE(sqtd, hnext);
}
ehci_soft_qtd_t *
ehci_hash_find_qtd(ehci_softc_t *sc, ehci_physaddr_t a)
{
int h = HASH(a);
ehci_soft_qtd_t *sqtd;
for (sqtd = LIST_FIRST(&sc->sc_hash_qtds[h]);
sqtd != NULL;
sqtd = LIST_NEXT(sqtd, hnext))
if (sqtd->physaddr == a)
return (sqtd);
return (NULL);
}
/*
* Close a reqular pipe.
* Assumes that there are no pending transactions.
@ -1722,14 +1712,55 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus;
ehci_soft_td_t *p, *n;
ehci_physaddr_t headp;
int s, hit;
int hit;
#endif
int s;
DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p sqh=%p\n", xfer, epipe,sqh));
if (xfer->device->bus->intr_context || !curproc)
panic("ehci_abort_xfer: not in process context\n");
/*
* Step 1: Make interrupt routine and hardware ignore xfer.
*/
s = splusb();
xfer->status = status; /* make software ignore it */
usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
splx(s);
/* XXX */
/*
* Step 2: Wait until we know hardware has finished any possible
* use of the xfer. Also make sure the soft interrupt routine
* has run.
*/
usb_delay_ms(epipe->pipe.device->bus, 1); /* Hardware finishes in 1ms */
/* XXX should have some communication with softintr() to know
when it's done */
usb_delay_ms(epipe->pipe.device->bus, 250);
/*
* Step 3: Remove any vestiges of the xfer from the hardware.
* The complication here is that the hardware may have executed
* beyond the xfer we're trying to abort. So as we're scanning
* the TDs of this xfer we check if the hardware points to
* any of them.
*/
s = splusb(); /* XXX why? */
/* XXX */
/*
* Step 4: Turn on hardware again.
*/
/* XXX */
/*
* Step 5: Execute callback.
*/
usb_transfer_complete(xfer);
splx(s);
}
/************************/
@ -1779,7 +1810,7 @@ ehci_device_ctrl_close(usbd_pipe_handle pipe)
/*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/
DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe));
ehci_close_pipe(pipe, sc->sc_ctrl_head);
ehci_close_pipe(pipe, sc->sc_async_head);
/*ehci_free_std(sc, epipe->tail.td);*/
}