From b38958f9606bc002128768a9e87d87ca14170a03 Mon Sep 17 00:00:00 2001 From: augustss Date: Tue, 20 Nov 2001 13:49:07 +0000 Subject: [PATCH] Use device speed in setup. Simplify async list handling. --- sys/dev/usb/ehci.c | 303 +++++++++++++++++++++++++-------------------- 1 file changed, 167 insertions(+), 136 deletions(-) diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c index 43822f83394a..f5890246044e 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -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 -__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 #include @@ -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);*/ }