From 8ae9b8c2b3c5707238e312f29f7fd0ac65417a68 Mon Sep 17 00:00:00 2001 From: Aaron Campbell Date: Tue, 28 Mar 2000 19:37:54 +0000 Subject: Much cleaner sync with NetBSD. Some #if defined() magic has been sent in the form of a diff to augustss@netbsd.org so that future syncs will be very easy. This commit also adds support for ADMtek AN986 "Pegasus" based USB Ethernet, CATC USB-EL1210A based USB Ethernet, and USB Printers (all untested). --- sys/dev/usb/uhci.c | 1260 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 781 insertions(+), 479 deletions(-) (limited to 'sys/dev/usb/uhci.c') diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c index 184c2e930ae..ca3fe32acea 100644 --- a/sys/dev/usb/uhci.c +++ b/sys/dev/usb/uhci.c @@ -1,5 +1,6 @@ -/* $OpenBSD: uhci.c,v 1.9 2000/03/26 21:47:51 aaron Exp $ */ -/* $NetBSD: uhci.c,v 1.87 2000/02/29 21:37:01 augustss Exp $ */ +/* $OpenBSD: uhci.c,v 1.10 2000/03/28 19:37:49 aaron Exp $ */ +/* $NetBSD: uhci.c,v 1.100 2000/03/27 09:41:36 augustss Exp $ */ +/* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -93,6 +94,7 @@ struct cfdriver uhci_cd = { #endif #ifdef UHCI_DEBUG +uhci_softc_t *thesc; #define DPRINTF(x) if (uhcidebug) printf x #define DPRINTFN(n,x) if (uhcidebug>(n)) printf x int uhcidebug = 0; @@ -105,16 +107,23 @@ int uhcidebug = 0; * The UHCI controller is little endian, so on big endian machines * the data strored in memory needs to be swapped. */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) #if BYTE_ORDER == BIG_ENDIAN -#define LE(x) (bswap32(x)) +#define htole32(x) (bswap32(x)) +#define le32toh(x) (bswap32(x)) #else -#define LE(x) (x) +#define htole32(x) (x) +#define le32toh(x) (x) +#endif #endif struct uhci_pipe { struct usbd_pipe pipe; - uhci_intr_info_t *iinfo; int nexttoggle; + + u_char aborting; + usbd_xfer_handle abortstart, abortend; + /* Info needed for different pipe kinds. */ union { /* Control pipe */ @@ -143,118 +152,116 @@ struct uhci_pipe { } u; }; -/* - * The uhci_intr_info free list can be global since they contain - * no dma specific data. The other free lists do. - */ -LIST_HEAD(, uhci_intr_info) uhci_ii_free; - -void uhci_busreset __P((uhci_softc_t *)); -void uhci_shutdown __P((void *v)); -void uhci_power __P((int, void *)); -usbd_status uhci_run __P((uhci_softc_t *, int run)); -uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *)); -void uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *)); -uhci_soft_qh_t *uhci_alloc_sqh __P((uhci_softc_t *)); -void uhci_free_sqh __P((uhci_softc_t *, uhci_soft_qh_t *)); -uhci_intr_info_t *uhci_alloc_intr_info __P((uhci_softc_t *)); -void uhci_free_intr_info __P((uhci_intr_info_t *ii)); +static void uhci_busreset __P((uhci_softc_t *)); +static void uhci_shutdown __P((void *v)); +static void uhci_power __P((int, void *)); +static usbd_status uhci_run __P((uhci_softc_t *, int run)); +static uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *)); +static void uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *)); +static uhci_soft_qh_t *uhci_alloc_sqh __P((uhci_softc_t *)); +static void uhci_free_sqh __P((uhci_softc_t *, uhci_soft_qh_t *)); #if 0 -void uhci_enter_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *, +static void uhci_enter_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *, uhci_intr_info_t *)); -void uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *)); +static void uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *)); #endif -void uhci_free_std_chain __P((uhci_softc_t *, +static void uhci_free_std_chain __P((uhci_softc_t *, uhci_soft_td_t *, uhci_soft_td_t *)); -usbd_status uhci_alloc_std_chain __P((struct uhci_pipe *, uhci_softc_t *, - int, int, u_int16_t, usb_dma_t *, - uhci_soft_td_t **, - uhci_soft_td_t **)); -void uhci_timo __P((void *)); -void uhci_waitintr __P((uhci_softc_t *, usbd_xfer_handle)); -void uhci_check_intr __P((uhci_softc_t *, uhci_intr_info_t *)); -void uhci_idone __P((uhci_intr_info_t *)); -void uhci_abort_xfer __P((usbd_xfer_handle, usbd_status status)); -void uhci_abort_xfer_end __P((void *v)); -void uhci_timeout __P((void *)); -void uhci_wakeup_ctrl __P((void *, int, int, void *, int)); -void uhci_lock_frames __P((uhci_softc_t *)); -void uhci_unlock_frames __P((uhci_softc_t *)); -void uhci_add_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); -void uhci_add_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); -void uhci_remove_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); -void uhci_remove_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); -int uhci_str __P((usb_string_descriptor_t *, int, char *)); - -usbd_status uhci_setup_isoc __P((usbd_pipe_handle pipe)); -void uhci_device_isoc_enter __P((usbd_xfer_handle)); - -void uhci_wakeup_cb __P((usbd_xfer_handle xfer)); - -usbd_status uhci_allocm __P((struct usbd_bus *, usb_dma_t *, u_int32_t)); -void uhci_freem __P((struct usbd_bus *, usb_dma_t *)); - -usbd_xfer_handle uhci_allocx __P((struct usbd_bus *)); -void uhci_freex __P((struct usbd_bus *, usbd_xfer_handle)); - -usbd_status uhci_device_ctrl_transfer __P((usbd_xfer_handle)); -usbd_status uhci_device_ctrl_start __P((usbd_xfer_handle)); -void uhci_device_ctrl_abort __P((usbd_xfer_handle)); -void uhci_device_ctrl_close __P((usbd_pipe_handle)); -void uhci_device_ctrl_done __P((usbd_xfer_handle)); - -usbd_status uhci_device_intr_transfer __P((usbd_xfer_handle)); -usbd_status uhci_device_intr_start __P((usbd_xfer_handle)); -void uhci_device_intr_abort __P((usbd_xfer_handle)); -void uhci_device_intr_close __P((usbd_pipe_handle)); -void uhci_device_intr_done __P((usbd_xfer_handle)); - -usbd_status uhci_device_bulk_transfer __P((usbd_xfer_handle)); -usbd_status uhci_device_bulk_start __P((usbd_xfer_handle)); -void uhci_device_bulk_abort __P((usbd_xfer_handle)); -void uhci_device_bulk_close __P((usbd_pipe_handle)); -void uhci_device_bulk_done __P((usbd_xfer_handle)); - -usbd_status uhci_device_isoc_transfer __P((usbd_xfer_handle)); -usbd_status uhci_device_isoc_start __P((usbd_xfer_handle)); -void uhci_device_isoc_abort __P((usbd_xfer_handle)); -void uhci_device_isoc_close __P((usbd_pipe_handle)); -void uhci_device_isoc_done __P((usbd_xfer_handle)); - -usbd_status uhci_root_ctrl_transfer __P((usbd_xfer_handle)); -usbd_status uhci_root_ctrl_start __P((usbd_xfer_handle)); -void uhci_root_ctrl_abort __P((usbd_xfer_handle)); -void uhci_root_ctrl_close __P((usbd_pipe_handle)); -void uhci_root_ctrl_done __P((usbd_xfer_handle)); - -usbd_status uhci_root_intr_transfer __P((usbd_xfer_handle)); -usbd_status uhci_root_intr_start __P((usbd_xfer_handle)); -void uhci_root_intr_abort __P((usbd_xfer_handle)); -void uhci_root_intr_close __P((usbd_pipe_handle)); -void uhci_root_intr_done __P((usbd_xfer_handle)); - -usbd_status uhci_open __P((usbd_pipe_handle)); -void uhci_poll __P((struct usbd_bus *)); -void uhci_softintr __P((struct usbd_bus *)); - -usbd_status uhci_device_request __P((usbd_xfer_handle xfer)); - -void uhci_add_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *)); -void uhci_remove_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *)); -usbd_status uhci_device_setintr __P((uhci_softc_t *sc, - struct uhci_pipe *pipe, int ival)); - -void uhci_device_clear_toggle __P((usbd_pipe_handle pipe)); -void uhci_noop __P((usbd_pipe_handle pipe)); +static usbd_status uhci_alloc_std_chain __P((struct uhci_pipe *, + uhci_softc_t *, int, int, u_int16_t, usb_dma_t *, + uhci_soft_td_t **, uhci_soft_td_t **)); +static void uhci_poll_hub __P((void *)); +static void uhci_waitintr __P((uhci_softc_t *, + usbd_xfer_handle)); +static void uhci_check_intr __P((uhci_softc_t *, + uhci_intr_info_t *)); +static void uhci_idone __P((uhci_intr_info_t *)); + +static void uhci_abort_xfer __P((usbd_xfer_handle, + usbd_status status)); +static void uhci_abort_xfer_end __P((void *v)); +static void uhci_abort_unlink_qh __P((struct uhci_pipe *)); +static void uhci_abort_relink_qh __P((struct uhci_pipe *)); +static void uhci_cancel_abort __P((usbd_pipe_handle)); + +static void uhci_timeout __P((void *)); +static void uhci_add_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *)); +static void uhci_add_bulk __P((uhci_softc_t *, uhci_soft_qh_t *)); +static void uhci_remove_ctrl __P((uhci_softc_t *,uhci_soft_qh_t *)); +static void uhci_remove_bulk __P((uhci_softc_t *,uhci_soft_qh_t *)); +static int uhci_str __P((usb_string_descriptor_t *, int, char *)); + +static usbd_status uhci_setup_isoc __P((usbd_pipe_handle pipe)); +static void uhci_device_isoc_enter __P((usbd_xfer_handle)); + +static usbd_status uhci_allocm __P((struct usbd_bus *, usb_dma_t *, + u_int32_t)); +static void uhci_freem __P((struct usbd_bus *, usb_dma_t *)); + +static usbd_xfer_handle uhci_allocx __P((struct usbd_bus *)); +static void uhci_freex __P((struct usbd_bus *, usbd_xfer_handle)); + +static usbd_status uhci_device_ctrl_transfer __P((usbd_xfer_handle)); +static usbd_status uhci_device_ctrl_start __P((usbd_xfer_handle)); +static void uhci_device_ctrl_abort __P((usbd_xfer_handle)); +static void uhci_device_ctrl_close __P((usbd_pipe_handle)); +static void uhci_device_ctrl_done __P((usbd_xfer_handle)); + +static usbd_status uhci_device_intr_transfer __P((usbd_xfer_handle)); +static usbd_status uhci_device_intr_start __P((usbd_xfer_handle)); +static void uhci_device_intr_abort __P((usbd_xfer_handle)); +static void uhci_device_intr_close __P((usbd_pipe_handle)); +static void uhci_device_intr_done __P((usbd_xfer_handle)); + +static usbd_status uhci_device_bulk_transfer __P((usbd_xfer_handle)); +static usbd_status uhci_device_bulk_start __P((usbd_xfer_handle)); +static void uhci_device_bulk_abort __P((usbd_xfer_handle)); +static void uhci_device_bulk_close __P((usbd_pipe_handle)); +static void uhci_device_bulk_done __P((usbd_xfer_handle)); + +static usbd_status uhci_device_isoc_transfer __P((usbd_xfer_handle)); +static usbd_status uhci_device_isoc_start __P((usbd_xfer_handle)); +static void uhci_device_isoc_abort __P((usbd_xfer_handle)); +static void uhci_device_isoc_close __P((usbd_pipe_handle)); +static void uhci_device_isoc_done __P((usbd_xfer_handle)); + +static usbd_status uhci_root_ctrl_transfer __P((usbd_xfer_handle)); +static usbd_status uhci_root_ctrl_start __P((usbd_xfer_handle)); +static void uhci_root_ctrl_abort __P((usbd_xfer_handle)); +static void uhci_root_ctrl_close __P((usbd_pipe_handle)); +static void uhci_root_ctrl_done __P((usbd_xfer_handle)); + +static usbd_status uhci_root_intr_transfer __P((usbd_xfer_handle)); +static usbd_status uhci_root_intr_start __P((usbd_xfer_handle)); +static void uhci_root_intr_abort __P((usbd_xfer_handle)); +static void uhci_root_intr_close __P((usbd_pipe_handle)); +static void uhci_root_intr_done __P((usbd_xfer_handle)); + +static usbd_status uhci_open __P((usbd_pipe_handle)); +static void uhci_poll __P((struct usbd_bus *)); +static void uhci_softintr __P((struct usbd_bus *)); + +static usbd_status uhci_device_request __P((usbd_xfer_handle xfer)); + +static void uhci_add_intr __P((uhci_softc_t *, uhci_soft_qh_t *)); +static void uhci_remove_intr __P((uhci_softc_t*, uhci_soft_qh_t*)); +static usbd_status uhci_device_setintr __P((uhci_softc_t *sc, + struct uhci_pipe *pipe, int ival)); + +static void uhci_device_clear_toggle __P((usbd_pipe_handle pipe)); +static void uhci_noop __P((usbd_pipe_handle pipe)); + +static __inline__ uhci_soft_qh_t *uhci_find_prev_qh + __P((uhci_soft_qh_t *, uhci_soft_qh_t *)); #ifdef UHCI_DEBUG -static void uhci_dumpregs __P((uhci_softc_t *)); -void uhci_dump_qhs __P((uhci_soft_qh_t *)); -void uhci_dump_qh __P((uhci_soft_qh_t *)); -void uhci_dump __P((void)); -void uhci_dump_tds __P((uhci_soft_td_t *)); -void uhci_dump_td __P((uhci_soft_td_t *)); +static void uhci_dumpregs __P((uhci_softc_t *)); +static void uhci_dump_qhs __P((uhci_soft_qh_t *)); +static void uhci_dump_qh __P((uhci_soft_qh_t *)); +static void uhci_dump_tds __P((uhci_soft_td_t *)); +static void uhci_dump_td __P((uhci_soft_td_t *)); +static void uhci_dump_ii __P((uhci_intr_info_t *ii)); #endif #define UWRITE1(sc, r, x) bus_space_write_1((sc)->iot, (sc)->ioh, (r), (x)) @@ -337,6 +344,28 @@ struct usbd_pipe_methods uhci_device_isoc_methods = { uhci_device_isoc_done, }; +#define uhci_add_intr_info(sc, ii) \ + LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ii), list); +#define uhci_del_intr_info(ii) \ + LIST_REMOVE((ii), list) + +static __inline__ uhci_soft_qh_t * +uhci_find_prev_qh(pqh, sqh) + uhci_soft_qh_t *pqh, *sqh; +{ + DPRINTFN(15,("uhci_find_prev_qh: pqh=%p sqh=%p\n", pqh, sqh)); + + for (; pqh->hlink != sqh; pqh = pqh->hlink) { +#if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) + if (le32toh(pqh->qh.qh_hlink) & UHCI_PTR_T) { + printf("uhci_find_qh: QH not found\n"); + return (NULL); + } +#endif + } + return (pqh); +} + void uhci_busreset(sc) uhci_softc_t *sc; @@ -358,6 +387,8 @@ uhci_init(sc) DPRINTFN(1,("uhci_init: start\n")); #ifdef UHCI_DEBUG + thesc = sc; + if (uhcidebug > 2) uhci_dumpregs(sc); #endif @@ -369,8 +400,8 @@ uhci_init(sc) /* Allocate and initialize real frame array. */ err = usb_allocmem(&sc->sc_bus, - UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), - UHCI_FRAMELIST_ALIGN, &sc->sc_dma); + UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), + UHCI_FRAMELIST_ALIGN, &sc->sc_dma); if (err) return (err); sc->sc_pframes = KERNADDR(&sc->sc_dma); @@ -381,8 +412,8 @@ uhci_init(sc) bsqh = uhci_alloc_sqh(sc); if (bsqh == NULL) return (USBD_NOMEM); - bsqh->qh.qh_hlink = LE(UHCI_PTR_T); /* end of QH chain */ - bsqh->qh.qh_elink = LE(UHCI_PTR_T); + bsqh->qh.qh_hlink = htole32(UHCI_PTR_T); /* end of QH chain */ + bsqh->qh.qh_elink = htole32(UHCI_PTR_T); sc->sc_bulk_start = sc->sc_bulk_end = bsqh; /* Allocate the dummy QH where control traffic will be queued. */ @@ -390,8 +421,8 @@ uhci_init(sc) if (csqh == NULL) return (USBD_NOMEM); csqh->hlink = bsqh; - csqh->qh.qh_hlink = LE(bsqh->physaddr | UHCI_PTR_Q); - csqh->qh.qh_elink = LE(UHCI_PTR_T); + csqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_Q); + csqh->qh.qh_elink = htole32(UHCI_PTR_T); sc->sc_ctl_start = sc->sc_ctl_end = csqh; /* @@ -405,14 +436,14 @@ uhci_init(sc) if (std == NULL || sqh == NULL) return (USBD_NOMEM); std->link.sqh = sqh; - std->td.td_link = LE(sqh->physaddr | UHCI_PTR_Q); - std->td.td_status = LE(UHCI_TD_IOS); /* iso, inactive */ - std->td.td_token = LE(0); - std->td.td_buffer = LE(0); + std->td.td_link = htole32(sqh->physaddr | UHCI_PTR_Q); + std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */ + std->td.td_token = htole32(0); + std->td.td_buffer = htole32(0); sqh->hlink = csqh; - sqh->qh.qh_hlink = LE(csqh->physaddr | UHCI_PTR_Q); + sqh->qh.qh_hlink = htole32(csqh->physaddr | UHCI_PTR_Q); sqh->elink = 0; - sqh->qh.qh_elink = LE(UHCI_PTR_T); + sqh->qh.qh_elink = htole32(UHCI_PTR_T); sc->sc_vframes[i].htd = std; sc->sc_vframes[i].etd = std; sc->sc_vframes[i].hqh = sqh; @@ -420,21 +451,24 @@ uhci_init(sc) for (j = i; j < UHCI_FRAMELIST_COUNT; j += UHCI_VFRAMELIST_COUNT) - sc->sc_pframes[j] = LE(std->physaddr); + sc->sc_pframes[j] = htole32(std->physaddr); } LIST_INIT(&sc->sc_intrhead); SIMPLEQ_INIT(&sc->sc_free_xfers); + usb_callout_init(sc->sc_poll_handle); + /* Set up the bus struct. */ sc->sc_bus.methods = &uhci_bus_methods; sc->sc_bus.pipe_size = sizeof(struct uhci_pipe); +#if defined(__NetBSD__) || defined(__OpenBSD__) sc->sc_suspend = PWR_RESUME; sc->sc_powerhook = powerhook_establish(uhci_power, sc); - sc->sc_shutdownhook = shutdownhook_establish(uhci_shutdown, sc); +#endif DPRINTFN(1,("uhci_init: enabling\n")); UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | @@ -481,8 +515,10 @@ uhci_detach(sc, flags) if (rv != 0) return (rv); +#if defined(__NetBSD__) || defined(__OpenBSD__) powerhook_disestablish(sc->sc_powerhook); shutdownhook_disestablish(sc->sc_shutdownhook); +#endif /* Free all xfers associated with this HC. */ for (;;) { @@ -491,7 +527,7 @@ uhci_detach(sc, flags) break; SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); free(xfer, M_USB); - } + } /* XXX free other data structures XXX */ @@ -525,12 +561,27 @@ uhci_allocx(bus) usbd_xfer_handle xfer; xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); - if (xfer != NULL) + if (xfer != NULL) { SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, xfer, next); - else - xfer = malloc(sizeof(*xfer), M_USB, M_NOWAIT); - if (xfer != NULL) - memset(xfer, 0, sizeof *xfer); +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_FREE) { + printf("uhci_freex: xfer=%p not free, 0x%08x\n", xfer, + xfer->busy_free); + } +#endif + } else { + xfer = malloc(sizeof(struct uhci_xfer), M_USB, M_NOWAIT); + } + if (xfer != NULL) { + memset(xfer, 0, sizeof (struct uhci_xfer)); + UXFER(xfer)->iinfo.sc = sc; +#ifdef DIAGNOSTIC + UXFER(xfer)->iinfo.isdone = 1; +#endif + } +#ifdef DIAGNOSTIC + xfer->busy_free = XFER_BUSY; +#endif return (xfer); } @@ -541,6 +592,16 @@ uhci_freex(bus, xfer) { struct uhci_softc *sc = (struct uhci_softc *)bus; +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_BUSY) { + printf("uhci_freex: xfer=%p not busy, 0x%08x\n", xfer, + xfer->busy_free); + return; + } + xfer->busy_free = XFER_FREE; + if (!UXFER(xfer)->iinfo.isdone) + printf("uhci_freex: !isdone\n"); +#endif SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); } @@ -584,9 +645,9 @@ uhci_power(why, v) if (uhcidebug > 2) uhci_dumpregs(sc); #endif - if (sc->sc_has_timo != NULL) - usb_untimeout(uhci_timo, sc->sc_has_timo, - sc->sc_has_timo->timo_handle); + if (sc->sc_intr_xfer != NULL) + usb_uncallout(sc->sc_poll_handle, uhci_poll_hub, + sc->sc_intr_xfer); sc->sc_bus.use_polling++; uhci_run(sc, 0); /* stop the controller */ @@ -622,9 +683,9 @@ uhci_power(why, v) uhci_run(sc, 1); /* and start traffic again */ usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); sc->sc_bus.use_polling--; - if (sc->sc_has_timo != NULL) - usb_timeout(uhci_timo, sc->sc_has_timo, - sc->sc_ival, sc->sc_has_timo->timo_handle); + if (sc->sc_intr_xfer != NULL) + usb_callout(sc->sc_poll_handle, sc->sc_ival, + uhci_poll_hub, sc->sc_intr_xfer); #ifdef UHCI_DEBUG if (uhcidebug > 2) uhci_dumpregs(sc); @@ -658,24 +719,24 @@ uhci_dump_td(p) DPRINTFN(-1,("TD(%p) at %08lx = link=0x%08lx status=0x%08lx " "token=0x%08lx buffer=0x%08lx\n", p, (long)p->physaddr, - (long)LE(p->td.td_link), - (long)LE(p->td.td_status), - (long)LE(p->td.td_token), - (long)LE(p->td.td_buffer))); + (long)le32toh(p->td.td_link), + (long)le32toh(p->td.td_status), + (long)le32toh(p->td.td_token), + (long)le32toh(p->td.td_buffer))); DPRINTFN(-1,(" %b %b,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d," "D=%d,maxlen=%d\n", - (int)LE(p->td.td_link), + (int)le32toh(p->td.td_link), "\20\1T\2Q\3VF", - (int)LE(p->td.td_status), + (int)le32toh(p->td.td_status), "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27" "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD", - UHCI_TD_GET_ERRCNT(LE(p->td.td_status)), - UHCI_TD_GET_ACTLEN(LE(p->td.td_status)), - UHCI_TD_GET_PID(LE(p->td.td_token)), - UHCI_TD_GET_DEVADDR(LE(p->td.td_token)), - UHCI_TD_GET_ENDPT(LE(p->td.td_token)), - UHCI_TD_GET_DT(LE(p->td.td_token)), - UHCI_TD_GET_MAXLEN(LE(p->td.td_token)))); + UHCI_TD_GET_ERRCNT(le32toh(p->td.td_status)), + UHCI_TD_GET_ACTLEN(le32toh(p->td.td_status)), + UHCI_TD_GET_PID(le32toh(p->td.td_token)), + UHCI_TD_GET_DEVADDR(le32toh(p->td.td_token)), + UHCI_TD_GET_ENDPT(le32toh(p->td.td_token)), + UHCI_TD_GET_DT(le32toh(p->td.td_token)), + UHCI_TD_GET_MAXLEN(le32toh(p->td.td_token)))); } void @@ -683,14 +744,16 @@ uhci_dump_qh(sqh) uhci_soft_qh_t *sqh; { DPRINTFN(-1,("QH(%p) at %08x: hlink=%08x elink=%08x\n", sqh, - (int)sqh->physaddr, LE(sqh->qh.qh_hlink), LE(sqh->qh.qh_elink))); + (int)sqh->physaddr, le32toh(sqh->qh.qh_hlink), + le32toh(sqh->qh.qh_elink))); } + #if 0 void uhci_dump() { - uhci_softc_t *sc = uhci; + uhci_softc_t *sc = thesc; uhci_dumpregs(sc); printf("intrs=%d\n", sc->sc_bus.no_intrs); @@ -699,6 +762,7 @@ uhci_dump() } #endif + void uhci_dump_qhs(sqh) uhci_soft_qh_t *sqh; @@ -719,12 +783,13 @@ uhci_dump_qhs(sqh) * TD2.x being the TDs queued at QH2 and QH1 being referenced from QH1. */ - if (sqh->hlink != NULL && !(sqh->qh.qh_hlink & UHCI_PTR_T)) + + if (sqh->hlink != NULL && !(le32toh(sqh->qh.qh_hlink) & UHCI_PTR_T)) uhci_dump_qhs(sqh->hlink); else DPRINTF(("No QH\n")); - if (sqh->elink != NULL && !(sqh->qh.qh_elink & UHCI_PTR_T)) + if (sqh->elink != NULL && !(le32toh(sqh->qh.qh_elink) & UHCI_PTR_T)) uhci_dump_tds(sqh->elink); else DPRINTF(("No TD\n")); @@ -744,10 +809,66 @@ uhci_dump_tds(std) * printing the free list in case the queue/TD has * already been moved there (seatbelt). */ - if (td->td.td_link & UHCI_PTR_T || td->td.td_link == 0) + if (le32toh(td->td.td_link) & UHCI_PTR_T || + le32toh(td->td.td_link) == 0) break; } } + +static void +uhci_dump_ii(ii) + uhci_intr_info_t *ii; +{ + usbd_pipe_handle pipe; + usb_endpoint_descriptor_t *ed; + usbd_device_handle dev; + +#ifdef DIAGNOSTIC +#define DONE ii->isdone +#else +#define DONE 0 +#endif + if (ii == NULL) { + printf("ii NULL\n"); + return; + } + if (ii->xfer == NULL) { + printf("ii %p: done=%d xfer=NULL\n", + ii, DONE); + return; + } + pipe = ii->xfer->pipe; + if (pipe == NULL) { + printf("ii %p: done=%d xfer=%p pipe=NULL\n", + ii, DONE, ii->xfer); + return; + } + ed = pipe->endpoint->edesc; + dev = pipe->device; + printf("ii %p: done=%d xfer=%p dev=%p vid=0x%04x pid=0x%04x addr=%d pipe=%p ep=0x%02x attr=0x%02x\n", + ii, DONE, ii->xfer, dev, + UGETW(dev->ddesc.idVendor), + UGETW(dev->ddesc.idProduct), + dev->address, pipe, + ed->bEndpointAddress, ed->bmAttributes); +#undef DONE +} + +void uhci_dump_iis(struct uhci_softc *); +void +uhci_dump_iis(sc) + struct uhci_softc *sc; +{ + uhci_intr_info_t *ii; + + printf("intr_info list:\n"); + for (ii = LIST_FIRST(&sc->sc_intrhead); ii; ii = LIST_NEXT(ii, list)) + uhci_dump_ii(ii); +} + +void iidump(void); +void iidump() { uhci_dump_iis(thesc); } + #endif /* @@ -755,7 +876,7 @@ uhci_dump_tds(std) * from the root controller interrupt pipe for port status change. */ void -uhci_timo(addr) +uhci_poll_hub(addr) void *addr; { usbd_xfer_handle xfer = addr; @@ -764,9 +885,9 @@ uhci_timo(addr) int s; u_char *p; - DPRINTFN(20, ("uhci_timo\n")); + DPRINTFN(20, ("uhci_poll_hub\n")); - usb_timeout(uhci_timo, xfer, sc->sc_ival, xfer->timo_handle); + usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); p = KERNADDR(&xfer->dmabuf); p[0] = 0; @@ -781,7 +902,6 @@ uhci_timo(addr) xfer->actlen = 1; xfer->status = USBD_NORMAL_COMPLETION; s = splusb(); - xfer->hcpriv = 0; xfer->device->bus->intr_context++; usb_transfer_complete(xfer); xfer->device->bus->intr_context--; @@ -800,64 +920,6 @@ uhci_root_ctrl_done(xfer) { } - -void -uhci_lock_frames(sc) - uhci_softc_t *sc; -{ - int s = splusb(); - - while (sc->sc_vflock & UHCI_HAS_LOCK) { - sc->sc_vflock |= UHCI_WANT_LOCK; - tsleep(&sc->sc_vflock, PRIBIO, "uhcqhl", 0); - } - sc->sc_vflock = UHCI_HAS_LOCK; - splx(s); -} - -void -uhci_unlock_frames(sc) - uhci_softc_t *sc; -{ - int s = splusb(); - - sc->sc_vflock &= ~UHCI_HAS_LOCK; - if (sc->sc_vflock & UHCI_WANT_LOCK) - wakeup(&sc->sc_vflock); - splx(s); -} - -/* - * Allocate an interrupt information struct. A free list is kept - * for fast allocation. - */ -uhci_intr_info_t * -uhci_alloc_intr_info(sc) - uhci_softc_t *sc; -{ - uhci_intr_info_t *ii; - - ii = LIST_FIRST(&uhci_ii_free); - if (ii) - LIST_REMOVE(ii, list); - else { - ii = malloc(sizeof(uhci_intr_info_t), M_USBHC, M_NOWAIT); - } - ii->sc = sc; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif - - return ii; -} - -void -uhci_free_intr_info(ii) - uhci_intr_info_t *ii; -{ - LIST_INSERT_HEAD(&uhci_ii_free, ii, list); /* and put on free list */ -} - /* Add control QH, called at splusb(). */ void uhci_add_ctrl(sc, sqh) @@ -873,7 +935,7 @@ uhci_add_ctrl(sc, sqh) sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; eqh->hlink = sqh; - eqh->qh.qh_hlink = LE(sqh->physaddr | UHCI_PTR_Q); + eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_Q); sc->sc_ctl_end = sqh; } @@ -888,15 +950,7 @@ uhci_remove_ctrl(sc, sqh) SPLUSBCHECK; DPRINTFN(10, ("uhci_remove_ctrl: sqh=%p\n", sqh)); - for (pqh = sc->sc_ctl_start; pqh->hlink != sqh; pqh=pqh->hlink) -#if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) - if (LE(pqh->qh.qh_hlink) & UHCI_PTR_T) { - printf("uhci_remove_ctrl: QH not found\n"); - return; - } -#else - ; -#endif + pqh = uhci_find_prev_qh(sc->sc_ctl_start, sqh); pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; if (sc->sc_ctl_end == sqh) @@ -918,7 +972,7 @@ uhci_add_bulk(sc, sqh) sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; eqh->hlink = sqh; - eqh->qh.qh_hlink = LE(sqh->physaddr | UHCI_PTR_Q); + eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_Q); sc->sc_bulk_end = sqh; } @@ -933,15 +987,7 @@ uhci_remove_bulk(sc, sqh) SPLUSBCHECK; DPRINTFN(10, ("uhci_remove_bulk: sqh=%p\n", sqh)); - for (pqh = sc->sc_bulk_start; pqh->hlink != sqh; pqh = pqh->hlink) -#if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) - if (LE(pqh->qh.qh_hlink) & UHCI_PTR_T) { - printf("uhci_remove_bulk: QH not found\n"); - return; - } -#else - ; -#endif + pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh); pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; if (sc->sc_bulk_end == sqh) @@ -967,7 +1013,7 @@ uhci_intr(arg) if (status == 0) /* The interrupt was not for us. */ return (0); -#if defined(DIAGNOSTIC) && (defined(__NetBSD__) || defined(__OpenBSD__)) +#if defined(DIAGNOSTIC) && defined(__NetBSD__) if (sc->sc_suspend != PWR_RESUME) printf("uhci_intr: suspended sts=0x%x\n", status); #endif @@ -987,7 +1033,7 @@ uhci_intr(arg) } if (status & UHCI_STS_HCPE) { ack |= UHCI_STS_HCPE; - printf("%s: host controller process error\n", + printf("%s: host controller process error\n", USBDEVNAME(sc->sc_bus.bdev)); } if (status & UHCI_STS_HCH) { @@ -1068,10 +1114,10 @@ uhci_check_intr(sc, ii) * is a an error somewhere in the middle, or whether there was a * short packet (SPD and not ACTIVE). */ - if (LE(lstd->td.td_status) & UHCI_TD_ACTIVE) { - DPRINTFN(15, ("uhci_check_intr: active ii=%p\n", ii)); + if (le32toh(lstd->td.td_status) & UHCI_TD_ACTIVE) { + DPRINTFN(12, ("uhci_check_intr: active ii=%p\n", ii)); for (std = ii->stdstart; std != lstd; std = std->link.std) { - status = LE(std->td.td_status); + status = le32toh(std->td.td_status); /* If there's an active TD the xfer isn't done. */ if (status & UHCI_TD_ACTIVE) break; @@ -1080,17 +1126,17 @@ uhci_check_intr(sc, ii) goto done; /* We want short packets, and it is short: it's done */ if ((status & UHCI_TD_SPD) && - UHCI_TD_GET_ACTLEN(status) < - UHCI_TD_GET_MAXLEN(LE(std->td.td_token))) + UHCI_TD_GET_ACTLEN(status) < + UHCI_TD_GET_MAXLEN(le32toh(std->td.td_token))) goto done; } - DPRINTFN(15, ("uhci_check_intr: ii=%p std=%p still active\n", + DPRINTFN(12, ("uhci_check_intr: ii=%p std=%p still active\n", ii, ii->stdstart)); return; } done: - DPRINTFN(15, ("uhci_check_intr: ii=%p done\n", ii)); - usb_untimeout(uhci_timeout, ii, ii->timeout_handle); + DPRINTFN(12, ("uhci_check_intr: ii=%p done\n", ii)); + usb_uncallout(ii->xfer->timeout_handle, uhci_timeout, ii); uhci_idone(ii); } @@ -1110,7 +1156,12 @@ uhci_idone(ii) int s = splhigh(); if (ii->isdone) { splx(s); +#ifdef UHCI_DEBUG + printf("uhci_idone: ii is done!\n "); + uhci_dump_ii(ii); +#else printf("uhci_idone: ii=%p is done!\n", ii); +#endif return; } ii->isdone = 1; @@ -1133,7 +1184,7 @@ uhci_idone(ii) nframes = xfer->nframes; actlen = 0; - n = xfer->hcprivint; + n = UXFER(xfer)->curframe; for (i = 0; i < nframes; i++) { std = stds[n]; #ifdef UHCI_DEBUG @@ -1144,13 +1195,12 @@ uhci_idone(ii) #endif if (++n >= UHCI_VFRAMELIST_COUNT) n = 0; - status = LE(std->td.td_status); + status = le32toh(std->td.td_status); actlen += UHCI_TD_GET_ACTLEN(status); } upipe->u.iso.inuse -= nframes; xfer->actlen = actlen; xfer->status = USBD_NORMAL_COMPLETION; - xfer->hcpriv = ii; usb_transfer_complete(xfer); return; } @@ -1163,20 +1213,20 @@ uhci_idone(ii) #endif /* The transfer is done, compute actual length and status. */ - /* XXX Is this correct for control xfers? */ actlen = 0; for (std = ii->stdstart; std != NULL; std = std->link.std) { - nstatus = LE(std->td.td_status); + nstatus = le32toh(std->td.td_status); if (nstatus & UHCI_TD_ACTIVE) break; status = nstatus; - if (UHCI_TD_GET_PID(LE(std->td.td_token)) != UHCI_TD_PID_SETUP) + if (UHCI_TD_GET_PID(le32toh(std->td.td_token)) != + UHCI_TD_PID_SETUP) actlen += UHCI_TD_GET_ACTLEN(status); } /* If there are left over TDs we need to update the toggle. */ if (std != NULL) - upipe->nexttoggle = UHCI_TD_GET_DT(LE(std->td.td_token)); + upipe->nexttoggle = UHCI_TD_GET_DT(le32toh(std->td.td_token)); status &= UHCI_TD_ERROR; DPRINTFN(10, ("uhci_check_intr: actlen=%d, status=0x%x\n", @@ -1198,7 +1248,6 @@ uhci_idone(ii) } else { xfer->status = USBD_NORMAL_COMPLETION; } - xfer->hcpriv = ii; usb_transfer_complete(xfer); } @@ -1275,17 +1324,16 @@ uhci_poll(bus) #if 0 void -uhci_reset(p) - void *p; +uhci_reset(sc) + uhci_softc_t *sc; { - uhci_softc_t *sc = p; int n; UHCICMD(sc, UHCI_CMD_HCRESET); /* The reset bit goes low when the controller is done. */ for (n = 0; n < UHCI_RESET_TIMEOUT && (UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET); n++) - delay(100); + usb_delay_ms(&sc->sc_bus, 1); if (n >= UHCI_RESET_TIMEOUT) printf("%s: controller did not reset\n", USBDEVNAME(sc->sc_bus.bdev)); @@ -1348,7 +1396,7 @@ uhci_alloc_std(sc) if (sc->sc_freetds == NULL) { DPRINTFN(2,("uhci_alloc_std: allocating chunk\n")); err = usb_allocmem(&sc->sc_bus, UHCI_STD_SIZE * UHCI_STD_CHUNK, - UHCI_TD_ALIGN, &dma); + UHCI_TD_ALIGN, &dma); if (err) return (0); for(i = 0; i < UHCI_STD_CHUNK; i++) { @@ -1372,11 +1420,11 @@ uhci_free_std(sc, std) { #ifdef DIAGNOSTIC #define TD_IS_FREE 0x12345678 - if (std->td.td_token == LE(TD_IS_FREE)) { + if (le32toh(std->td.td_token) == TD_IS_FREE) { printf("uhci_free_std: freeing free TD %p\n", std); return; } - std->td.td_token = LE(TD_IS_FREE); + std->td.td_token = htole32(TD_IS_FREE); #endif std->link.std = sc->sc_freetds; sc->sc_freetds = std; @@ -1394,7 +1442,7 @@ uhci_alloc_sqh(sc) if (sc->sc_freeqhs == NULL) { DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n")); err = usb_allocmem(&sc->sc_bus, UHCI_SQH_SIZE * UHCI_SQH_CHUNK, - UHCI_QH_ALIGN, &dma); + UHCI_QH_ALIGN, &dma); if (err) return (0); for(i = 0; i < UHCI_SQH_CHUNK; i++) { @@ -1420,22 +1468,6 @@ uhci_free_sqh(sc, sqh) sc->sc_freeqhs = sqh; } -#if 0 -/* - * Enter a list of transfers onto a control queue. - * Called at splusb() - */ -void -uhci_enter_ctl_q(sc, sqh, ii) - uhci_softc_t *sc; - uhci_soft_qh_t *sqh; - uhci_intr_info_t *ii; -{ - DPRINTFN(5, ("uhci_enter_ctl_q: sqh=%p\n", sqh)); - -} -#endif - void uhci_free_std_chain(sc, std, stdend) uhci_softc_t *sc; @@ -1467,8 +1499,8 @@ uhci_alloc_std_chain(upipe, sc, len, rd, flags, dma, sp, ep) int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; DPRINTFN(8, ("uhci_alloc_std_chain: addr=%d endpt=%d len=%d ls=%d " - "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len, - upipe->pipe.device->lowspeed, flags)); + "flags=0x%x\n", addr, UE_GET_ADDR(endpt), len, + upipe->pipe.device->lowspeed, flags)); maxp = UGETW(upipe->pipe.endpoint->edesc->wMaxPacketSize); if (maxp == 0) { printf("uhci_alloc_std_chain: maxp=0\n"); @@ -1503,12 +1535,12 @@ uhci_alloc_std_chain(upipe, sc, len, rd, flags, dma, sp, ep) } p->link.std = lastp; if (lastlink == UHCI_PTR_T) - p->td.td_link = LE(lastlink); + p->td.td_link = htole32(lastlink); else - p->td.td_link = LE(lastlink|UHCI_PTR_VF); + p->td.td_link = htole32(lastlink|UHCI_PTR_VF); lastp = p; lastlink = p->physaddr; - p->td.td_status = LE(status); + p->td.td_status = htole32(status); if (i == ntd) { /* last TD */ l = len % maxp; @@ -1518,9 +1550,9 @@ uhci_alloc_std_chain(upipe, sc, len, rd, flags, dma, sp, ep) } else l = maxp; p->td.td_token = - LE(rd ? UHCI_TD_IN (l, endpt, addr, tog) : - UHCI_TD_OUT(l, endpt, addr, tog)); - p->td.td_buffer = LE(DMAADDR(dma) + i * maxp); + htole32(rd ? UHCI_TD_IN (l, endpt, addr, tog) : + UHCI_TD_OUT(l, endpt, addr, tog)); + p->td.td_buffer = htole32(DMAADDR(dma) + i * maxp); tog ^= 1; } *sp = lastp; @@ -1554,8 +1586,9 @@ uhci_device_bulk_transfer(xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -1567,7 +1600,7 @@ uhci_device_bulk_start(xfer) struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; - uhci_intr_info_t *ii = upipe->iinfo; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_soft_td_t *data, *dataend; uhci_soft_qh_t *sqh; usbd_status err; @@ -1594,10 +1627,10 @@ uhci_device_bulk_start(xfer) upipe->u.bulk.length = len; err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags, - &xfer->dmabuf, &data, &dataend); + &xfer->dmabuf, &data, &dataend); if (err) return (err); - dataend->td.td_status |= LE(UHCI_TD_IOC); + dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef UHCI_DEBUG if (uhcidebug > 8) { @@ -1610,9 +1643,6 @@ uhci_device_bulk_start(xfer) ii->xfer = xfer; ii->stdstart = data; ii->stdend = dataend; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_bulk_transfer: not done, ii=%p\n", ii); @@ -1621,17 +1651,17 @@ uhci_device_bulk_start(xfer) #endif sqh->elink = data; - sqh->qh.qh_elink = LE(data->physaddr); - sqh->intr_info = ii; + sqh->qh.qh_elink = htole32(data->physaddr); s = splusb(); uhci_add_bulk(sc, sqh); - LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); + uhci_add_intr_info(sc, ii); if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_timeout(uhci_timeout, ii, MS_TO_TICKS(xfer->timeout), - ii->timeout_handle); + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + uhci_timeout, ii); } + xfer->status = USBD_IN_PROGRESS; splx(s); #ifdef UHCI_DEBUG @@ -1656,61 +1686,317 @@ uhci_device_bulk_abort(xfer) uhci_abort_xfer(xfer, USBD_CANCELLED); } +/* + * Aborting a xfer on the UHCI host controller is tricky. + * The problem is that the HC can asynchronously manipulate + * the very fields in the QH and TD that we need to abort a + * xfer. + * The problematic field are qh_elink (which points to the first + * TD) and td_status which contains the active flag. + * + * Here's my current (convoluted) strategy: + * - Block HC interrupt. We need this to check if the xfer + * might already be over. If called outside splusb() this can + * happen. + * - Check if an abort is already in progress (see below), if so + * just link out the xfer, run the callback, and return. + * - Otherwise, flag that abort is in progress. + * - Remove the QH for the xfer from the list of QHs (this + * can be done safely). + * - Remove the transaction from the list of transactions examined + * when the HC interrupts. + * At this point we know that the transaction will never be considered + * by the interrupt routine. The trouble we have is that the HC might + * be following the chain of TDs rooted at the unlinked QH because it + * started before the unlink. + * We would like to run the xfer callback function at this point + * to inform it that the xfer has been aborted, but running the + * callback might result in freeing the xfer. This would be bad + * since the HC might still use it. So we need to avoid this by: + * - Disable the active flag in all TD belonging to the xfer. + * If we do this we can guarantee that the HC will execute at most one + * TD after we turn off the flag in the last TD. + * - Busy-wait until the HC has finished with the TD. We do this by + * keeping track of the longest TD and using delay() for the time it + * takes to complete it (one byte takes a little less than 1 (LS 6) us). + * - Run the callback routine, since at this point the HC can not be + * using any TDs in the xfer. + * We still cannot manipulate the qh_elink field in the QH since the + * HC might be following TDs further down the chain for another 1 ms. + * So... + * - Set up a timeout 1 ms into the future. + * - Turn on interrupts. + * - Return. + * + * When the timeout happens we do the following: + * - Check if the qh_elink field points anywhere in the TD chain we had + * when the timeout was set up. If it is, leave qh_elink alone, + * otherwise set qh_elink pointing to the next (if any) xfer in + * the TD chain. + * - Link the QH back where we got it. + * - Turn off flag about abort in progress. + * Done! + * + * The timeout is associated with the pipe and it must be cancelled if + * the pipe is closed. + */ + void uhci_abort_xfer(xfer, status) usbd_xfer_handle xfer; usbd_status status; { struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; - uhci_intr_info_t *ii = upipe->iinfo; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_soft_td_t *std; + int s; + int len, maxlen; - DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status)); + DPRINTFN(1,("uhci_abort_xfer: xfer=%p, xfer->status=%d, status=%d\n", + xfer, xfer->status, status)); - /* Make interrupt routine ignore it, */ + s = splusb(); + + /* Transfer is already done. */ + if (xfer->status != USBD_NOT_STARTED && + xfer->status != USBD_IN_PROGRESS) { + splx(s); + return; + } + + /* Give xfer the requested abort code. */ xfer->status = status; - /* don't timeout, */ - usb_untimeout(uhci_timeout, ii, ii->timeout_handle); + /* If already aborting, bail out early. */ + if (upipe->aborting) { + /* Unlink the xfer from HC */ + /*XXX only one xfer for now*/ + printf("uhci_abort_xfer: abort while aborting\n"); + /* Finalize xfer. */ + usb_transfer_complete(xfer); + splx(s); + return; + } - /* make hardware ignore it, */ - for (std = ii->stdstart; std != 0; std = std->link.std) - std->td.td_status &= LE(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + upipe->aborting = 1; + upipe->abortstart = SIMPLEQ_NEXT(xfer, next); + upipe->abortend = NULL; /* XXX only one xfer for now */ - xfer->hcpriv = ii; + /* Remove QH(s) from HC schedule. */ + uhci_abort_unlink_qh(upipe); -#if 1 - /* Make sure hardware has completed, */ - if (xfer->device->bus->intr_context) { - /* We have no process context, so we can't use tsleep(). */ - timeout(uhci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND); - } else { -#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__) - KASSERT(intr_nesting_level == 0, - ("ohci_abort_req in interrupt context")); -#endif - usb_delay_ms(xfer->pipe->device->bus, 1); - /* and call final part of interrupt handler. */ - uhci_abort_xfer_end(xfer); + /* Remove intr_info from list is done by usb_transfer_complete() .*/ + + /* Disable active bit. */ + maxlen = 0; + for (std = ii->stdstart; std != NULL; std = std->link.std) { + std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + len = UHCI_TD_GET_MAXLEN(std->td.td_token); + if (len > maxlen) + maxlen = len; } -#else - delay(1000); - uhci_abort_xfer_end(xfer); + /* compute delay in us */ + if (upipe->pipe.device->lowspeed) + maxlen *= 6; + /* wait for HC to complete TDs */ + delay(maxlen); + + /* Don't timeout, */ + usb_uncallout(xfer->timeout_handle, uhci_timeout, ii); + +#ifdef DIAGNOSTIC + UXFER(xfer)->iinfo.isdone = 1; #endif + /* Run callback and remove from interrupt list. */ + usb_transfer_complete(xfer); + + /* Set up final processing. */ + usb_callout(xfer->pipe->abort_handle, hz / USB_FRAMES_PER_SECOND, + uhci_abort_xfer_end, upipe); + + /* And return. */ + splx(s); } void uhci_abort_xfer_end(v) void *v; { - usbd_xfer_handle xfer = v; + struct uhci_pipe *upipe = v; + usbd_xfer_handle xf; + uhci_soft_td_t *std; + uhci_soft_qh_t *sqh, **qhs; int s; + int i, nqhs; + + DPRINTFN(5,("uhci_abort_xfer_end: upipe=%p\n", upipe)); + + switch (UE_GET_XFERTYPE(upipe->pipe.endpoint->edesc->bmAttributes)) { + case UE_CONTROL: +#if 0 + qhs = &upipe->u.ctl.sqh; + nqhs = 1; +#else +/* only one ctl transfer; qh unlinked by usb_transfer_complete() */ + nqhs = 0; +#endif + break; + case UE_ISOCHRONOUS: + printf("uhci_abort_xfer_end: iso\n"); + nqhs = 0; + break; + case UE_BULK: + qhs = &upipe->u.bulk.sqh; + nqhs = 1; + break; + case UE_INTERRUPT: + qhs = upipe->u.intr.qhs; + nqhs = upipe->u.intr.npoll; + break; + } s = splusb(); - usb_transfer_complete(xfer); + + for (i = 0; i < nqhs; i++) { + sqh = qhs[i]; + /* Check if inside remaining TD chain. */ + for (xf = upipe->abortstart; xf != NULL; + xf = SIMPLEQ_NEXT(xf, next)) { + for (std = UXFER(xf)->iinfo.stdstart; std != NULL; + std = std->link.std) { + if (std->physaddr == le32toh(sqh->qh.qh_elink)) + goto outside; + } + if (xf == upipe->abortend) + break; + } + if (upipe->abortstart != NULL) { + std = UXFER(upipe->abortstart)->iinfo.stdstart; + DPRINTFN(5,("uhci_abort_xfer_end: new std=%p\n", std)); + sqh->elink = std; + sqh->qh.qh_elink = htole32(std->physaddr); + } else { + DPRINTFN(5,("uhci_abort_xfer_end: new std=NULL\n")); + sqh->elink = NULL; + sqh->qh.qh_elink = htole32(UHCI_PTR_T); + } + } + +outside: + + /* Insert QH again. */ + uhci_abort_relink_qh(upipe); + + /* No longer aborting */ + upipe->aborting = 0; + + splx(s); +} + +void +uhci_abort_unlink_qh(upipe) + struct uhci_pipe *upipe; +{ + uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; + uhci_soft_qh_t *sqh, *pqh, **qhs; + int i, npoll; + + DPRINTFN(5,("uhci_abort_unlink_qh: sc=%p pipe=%p\n", sc, upipe)); + + switch (UE_GET_XFERTYPE(upipe->pipe.endpoint->edesc->bmAttributes)) { + case UE_CONTROL: +#if 0 +/* At the moment the done routine removes the QH */ + sqh = upipe->u.ctl.sqh; + pqh = uhci_find_prev_qh(sc->sc_ctl_start, sqh); + pqh->qh.qh_hlink = sqh->qh.qh_hlink; +#endif + break; +#ifdef DIAGNOSTIC + case UE_ISOCHRONOUS: + printf("uhci_abort_unlink_qh: iso\n"); + break; +#endif + case UE_BULK: +#if 0 +/* At the moment the done routine removes the QH */ + sqh = upipe->u.bulk.sqh; + pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh); + pqh->qh.qh_hlink = sqh->qh.qh_hlink; +#endif + break; + case UE_INTERRUPT: + npoll = upipe->u.intr.npoll; + qhs = upipe->u.intr.qhs; + for (i = 0; i < npoll; i++) { + sqh = qhs[i]; + pqh = uhci_find_prev_qh(sc->sc_vframes[sqh->pos].hqh, + sqh); + pqh->qh.qh_hlink = sqh->qh.qh_hlink; + } + break; + } +} + +void +uhci_abort_relink_qh(upipe) + struct uhci_pipe *upipe; +{ + uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; + uhci_soft_qh_t *sqh, *pqh, **qhs; + int i, npoll; + + switch (UE_GET_XFERTYPE(upipe->pipe.endpoint->edesc->bmAttributes)) { + case UE_CONTROL: +#if 0 +/* At the moment the done routine removes the QH */ + sqh = upipe->u.ctl.sqh; + pqh = uhci_find_prev_qh(sc->sc_ctl_start, sqh); + pqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_Q); +#endif + break; +#ifdef DIAGNOSTIC + case UE_ISOCHRONOUS: + printf("uhci_abort_relink_qh: iso\n"); + break; +#endif + case UE_BULK: +#if 0 +/* At the moment the done routine removes the QH */ + sqh = upipe->u.bulk.sqh; + pqh = uhci_find_prev_qh(sc->sc_bulk_start, sqh); + pqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_Q); +#endif + break; + case UE_INTERRUPT: + npoll = upipe->u.intr.npoll; + qhs = upipe->u.intr.qhs; + for (i = 0; i < npoll; i++) { + sqh = qhs[i]; + pqh = uhci_find_prev_qh(sc->sc_vframes[sqh->pos].hqh, + sqh); + pqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_Q); + } + break; + } +} + +void +uhci_cancel_abort(pipe) + usbd_pipe_handle pipe; +{ + struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; + int s; + + s = splusb(); + if (upipe->aborting) { + usb_uncallout(pipe->abort_handle, uhci_abort_xfer_end, upipe); + upipe->aborting = 0; + } splx(s); } + /* Close a device bulk pipe. */ void uhci_device_bulk_close(pipe) @@ -1720,9 +2006,8 @@ uhci_device_bulk_close(pipe) usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; + uhci_cancel_abort(pipe); uhci_free_sqh(sc, upipe->u.bulk.sqh); - uhci_free_intr_info(upipe->iinfo); - /* XXX free other resources? */ } usbd_status @@ -1736,8 +2021,9 @@ uhci_device_ctrl_transfer(xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -1777,8 +2063,9 @@ uhci_device_intr_transfer(xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -1790,7 +2077,7 @@ uhci_device_intr_start(xfer) struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; usbd_device_handle dev = upipe->pipe.device; uhci_softc_t *sc = (uhci_softc_t *)dev->bus; - uhci_intr_info_t *ii = upipe->iinfo; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_soft_td_t *data, *dataend; uhci_soft_qh_t *sqh; usbd_status err; @@ -1808,10 +2095,10 @@ uhci_device_intr_start(xfer) #endif err = uhci_alloc_std_chain(upipe, sc, xfer->length, 1, xfer->flags, - &xfer->dmabuf, &data, &dataend); + &xfer->dmabuf, &data, &dataend); if (err) return (err); - dataend->td.td_status |= LE(UHCI_TD_IOC); + dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef UHCI_DEBUG if (uhcidebug > 10) { @@ -1826,9 +2113,6 @@ uhci_device_intr_start(xfer) ii->xfer = xfer; ii->stdstart = data; ii->stdend = dataend; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_intr_transfer: not done, ii=%p\n", ii); @@ -1841,8 +2125,10 @@ uhci_device_intr_start(xfer) for (i = 0; i < upipe->u.intr.npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->elink = data; - sqh->qh.qh_elink = LE(data->physaddr); + sqh->qh.qh_elink = htole32(data->physaddr); } + uhci_add_intr_info(sc, ii); + xfer->status = USBD_IN_PROGRESS; splx(s); #ifdef UHCI_DEBUG @@ -1870,10 +2156,7 @@ void uhci_device_ctrl_close(pipe) usbd_pipe_handle pipe; { - struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; - - uhci_free_intr_info(upipe->iinfo); - /* XXX free other resources */ + uhci_cancel_abort(pipe); } /* Abort a device interrupt request. */ @@ -1896,17 +2179,17 @@ uhci_device_intr_close(pipe) { struct uhci_pipe *upipe = (struct uhci_pipe *)pipe; uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; - int i, s, npoll; + int i, npoll; + int s; - upipe->iinfo->stdstart = 0; /* inactive */ + uhci_cancel_abort(pipe); /* Unlink descriptors from controller data structures. */ npoll = upipe->u.intr.npoll; - uhci_lock_frames(sc); + s = splusb(); for (i = 0; i < npoll; i++) - uhci_remove_intr(sc, upipe->u.intr.qhs[i]->pos, - upipe->u.intr.qhs[i]); - uhci_unlock_frames(sc); + uhci_remove_intr(sc, upipe->u.intr.qhs[i]); + splx(s); /* * We now have to wait for any activity on the physical @@ -1918,11 +2201,6 @@ uhci_device_intr_close(pipe) uhci_free_sqh(sc, upipe->u.intr.qhs[i]); free(upipe->u.intr.qhs, M_USBHC); - s = splusb(); - LIST_REMOVE(upipe->iinfo, list); /* remove from active list */ - splx(s); - uhci_free_intr_info(upipe->iinfo); - /* XXX free other resources */ } @@ -1936,7 +2214,7 @@ uhci_device_request(xfer) uhci_softc_t *sc = (uhci_softc_t *)dev->bus; int addr = dev->address; int endpt = upipe->pipe.endpoint->edesc->bEndpointAddress; - uhci_intr_info_t *ii = upipe->iinfo; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_soft_td_t *setup, *data, *stat, *next, *dataend; uhci_soft_qh_t *sqh; int len; @@ -1963,12 +2241,12 @@ uhci_device_request(xfer) if (len != 0) { upipe->nexttoggle = 1; err = uhci_alloc_std_chain(upipe, sc, len, isread, xfer->flags, - &xfer->dmabuf, &data, &dataend); + &xfer->dmabuf, &data, &dataend); if (err) return (err); next = data; dataend->link.std = stat; - dataend->td.td_link = LE(stat->physaddr | UHCI_PTR_VF); + dataend->td.td_link = htole32(stat->physaddr | UHCI_PTR_VF); } else { next = stat; } @@ -1977,19 +2255,20 @@ uhci_device_request(xfer) memcpy(KERNADDR(&upipe->u.ctl.reqdma), req, sizeof *req); setup->link.std = next; - setup->td.td_link = LE(next->physaddr | UHCI_PTR_VF); - setup->td.td_status = LE(UHCI_TD_SET_ERRCNT(3) | ls | UHCI_TD_ACTIVE); - setup->td.td_token = LE(UHCI_TD_SETUP(sizeof *req, endpt, addr)); - setup->td.td_buffer = LE(DMAADDR(&upipe->u.ctl.reqdma)); - - stat->link.std = 0; - stat->td.td_link = LE(UHCI_PTR_T); - stat->td.td_status = LE(UHCI_TD_SET_ERRCNT(3) | ls | + setup->td.td_link = htole32(next->physaddr | UHCI_PTR_VF); + setup->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls | + UHCI_TD_ACTIVE); + setup->td.td_token = htole32(UHCI_TD_SETUP(sizeof *req, endpt, addr)); + setup->td.td_buffer = htole32(DMAADDR(&upipe->u.ctl.reqdma)); + + stat->link.std = NULL; + stat->td.td_link = htole32(UHCI_PTR_T); + stat->td.td_status = htole32(UHCI_TD_SET_ERRCNT(3) | ls | UHCI_TD_ACTIVE | UHCI_TD_IOC); stat->td.td_token = - LE(isread ? UHCI_TD_OUT(0, endpt, addr, 1) : - UHCI_TD_IN (0, endpt, addr, 1)); - stat->td.td_buffer = LE(0); + htole32(isread ? UHCI_TD_OUT(0, endpt, addr, 1) : + UHCI_TD_IN (0, endpt, addr, 1)); + stat->td.td_buffer = htole32(0); #ifdef UHCI_DEBUG if (uhcidebug > 10) { @@ -2002,9 +2281,6 @@ uhci_device_request(xfer) ii->xfer = xfer; ii->stdstart = setup; ii->stdend = stat; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_request: not done, ii=%p\n", ii); @@ -2013,12 +2289,11 @@ uhci_device_request(xfer) #endif sqh->elink = setup; - sqh->qh.qh_elink = LE(setup->physaddr); - sqh->intr_info = ii; + sqh->qh.qh_elink = htole32(setup->physaddr); s = splusb(); uhci_add_ctrl(sc, sqh); - LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); + uhci_add_intr_info(sc, ii); #ifdef UHCI_DEBUG if (uhcidebug > 12) { uhci_soft_td_t *std; @@ -2030,7 +2305,7 @@ uhci_device_request(xfer) for (std = sc->sc_vframes[0].htd, link = 0; (link & UHCI_PTR_Q) == 0; std = std->link.std) { - link = LE(std->td.td_link); + link = le32toh(std->td.td_link); uhci_dump_td(std); } sxqh = (uhci_soft_qh_t *)std; @@ -2047,9 +2322,10 @@ uhci_device_request(xfer) } #endif if (xfer->timeout && !sc->sc_bus.use_polling) { - usb_timeout(uhci_timeout, ii, - MS_TO_TICKS(xfer->timeout), ii->timeout_handle); + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + uhci_timeout, ii); } + xfer->status = USBD_IN_PROGRESS; splx(s); return (USBD_NORMAL_COMPLETION); @@ -2103,6 +2379,7 @@ uhci_device_isoc_enter(xfer) if (xfer->status == USBD_IN_PROGRESS) { /* This request has already been entered into the frame list */ + printf("uhci_device_isoc_enter: xfer=%p in frame list\n", xfer); /* XXX */ } @@ -2119,12 +2396,12 @@ uhci_device_isoc_enter(xfer) } xfer->status = USBD_IN_PROGRESS; - xfer->hcprivint = next; + UXFER(xfer)->curframe = next; buf = DMAADDR(&xfer->dmabuf); - status = LE(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | - UHCI_TD_ACTIVE | - UHCI_TD_IOS)); + status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(0) | + UHCI_TD_ACTIVE | + UHCI_TD_IOS); nframes = xfer->nframes; s = splusb(); for (i = 0; i < nframes; i++) { @@ -2132,12 +2409,12 @@ uhci_device_isoc_enter(xfer) if (++next >= UHCI_VFRAMELIST_COUNT) next = 0; len = xfer->frlengths[i]; - std->td.td_buffer = LE(buf); + std->td.td_buffer = htole32(buf); if (i == nframes - 1) - status |= LE(UHCI_TD_IOC); - std->td.td_status = status; - std->td.td_token &= LE(~UHCI_TD_MAXLEN_MASK); - std->td.td_token |= LE(UHCI_TD_SET_MAXLEN(len)); + status |= UHCI_TD_IOC; + std->td.td_status = htole32(status); + std->td.td_token &= htole32(~UHCI_TD_MAXLEN_MASK); + std->td.td_token |= htole32(UHCI_TD_SET_MAXLEN(len)); #ifdef UHCI_DEBUG if (uhcidebug > 5) { DPRINTFN(5,("uhci_device_isoc_enter: TD %d\n", i)); @@ -2158,10 +2435,12 @@ uhci_device_isoc_start(xfer) { struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus; - uhci_intr_info_t *ii = upipe->iinfo; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_soft_td_t *end; int s, i; + DPRINTFN(5,("uhci_device_isoc_start: xfer=%p\n", xfer)); + if (sc->sc_dying) return (USBD_IOERROR); @@ -2171,27 +2450,31 @@ uhci_device_isoc_start(xfer) #endif /* Find the last TD */ - i = xfer->hcprivint + xfer->nframes; + i = UXFER(xfer)->curframe + xfer->nframes; if (i >= UHCI_VFRAMELIST_COUNT) i -= UHCI_VFRAMELIST_COUNT; end = upipe->u.iso.stds[i]; +#ifdef DIAGNOSTIC + if (end == NULL) { + printf("uhci_device_isoc_start: end == NULL\n"); + return (USBD_INVAL); + } +#endif + s = splusb(); /* Set up interrupt info. */ ii->xfer = xfer; ii->stdstart = end; ii->stdend = end; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_isoc_start: not done, ii=%p\n", ii); } ii->isdone = 0; #endif - LIST_INSERT_HEAD(&sc->sc_intrhead, ii, list); + uhci_add_intr_info(sc, ii); splx(s); @@ -2203,35 +2486,46 @@ uhci_device_isoc_abort(xfer) usbd_xfer_handle xfer; { struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; - uhci_intr_info_t *ii = upipe->iinfo; uhci_soft_td_t **stds = upipe->u.iso.stds; uhci_soft_td_t *std; - int i, n, nframes; + int i, n, s, nframes, maxlen, len; + + s = splusb(); + + /* Transfer is already done. */ + if (xfer->status != USBD_NOT_STARTED && + xfer->status != USBD_IN_PROGRESS) { + splx(s); + return; + } - /* Make interrupt routine ignore it, */ + /* Give xfer the requested abort code. */ xfer->status = USBD_CANCELLED; /* make hardware ignore it, */ nframes = xfer->nframes; - n = xfer->hcprivint; + n = UXFER(xfer)->curframe; + maxlen = 0; for (i = 0; i < nframes; i++) { std = stds[n]; - std->td.td_status &= LE(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); + len = UHCI_TD_GET_MAXLEN(std->td.td_token); + if (len > maxlen) + maxlen = len; if (++n >= UHCI_VFRAMELIST_COUNT) n = 0; } - xfer->hcpriv = ii; + /* and wait until we are sure the hardware has finished. */ + delay(maxlen); - /* make sure hardware has completed, */ - if (xfer->device->bus->intr_context) { - /* We have no process context, so we can't use tsleep(). */ - timeout(uhci_abort_xfer_end, xfer, hz / USB_FRAMES_PER_SECOND); - } else { - usb_delay_ms(xfer->pipe->device->bus, 1); - /* and call final part of interrupt handler. */ - uhci_abort_xfer_end(xfer); - } +#ifdef DIAGNOSTIC + UXFER(xfer)->iinfo.isdone = 1; +#endif + /* Run callback and remove from interrupt list. */ + usb_transfer_complete(xfer); + + splx(s); } void @@ -2243,7 +2537,7 @@ uhci_device_isoc_close(pipe) uhci_softc_t *sc = (uhci_softc_t *)dev->bus; uhci_soft_td_t *std, *vstd; struct iso *iso; - int i; + int i, s; /* * Make sure all TDs are marked as inactive. @@ -2254,10 +2548,10 @@ uhci_device_isoc_close(pipe) iso = &upipe->u.iso; for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) - iso->stds[i]->td.td_status &= LE(~UHCI_TD_ACTIVE); + iso->stds[i]->td.td_status &= htole32(~UHCI_TD_ACTIVE); usb_delay_ms(&sc->sc_bus, 2); /* wait for completion */ - uhci_lock_frames(sc); + s = splusb(); for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { std = iso->stds[i]; for (vstd = sc->sc_vframes[i].htd; @@ -2267,14 +2561,14 @@ uhci_device_isoc_close(pipe) if (vstd == NULL) { /*panic*/ printf("uhci_device_isoc_close: %p not found\n", std); - uhci_unlock_frames(sc); + splx(s); return; } vstd->link = std->link; vstd->td.td_link = std->td.td_link; uhci_free_std(sc, std); } - uhci_unlock_frames(sc); + splx(s); free(iso->stds, M_USBHC); } @@ -2292,36 +2586,36 @@ uhci_setup_isoc(pipe) uhci_soft_td_t *std, *vstd; u_int32_t token; struct iso *iso; - int i; + int i, s; iso = &upipe->u.iso; iso->stds = malloc(UHCI_VFRAMELIST_COUNT * sizeof (uhci_soft_td_t *), M_USBHC, M_WAITOK); - token = LE(rd ? UHCI_TD_IN (0, endpt, addr, 0) : - UHCI_TD_OUT(0, endpt, addr, 0)); + token = rd ? UHCI_TD_IN (0, endpt, addr, 0) : + UHCI_TD_OUT(0, endpt, addr, 0); /* Allocate the TDs and mark as inactive; */ for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { std = uhci_alloc_std(sc); if (std == 0) goto bad; - std->td.td_status = LE(UHCI_TD_IOS); /* iso, inactive */ - std->td.td_token = token; + std->td.td_status = htole32(UHCI_TD_IOS); /* iso, inactive */ + std->td.td_token = htole32(token); iso->stds[i] = std; } /* Insert TDs into schedule. */ - uhci_lock_frames(sc); + s = splusb(); for (i = 0; i < UHCI_VFRAMELIST_COUNT; i++) { std = iso->stds[i]; vstd = sc->sc_vframes[i].htd; std->link = vstd->link; std->td.td_link = vstd->td.td_link; vstd->link.std = std; - vstd->td.td_link = LE(std->physaddr); + vstd->td.td_link = htole32(std->physaddr); } - uhci_unlock_frames(sc); + splx(s); iso->next = -1; iso->inuse = 0; @@ -2339,21 +2633,41 @@ void uhci_device_isoc_done(xfer) usbd_xfer_handle xfer; { - uhci_intr_info_t *ii = xfer->hcpriv; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; DPRINTFN(4, ("uhci_isoc_done: length=%d\n", xfer->actlen)); + if (ii->xfer != xfer) + /* Not on interrupt list, ignore it. */ + return; + +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_BUSY) { + printf("uhci_device_isoc_done: xfer=%p not busy 0x%08x\n", + xfer, xfer->busy_free); + return; + } + + if (ii->stdend == NULL) { + printf("uhci_device_isoc_done: xfer=%p stdend==NULL\n", xfer); +#ifdef UHCI_DEBUG + uhci_dump_ii(ii); +#endif + return; + } +#endif + /* Turn off the interrupt since it is active even if the TD is not. */ - ii->stdend->td.td_status &= LE(~UHCI_TD_IOC); + ii->stdend->td.td_status &= htole32(~UHCI_TD_IOC); - LIST_REMOVE(ii, list); /* remove from active list */ + uhci_del_intr_info(ii); /* remove from active list */ } void uhci_device_intr_done(xfer) usbd_xfer_handle xfer; { - uhci_intr_info_t *ii = xfer->hcpriv; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_softc_t *sc = ii->sc; struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; uhci_soft_qh_t *sqh; @@ -2365,7 +2679,7 @@ uhci_device_intr_done(xfer) for(i = 0; i < npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->elink = 0; - sqh->qh.qh_elink = LE(UHCI_PTR_T); + sqh->qh.qh_elink = htole32(UHCI_PTR_T); } uhci_free_std_chain(sc, ii->stdstart, 0); @@ -2373,10 +2687,12 @@ uhci_device_intr_done(xfer) if (xfer->pipe->repeat) { uhci_soft_td_t *data, *dataend; + DPRINTFN(5,("uhci_device_intr_done: requeing\n")); + /* This alloc cannot fail since we freed the chain above. */ uhci_alloc_std_chain(upipe, sc, xfer->length, 1, xfer->flags, &xfer->dmabuf, &data, &dataend); - dataend->td.td_status |= LE(UHCI_TD_IOC); + dataend->td.td_status |= htole32(UHCI_TD_IOC); #ifdef UHCI_DEBUG if (uhcidebug > 10) { @@ -2388,9 +2704,6 @@ uhci_device_intr_done(xfer) ii->stdstart = data; ii->stdend = dataend; -#if defined(__FreeBSD__) - callout_handle_init(&ii->timeout_handle); -#endif #ifdef DIAGNOSTIC if (!ii->isdone) { printf("uhci_device_intr_done: not done, ii=%p\n", ii); @@ -2400,10 +2713,13 @@ uhci_device_intr_done(xfer) for (i = 0; i < npoll; i++) { sqh = upipe->u.intr.qhs[i]; sqh->elink = data; - sqh->qh.qh_elink = LE(data->physaddr); + sqh->qh.qh_elink = htole32(data->physaddr); } + xfer->status = USBD_IN_PROGRESS; + /* The ii is already on the examined list, just leave it. */ } else { - ii->stdstart = 0; /* mark as inactive */ + DPRINTFN(5,("uhci_device_intr_done: removing\n")); + uhci_del_intr_info(ii); } } @@ -2412,7 +2728,7 @@ void uhci_device_ctrl_done(xfer) usbd_xfer_handle xfer; { - uhci_intr_info_t *ii = xfer->hcpriv; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_softc_t *sc = ii->sc; struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; @@ -2421,7 +2737,7 @@ uhci_device_ctrl_done(xfer) panic("uhci_ctrl_done: not a request\n"); #endif - LIST_REMOVE(ii, list); /* remove from active list */ + uhci_del_intr_info(ii); /* remove from active list */ uhci_remove_ctrl(sc, upipe->u.ctl.sqh); @@ -2436,11 +2752,11 @@ void uhci_device_bulk_done(xfer) usbd_xfer_handle xfer; { - uhci_intr_info_t *ii = xfer->hcpriv; + uhci_intr_info_t *ii = &UXFER(xfer)->iinfo; uhci_softc_t *sc = ii->sc; struct uhci_pipe *upipe = (struct uhci_pipe *)xfer->pipe; - LIST_REMOVE(ii, list); /* remove from active list */ + uhci_del_intr_info(ii); /* remove from active list */ uhci_remove_bulk(sc, upipe->u.bulk.sqh); @@ -2451,45 +2767,36 @@ uhci_device_bulk_done(xfer) /* Add interrupt QH, called with vflock. */ void -uhci_add_intr(sc, n, sqh) +uhci_add_intr(sc, sqh) uhci_softc_t *sc; - int n; uhci_soft_qh_t *sqh; { - struct uhci_vframe *vf = &sc->sc_vframes[n]; + struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos]; uhci_soft_qh_t *eqh; - DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", n, sqh)); + DPRINTFN(4, ("uhci_add_intr: n=%d sqh=%p\n", sqh->pos, sqh)); + eqh = vf->eqh; sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; eqh->hlink = sqh; - eqh->qh.qh_hlink = LE(sqh->physaddr | UHCI_PTR_Q); + eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_Q); vf->eqh = sqh; vf->bandwidth++; } /* Remove interrupt QH, called with vflock. */ void -uhci_remove_intr(sc, n, sqh) +uhci_remove_intr(sc, sqh) uhci_softc_t *sc; - int n; uhci_soft_qh_t *sqh; { - struct uhci_vframe *vf = &sc->sc_vframes[n]; + struct uhci_vframe *vf = &sc->sc_vframes[sqh->pos]; uhci_soft_qh_t *pqh; - DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", n, sqh)); + DPRINTFN(4, ("uhci_remove_intr: n=%d sqh=%p\n", sqh->pos, sqh)); - for (pqh = vf->hqh; pqh->hlink != sqh; pqh = pqh->hlink) -#if defined(DIAGNOSTIC) || defined(UHCI_DEBUG) - if (LE(pqh->qh.qh_hlink) & UHCI_PTR_T) { - DPRINTF(("uhci_remove_intr: QH not found\n")); - return; - } -#else - ; -#endif + pqh = uhci_find_prev_qh(vf->hqh, sqh); pqh->hlink = sqh->hlink; pqh->qh.qh_hlink = sqh->qh.qh_hlink; if (vf->eqh == sqh) @@ -2537,26 +2844,19 @@ uhci_device_setintr(sc, upipe, ival) } DPRINTFN(1, ("uhci_setintr: bw=%d offs=%d\n", bestbw, bestoffs)); - upipe->iinfo->stdstart = 0; for(i = 0; i < npoll; i++) { upipe->u.intr.qhs[i] = sqh = uhci_alloc_sqh(sc); sqh->elink = 0; - sqh->qh.qh_elink = LE(UHCI_PTR_T); + sqh->qh.qh_elink = htole32(UHCI_PTR_T); sqh->pos = MOD(i * ival + bestoffs); - sqh->intr_info = upipe->iinfo; } #undef MOD s = splusb(); - LIST_INSERT_HEAD(&sc->sc_intrhead, upipe->iinfo, list); - splx(s); - - uhci_lock_frames(sc); /* Enter QHs into the controller data structures. */ for(i = 0; i < npoll; i++) - uhci_add_intr(sc, upipe->u.intr.qhs[i]->pos, - upipe->u.intr.qhs[i]); - uhci_unlock_frames(sc); + uhci_add_intr(sc, upipe->u.intr.qhs[i]); + splx(s); DPRINTFN(5, ("uhci_setintr: returns %p\n", upipe)); return (USBD_NORMAL_COMPLETION); @@ -2576,6 +2876,10 @@ uhci_open(pipe) DPRINTFN(1, ("uhci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", pipe, pipe->device->address, ed->bEndpointAddress, sc->sc_addr)); + + upipe->aborting = 0; + upipe->nexttoggle = 0; + if (pipe->device->address == sc->sc_addr) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: @@ -2588,9 +2892,6 @@ uhci_open(pipe) return (USBD_INVAL); } } else { - upipe->iinfo = uhci_alloc_intr_info(sc); - if (upipe->iinfo == 0) - return (USBD_NOMEM); switch (ed->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &uhci_device_ctrl_methods; @@ -2609,8 +2910,8 @@ uhci_open(pipe) goto bad; } err = usb_allocmem(&sc->sc_bus, - sizeof(usb_device_request_t), - 0, &upipe->u.ctl.reqdma); + sizeof(usb_device_request_t), + 0, &upipe->u.ctl.reqdma); if (err) { uhci_free_sqh(sc, upipe->u.ctl.sqh); uhci_free_std(sc, upipe->u.ctl.setup); @@ -2638,7 +2939,6 @@ uhci_open(pipe) return (USBD_NORMAL_COMPLETION); bad: - uhci_free_intr_info(upipe->iinfo); return (USBD_NOMEM); } @@ -2736,8 +3036,9 @@ uhci_root_ctrl_transfer(xfer) if (err) return (err); - /* Pipe isn't running (otherwise err would be USBD_INPROG), - * start first + /* + * Pipe isn't running (otherwise err would be USBD_INPROG), + * so start it first. */ return (uhci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -3067,7 +3368,6 @@ uhci_root_ctrl_start(xfer) err = USBD_NORMAL_COMPLETION; ret: xfer->status = err; - xfer->hcpriv = 0; s = splusb(); usb_transfer_complete(xfer); splx(s); @@ -3097,14 +3397,17 @@ uhci_root_intr_abort(xfer) { uhci_softc_t *sc = (uhci_softc_t *)xfer->pipe->device->bus; - usb_untimeout(uhci_timo, xfer, xfer->timo_handle); - sc->sc_has_timo = NULL; + usb_uncallout(sc->sc_poll_handle, uhci_poll_hub, xfer); + sc->sc_intr_xfer = NULL; if (xfer->pipe->intrxfer == xfer) { DPRINTF(("uhci_root_intr_abort: remove\n")); xfer->pipe->intrxfer = 0; } xfer->status = USBD_CANCELLED; +#ifdef DIAGNOSTIC + UXFER(xfer)->iinfo.isdone = 1; +#endif usb_transfer_complete(xfer); } @@ -3140,8 +3443,8 @@ uhci_root_intr_start(xfer) return (USBD_IOERROR); sc->sc_ival = MS_TO_TICKS(xfer->pipe->endpoint->edesc->bInterval); - usb_timeout(uhci_timo, xfer, sc->sc_ival, xfer->timo_handle); - sc->sc_has_timo = xfer; + usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); + sc->sc_intr_xfer = xfer; return (USBD_IN_PROGRESS); } @@ -3152,8 +3455,7 @@ uhci_root_intr_close(pipe) { uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus; - usb_untimeout(uhci_timo, pipe->intrxfer, pipe->intrxfer->timo_handle); - sc->sc_has_timo = NULL; + usb_uncallout(sc->sc_poll_handle, uhci_poll_hub, sc->sc_intr_xfer); + sc->sc_intr_xfer = NULL; DPRINTF(("uhci_root_intr_close\n")); } - -- cgit v1.2.3