Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/dev/usb/ehci.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/dev/usb/ehci.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.228 retrieving revision 1.228.2.1 diff -u -p -r1.228 -r1.228.2.1 --- src/sys/dev/usb/ehci.c 2014/08/05 10:33:46 1.228 +++ src/sys/dev/usb/ehci.c 2017/04/05 19:54:19 1.228.2.1 @@ -1,4 +1,4 @@ -/* $NetBSD: ehci.c,v 1.228 2014/08/05 10:33:46 skrll Exp $ */ +/* $NetBSD: ehci.c,v 1.228.2.1 2017/04/05 19:54:19 snj Exp $ */ /* * Copyright (c) 2004-2012 The NetBSD Foundation, Inc. @@ -53,53 +53,83 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.228 2014/08/05 10:33:46 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.228.2.1 2017/04/05 19:54:19 snj Exp $"); #include "ohci.h" #include "uhci.h" +#ifdef _KERNEL_OPT +#include "opt_usb.h" +#endif + #include -#include + +#include +#include +#include #include #include -#include -#include +#include #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include +#include #include #include #include #include -#include +#include -#ifdef EHCI_DEBUG -static void __printflike(1, 2) -ehciprintf(const char *fmt, ...) + +#ifdef USB_DEBUG +#ifndef EHCI_DEBUG +#define ehcidebug 0 +#else +static int ehcidebug = 0; + +SYSCTL_SETUP(sysctl_hw_ehci_setup, "sysctl hw.ehci setup") { - va_list ap; + int err; + const struct sysctlnode *rnode; + const struct sysctlnode *cnode; + + err = sysctl_createv(clog, 0, NULL, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "ehci", + SYSCTL_DESCR("ehci global controls"), + NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); + + if (err) + goto fail; + + /* control debugging printfs */ + err = sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "debug", SYSCTL_DESCR("Enable debugging output"), + NULL, 0, &ehcidebug, sizeof(ehcidebug), CTL_CREATE, CTL_EOL); + if (err) + goto fail; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); + return; +fail: + aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); } -#define DPRINTF(x) do { if (ehcidebug) ehciprintf x; } while(0) -#define DPRINTFN(n,x) do { if (ehcidebug>(n)) ehciprintf x; } while (0) -int ehcidebug = 0; -#else -#define DPRINTF(x) -#define DPRINTFN(n,x) -#endif +#endif /* EHCI_DEBUG */ +#endif /* USB_DEBUG */ + +#define DPRINTF(FMT,A,B,C,D) USBHIST_LOG(ehcidebug,FMT,A,B,C,D) +#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(ehcidebug,N,FMT,A,B,C,D) +#define EHCIHIST_FUNC() USBHIST_FUNC() +#define EHCIHIST_CALLED() USBHIST_CALLED(ehcidebug) struct ehci_pipe { struct usbd_pipe pipe; @@ -107,115 +137,138 @@ struct ehci_pipe { ehci_soft_qh_t *sqh; union { - ehci_soft_qtd_t *qtd; - /* ehci_soft_itd_t *itd; */ - } tail; - union { /* Control pipe */ struct { usb_dma_t reqdma; - } ctl; + } ctrl; /* Interrupt pipe */ struct { u_int length; } intr; - /* Bulk pipe */ - struct { - u_int length; - } bulk; /* Iso pipe */ struct { u_int next_frame; u_int cur_xfers; } isoc; - } u; + }; }; -Static usbd_status ehci_open(usbd_pipe_handle); +typedef TAILQ_HEAD(ex_completeq, ehci_xfer) ex_completeq_t; + +Static usbd_status ehci_open(struct usbd_pipe *); Static void ehci_poll(struct usbd_bus *); Static void ehci_softintr(void *); Static int ehci_intr1(ehci_softc_t *); -Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle); -Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *); -Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *); -Static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *); -Static void ehci_idone(struct ehci_xfer *); +Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *, + ex_completeq_t *); +Static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *, + ex_completeq_t *); +Static void ehci_check_sitd_intr(ehci_softc_t *, struct ehci_xfer *, + ex_completeq_t *); +Static void ehci_idone(struct ehci_xfer *, ex_completeq_t *); Static void ehci_timeout(void *); Static void ehci_timeout_task(void *); Static void ehci_intrlist_timeout(void *); Static void ehci_doorbell(void *); Static void ehci_pcd(void *); -Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t); -Static void ehci_freem(struct usbd_bus *, usb_dma_t *); +Static struct usbd_xfer * + ehci_allocx(struct usbd_bus *, unsigned int); +Static void ehci_freex(struct usbd_bus *, struct usbd_xfer *); -Static usbd_xfer_handle ehci_allocx(struct usbd_bus *); -Static void ehci_freex(struct usbd_bus *, usbd_xfer_handle); Static void ehci_get_lock(struct usbd_bus *, kmutex_t **); +Static int ehci_roothub_ctrl(struct usbd_bus *, + usb_device_request_t *, void *, int); -Static usbd_status ehci_root_ctrl_transfer(usbd_xfer_handle); -Static usbd_status ehci_root_ctrl_start(usbd_xfer_handle); -Static void ehci_root_ctrl_abort(usbd_xfer_handle); -Static void ehci_root_ctrl_close(usbd_pipe_handle); -Static void ehci_root_ctrl_done(usbd_xfer_handle); - -Static usbd_status ehci_root_intr_transfer(usbd_xfer_handle); -Static usbd_status ehci_root_intr_start(usbd_xfer_handle); -Static void ehci_root_intr_abort(usbd_xfer_handle); -Static void ehci_root_intr_close(usbd_pipe_handle); -Static void ehci_root_intr_done(usbd_xfer_handle); - -Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle); -Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle); -Static void ehci_device_ctrl_abort(usbd_xfer_handle); -Static void ehci_device_ctrl_close(usbd_pipe_handle); -Static void ehci_device_ctrl_done(usbd_xfer_handle); - -Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle); -Static usbd_status ehci_device_bulk_start(usbd_xfer_handle); -Static void ehci_device_bulk_abort(usbd_xfer_handle); -Static void ehci_device_bulk_close(usbd_pipe_handle); -Static void ehci_device_bulk_done(usbd_xfer_handle); - -Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle); -Static usbd_status ehci_device_intr_start(usbd_xfer_handle); -Static void ehci_device_intr_abort(usbd_xfer_handle); -Static void ehci_device_intr_close(usbd_pipe_handle); -Static void ehci_device_intr_done(usbd_xfer_handle); - -Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle); -Static usbd_status ehci_device_isoc_start(usbd_xfer_handle); -Static void ehci_device_isoc_abort(usbd_xfer_handle); -Static void ehci_device_isoc_close(usbd_pipe_handle); -Static void ehci_device_isoc_done(usbd_xfer_handle); +Static usbd_status ehci_root_intr_transfer(struct usbd_xfer *); +Static usbd_status ehci_root_intr_start(struct usbd_xfer *); +Static void ehci_root_intr_abort(struct usbd_xfer *); +Static void ehci_root_intr_close(struct usbd_pipe *); +Static void ehci_root_intr_done(struct usbd_xfer *); + +Static int ehci_device_ctrl_init(struct usbd_xfer *); +Static void ehci_device_ctrl_fini(struct usbd_xfer *); +Static usbd_status ehci_device_ctrl_transfer(struct usbd_xfer *); +Static usbd_status ehci_device_ctrl_start(struct usbd_xfer *); +Static void ehci_device_ctrl_abort(struct usbd_xfer *); +Static void ehci_device_ctrl_close(struct usbd_pipe *); +Static void ehci_device_ctrl_done(struct usbd_xfer *); + +Static int ehci_device_bulk_init(struct usbd_xfer *); +Static void ehci_device_bulk_fini(struct usbd_xfer *); +Static usbd_status ehci_device_bulk_transfer(struct usbd_xfer *); +Static usbd_status ehci_device_bulk_start(struct usbd_xfer *); +Static void ehci_device_bulk_abort(struct usbd_xfer *); +Static void ehci_device_bulk_close(struct usbd_pipe *); +Static void ehci_device_bulk_done(struct usbd_xfer *); + +Static int ehci_device_intr_init(struct usbd_xfer *); +Static void ehci_device_intr_fini(struct usbd_xfer *); +Static usbd_status ehci_device_intr_transfer(struct usbd_xfer *); +Static usbd_status ehci_device_intr_start(struct usbd_xfer *); +Static void ehci_device_intr_abort(struct usbd_xfer *); +Static void ehci_device_intr_close(struct usbd_pipe *); +Static void ehci_device_intr_done(struct usbd_xfer *); + +Static int ehci_device_isoc_init(struct usbd_xfer *); +Static void ehci_device_isoc_fini(struct usbd_xfer *); +Static usbd_status ehci_device_isoc_transfer(struct usbd_xfer *); +Static void ehci_device_isoc_abort(struct usbd_xfer *); +Static void ehci_device_isoc_close(struct usbd_pipe *); +Static void ehci_device_isoc_done(struct usbd_xfer *); + +Static int ehci_device_fs_isoc_init(struct usbd_xfer *); +Static void ehci_device_fs_isoc_fini(struct usbd_xfer *); +Static usbd_status ehci_device_fs_isoc_transfer(struct usbd_xfer *); +Static void ehci_device_fs_isoc_abort(struct usbd_xfer *); +Static void ehci_device_fs_isoc_close(struct usbd_pipe *); +Static void ehci_device_fs_isoc_done(struct usbd_xfer *); -Static void ehci_device_clear_toggle(usbd_pipe_handle pipe); -Static void ehci_noop(usbd_pipe_handle pipe); +Static void ehci_device_clear_toggle(struct usbd_pipe *); +Static void ehci_noop(struct usbd_pipe *); Static void ehci_disown(ehci_softc_t *, int, int); -Static ehci_soft_qh_t *ehci_alloc_sqh(ehci_softc_t *); +Static ehci_soft_qh_t * ehci_alloc_sqh(ehci_softc_t *); Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *); -Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_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 usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *, - ehci_softc_t *, int, int, usbd_xfer_handle, - ehci_soft_qtd_t **, ehci_soft_qtd_t **); -Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *, - ehci_soft_qtd_t *); - -Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *sc); -Static void ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd); -Static void ehci_rem_free_itd_chain(ehci_softc_t *sc, - struct ehci_xfer *exfer); -Static void ehci_abort_isoc_xfer(usbd_xfer_handle xfer, - usbd_status status); +Static int ehci_alloc_sqtd_chain(ehci_softc_t *, + struct usbd_xfer *, int, int, ehci_soft_qtd_t **); +Static void ehci_free_sqtds(ehci_softc_t *, struct ehci_xfer *); + +Static void ehci_reset_sqtd_chain(ehci_softc_t *, struct usbd_xfer *, + int, int, int *, ehci_soft_qtd_t **); +Static void ehci_append_sqtd(ehci_soft_qtd_t *, ehci_soft_qtd_t *); -Static usbd_status ehci_device_request(usbd_xfer_handle xfer); +Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *); +Static ehci_soft_sitd_t * + ehci_alloc_sitd(ehci_softc_t *); + +Static void ehci_remove_itd_chain(ehci_softc_t *, ehci_soft_itd_t *); +Static void ehci_remove_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *); +Static void ehci_free_itd_chain(ehci_softc_t *, ehci_soft_itd_t *); +Static void ehci_free_sitd_chain(ehci_softc_t *, ehci_soft_sitd_t *); + +static inline void +ehci_free_itd_locked(ehci_softc_t *sc, ehci_soft_itd_t *itd) +{ + + LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list); +} + +static inline void +ehci_free_sitd_locked(ehci_softc_t *sc, ehci_soft_sitd_t *sitd) +{ + + LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list); +} + +Static void ehci_abort_isoc_xfer(struct usbd_xfer *, usbd_status); Static usbd_status ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *, - int ival); + int); Static void ehci_add_qh(ehci_softc_t *, ehci_soft_qh_t *, ehci_soft_qh_t *); @@ -224,104 +277,112 @@ Static void ehci_rem_qh(ehci_softc_t *, Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_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); +Static void ehci_close_pipe(struct usbd_pipe *, ehci_soft_qh_t *); +Static void ehci_abort_xfer(struct usbd_xfer *, usbd_status); #ifdef EHCI_DEBUG -Static void ehci_dump_regs(ehci_softc_t *); -void ehci_dump(void); Static ehci_softc_t *theehci; -Static void ehci_dump_link(ehci_link_t, int); +void ehci_dump(void); +#endif + +#ifdef EHCI_DEBUG +Static void ehci_dump_regs(ehci_softc_t *); Static void ehci_dump_sqtds(ehci_soft_qtd_t *); Static void ehci_dump_sqtd(ehci_soft_qtd_t *); Static void ehci_dump_qtd(ehci_qtd_t *); Static void ehci_dump_sqh(ehci_soft_qh_t *); -#if notyet -Static void ehci_dump_sitd(struct ehci_soft_itd *itd); +Static void ehci_dump_sitd(struct ehci_soft_itd *); +Static void ehci_dump_itds(ehci_soft_itd_t *); Static void ehci_dump_itd(struct ehci_soft_itd *); -#endif -#ifdef DIAGNOSTIC Static void ehci_dump_exfer(struct ehci_xfer *); #endif -#endif #define EHCI_NULL htole32(EHCI_LINK_TERMINATE) -#define EHCI_INTR_ENDPT 1 +static inline void +ehci_add_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) +{ -#define ehci_add_intr_list(sc, ex) \ - TAILQ_INSERT_TAIL(&(sc)->sc_intrhead, (ex), inext); -#define ehci_del_intr_list(sc, ex) \ - do { \ - TAILQ_REMOVE(&sc->sc_intrhead, (ex), inext); \ - (ex)->inext.tqe_prev = NULL; \ - } while (0) -#define ehci_active_intr_list(ex) ((ex)->inext.tqe_prev != NULL) + TAILQ_INSERT_TAIL(&sc->sc_intrhead, ex, ex_next); +} -Static const struct usbd_bus_methods ehci_bus_methods = { - .open_pipe = ehci_open, - .soft_intr = ehci_softintr, - .do_poll = ehci_poll, - .allocm = ehci_allocm, - .freem = ehci_freem, - .allocx = ehci_allocx, - .freex = ehci_freex, - .get_lock = ehci_get_lock, - .new_device = NULL, -}; +static inline void +ehci_del_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) +{ + + TAILQ_REMOVE(&sc->sc_intrhead, ex, ex_next); +} -Static const struct usbd_pipe_methods ehci_root_ctrl_methods = { - .transfer = ehci_root_ctrl_transfer, - .start = ehci_root_ctrl_start, - .abort = ehci_root_ctrl_abort, - .close = ehci_root_ctrl_close, - .cleartoggle = ehci_noop, - .done = ehci_root_ctrl_done, +Static const struct usbd_bus_methods ehci_bus_methods = { + .ubm_open = ehci_open, + .ubm_softint = ehci_softintr, + .ubm_dopoll = ehci_poll, + .ubm_allocx = ehci_allocx, + .ubm_freex = ehci_freex, + .ubm_getlock = ehci_get_lock, + .ubm_rhctrl = ehci_roothub_ctrl, }; Static const struct usbd_pipe_methods ehci_root_intr_methods = { - .transfer = ehci_root_intr_transfer, - .start = ehci_root_intr_start, - .abort = ehci_root_intr_abort, - .close = ehci_root_intr_close, - .cleartoggle = ehci_noop, - .done = ehci_root_intr_done, + .upm_transfer = ehci_root_intr_transfer, + .upm_start = ehci_root_intr_start, + .upm_abort = ehci_root_intr_abort, + .upm_close = ehci_root_intr_close, + .upm_cleartoggle = ehci_noop, + .upm_done = ehci_root_intr_done, }; Static const struct usbd_pipe_methods ehci_device_ctrl_methods = { - .transfer = ehci_device_ctrl_transfer, - .start = ehci_device_ctrl_start, - .abort = ehci_device_ctrl_abort, - .close = ehci_device_ctrl_close, - .cleartoggle = ehci_noop, - .done = ehci_device_ctrl_done, + .upm_init = ehci_device_ctrl_init, + .upm_fini = ehci_device_ctrl_fini, + .upm_transfer = ehci_device_ctrl_transfer, + .upm_start = ehci_device_ctrl_start, + .upm_abort = ehci_device_ctrl_abort, + .upm_close = ehci_device_ctrl_close, + .upm_cleartoggle = ehci_noop, + .upm_done = ehci_device_ctrl_done, }; Static const struct usbd_pipe_methods ehci_device_intr_methods = { - .transfer = ehci_device_intr_transfer, - .start = ehci_device_intr_start, - .abort = ehci_device_intr_abort, - .close = ehci_device_intr_close, - .cleartoggle = ehci_device_clear_toggle, - .done = ehci_device_intr_done, + .upm_init = ehci_device_intr_init, + .upm_fini = ehci_device_intr_fini, + .upm_transfer = ehci_device_intr_transfer, + .upm_start = ehci_device_intr_start, + .upm_abort = ehci_device_intr_abort, + .upm_close = ehci_device_intr_close, + .upm_cleartoggle = ehci_device_clear_toggle, + .upm_done = ehci_device_intr_done, }; Static const struct usbd_pipe_methods ehci_device_bulk_methods = { - .transfer = ehci_device_bulk_transfer, - .start = ehci_device_bulk_start, - .abort = ehci_device_bulk_abort, - .close = ehci_device_bulk_close, - .cleartoggle = ehci_device_clear_toggle, - .done = ehci_device_bulk_done, + .upm_init = ehci_device_bulk_init, + .upm_fini = ehci_device_bulk_fini, + .upm_transfer = ehci_device_bulk_transfer, + .upm_start = ehci_device_bulk_start, + .upm_abort = ehci_device_bulk_abort, + .upm_close = ehci_device_bulk_close, + .upm_cleartoggle = ehci_device_clear_toggle, + .upm_done = ehci_device_bulk_done, }; Static const struct usbd_pipe_methods ehci_device_isoc_methods = { - .transfer = ehci_device_isoc_transfer, - .start = ehci_device_isoc_start, - .abort = ehci_device_isoc_abort, - .close = ehci_device_isoc_close, - .cleartoggle = ehci_noop, - .done = ehci_device_isoc_done, + .upm_init = ehci_device_isoc_init, + .upm_fini = ehci_device_isoc_fini, + .upm_transfer = ehci_device_isoc_transfer, + .upm_abort = ehci_device_isoc_abort, + .upm_close = ehci_device_isoc_close, + .upm_cleartoggle = ehci_noop, + .upm_done = ehci_device_isoc_done, +}; + +Static const struct usbd_pipe_methods ehci_device_fs_isoc_methods = { + .upm_init = ehci_device_fs_isoc_init, + .upm_fini = ehci_device_fs_isoc_fini, + .upm_transfer = ehci_device_fs_isoc_transfer, + .upm_abort = ehci_device_fs_isoc_abort, + .upm_close = ehci_device_fs_isoc_close, + .upm_cleartoggle = ehci_noop, + .upm_done = ehci_device_fs_isoc_done, }; static const uint8_t revbits[EHCI_MAX_POLLRATE] = { @@ -335,32 +396,32 @@ static const uint8_t revbits[EHCI_MAX_PO 0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f, }; -usbd_status +int ehci_init(ehci_softc_t *sc) { - u_int32_t vers, sparams, cparams, hcr; + uint32_t vers, sparams, cparams, hcr; u_int i; usbd_status err; ehci_soft_qh_t *sqh; u_int ncomp; - DPRINTF(("ehci_init: start\n")); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); #ifdef EHCI_DEBUG theehci = sc; #endif mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB); cv_init(&sc->sc_softwake_cv, "ehciab"); cv_init(&sc->sc_doorbell, "ehcidi"); sc->sc_xferpool = pool_cache_init(sizeof(struct ehci_xfer), 0, 0, 0, "ehcixfer", NULL, IPL_USB, NULL, NULL, NULL); - sc->sc_doorbell_si = softint_establish(SOFTINT_NET | SOFTINT_MPSAFE, + sc->sc_doorbell_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, ehci_doorbell, sc); KASSERT(sc->sc_doorbell_si != NULL); - sc->sc_pcd_si = softint_establish(SOFTINT_NET | SOFTINT_MPSAFE, + sc->sc_pcd_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, ehci_pcd, sc); KASSERT(sc->sc_pcd_si != NULL); @@ -368,10 +429,10 @@ ehci_init(ehci_softc_t *sc) vers = EREAD2(sc, EHCI_HCIVERSION); aprint_verbose("%s: EHCI version %x.%x\n", device_xname(sc->sc_dev), - vers >> 8, vers & 0xff); + vers >> 8, vers & 0xff); sparams = EREAD4(sc, EHCI_HCSPARAMS); - DPRINTF(("ehci_init: sparams=0x%x\n", sparams)); + DPRINTF("sparams=%#x", sparams, 0, 0, 0); sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); ncomp = EHCI_HCS_N_CC(sparams); if (ncomp != sc->sc_ncomp) { @@ -395,22 +456,28 @@ ehci_init(ehci_softc_t *sc) aprint_normal("\n"); } sc->sc_noport = EHCI_HCS_N_PORTS(sparams); - cparams = EREAD4(sc, EHCI_HCCPARAMS); - DPRINTF(("ehci_init: cparams=0x%x\n", cparams)); sc->sc_hasppc = EHCI_HCS_PPC(sparams); + cparams = EREAD4(sc, EHCI_HCCPARAMS); + DPRINTF("cparams=%#x", cparams, 0, 0, 0); + if (EHCI_HCC_64BIT(cparams)) { /* MUST clear segment register if 64 bit capable. */ - EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); + EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); } - sc->sc_bus.usbrev = USBREV_2_0; + if (cparams & EHCI_HCC_IST_FULLFRAME) { + sc->sc_istthreshold = 0; + } else { + sc->sc_istthreshold = EHCI_HCC_GET_IST_THRESHOLD(cparams); + } - usb_setup_reserve(sc->sc_dev, &sc->sc_dma_reserve, sc->sc_bus.dmatag, - USB_MEM_RESERVE); + sc->sc_bus.ub_revision = USBREV_2_0; + sc->sc_bus.ub_usedma = true; + sc->sc_bus.ub_dmaflags = USBMALLOC_MULTISEG; /* Reset the controller */ - DPRINTF(("%s: resetting\n", device_xname(sc->sc_dev))); + DPRINTF("resetting", 0, 0, 0, 0); EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ usb_delay_ms(&sc->sc_bus, 1); EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); @@ -422,22 +489,11 @@ ehci_init(ehci_softc_t *sc) } if (hcr) { aprint_error("%s: reset timeout\n", device_xname(sc->sc_dev)); - return (USBD_IOERROR); + return EIO; } if (sc->sc_vendor_init) sc->sc_vendor_init(sc); - /* - * If we are doing embedded transaction translation function, force - * the controller to host mode. - */ - if (sc->sc_flags & EHCIF_ETTF) { - uint32_t usbmode = EREAD4(sc, EHCI_USBMODE); - usbmode &= ~EHCI_USBMODE_CM; - usbmode |= EHCI_USBMODE_CM_HOST; - EWRITE4(sc, EHCI_USBMODE, usbmode); - } - /* XXX need proper intr scheduling */ sc->sc_rand = 96; @@ -446,13 +502,13 @@ ehci_init(ehci_softc_t *sc) case 0: sc->sc_flsize = 1024; break; case 1: sc->sc_flsize = 512; break; case 2: sc->sc_flsize = 256; break; - case 3: return (USBD_IOERROR); + case 3: return EIO; } err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t), EHCI_FLALIGN_ALIGN, &sc->sc_fldma); if (err) - return (err); - DPRINTF(("%s: flsize=%d\n", device_xname(sc->sc_dev),sc->sc_flsize)); + return err; + DPRINTF("flsize=%d", sc->sc_flsize, 0, 0, 0); sc->sc_flist = KERNADDR(&sc->sc_fldma, 0); for (i = 0; i < sc->sc_flsize; i++) { @@ -466,11 +522,12 @@ ehci_init(ehci_softc_t *sc) if (sc->sc_softitds == NULL) return ENOMEM; LIST_INIT(&sc->sc_freeitds); + LIST_INIT(&sc->sc_freesitds); TAILQ_INIT(&sc->sc_intrhead); /* Set up the bus struct. */ - sc->sc_bus.methods = &ehci_bus_methods; - sc->sc_bus.pipe_size = sizeof(struct ehci_pipe); + sc->sc_bus.ub_methods = &ehci_bus_methods; + sc->sc_bus.ub_pipesize= sizeof(struct ehci_pipe); sc->sc_eintrs = EHCI_NORMAL_INTRS; @@ -481,7 +538,7 @@ ehci_init(ehci_softc_t *sc) for (i = 0; i < EHCI_INTRQHS; i++) { sqh = ehci_alloc_sqh(sc); if (sqh == NULL) { - err = USBD_NOMEM; + err = ENOMEM; goto bad1; } sc->sc_islots[i].sqh = sqh; @@ -499,8 +556,8 @@ ehci_init(ehci_softc_t *sc) EHCI_LINK_QH); } sqh->qh.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); + sqh->qh.qh_endphub = htole32(EHCI_QH_SET_MULT(1)); sqh->qh.qh_curqtd = EHCI_NULL; - sqh->next = NULL; 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_HALTED); @@ -524,7 +581,7 @@ ehci_init(ehci_softc_t *sc) /* Allocate dummy QH that starts the async list. */ sqh = ehci_alloc_sqh(sc); if (sqh == NULL) { - err = USBD_NOMEM; + err = ENOMEM; goto bad1; } /* Fill the QH */ @@ -542,9 +599,9 @@ ehci_init(ehci_softc_t *sc) usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); #ifdef EHCI_DEBUG - if (ehcidebug) { - ehci_dump_sqh(sqh); - } + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); #endif /* Point to async list */ @@ -572,14 +629,14 @@ ehci_init(ehci_softc_t *sc) } if (hcr) { aprint_error("%s: run timeout\n", device_xname(sc->sc_dev)); - return (USBD_IOERROR); + return EIO; } /* Enable interrupts */ - DPRINTFN(1,("ehci_init: enabling\n")); + DPRINTF("enabling interupts", 0, 0, 0, 0); EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); - return (USBD_NORMAL_COMPLETION); + return 0; #if 0 bad2: @@ -587,7 +644,7 @@ ehci_init(ehci_softc_t *sc) #endif bad1: usb_freemem(&sc->sc_bus, &sc->sc_fldma); - return (err); + return err; } int @@ -596,6 +653,8 @@ ehci_intr(void *v) ehci_softc_t *sc = v; int ret = 0; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + if (sc == NULL) return 0; @@ -605,14 +664,12 @@ ehci_intr(void *v) goto done; /* If we get an interrupt while polling, then just ignore it. */ - if (sc->sc_bus.use_polling) { - u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + if (sc->sc_bus.ub_usepolling) { + uint32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); if (intrs) EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ -#ifdef DIAGNOSTIC - DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n")); -#endif + DPRINTFN(16, "ignored interrupt while polling", 0, 0, 0, 0); goto done; } @@ -626,35 +683,33 @@ done: Static int ehci_intr1(ehci_softc_t *sc) { - u_int32_t intrs, eintrs; + uint32_t intrs, eintrs; - DPRINTFN(20,("ehci_intr1: enter\n")); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); /* In case the interrupt occurs before initialization has completed. */ if (sc == NULL) { #ifdef DIAGNOSTIC printf("ehci_intr1: sc == NULL\n"); #endif - return (0); + return 0; } KASSERT(mutex_owned(&sc->sc_intr_lock)); intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); if (!intrs) - return (0); + return 0; eintrs = intrs & sc->sc_eintrs; - DPRINTFN(7, ("ehci_intr1: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", - sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), - (u_int)eintrs)); + DPRINTF("sc=%p intrs=%#x(%#x) eintrs=%#x", sc, intrs, + EOREAD4(sc, EHCI_USBSTS), eintrs); if (!eintrs) - return (0); + return 0; EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ - sc->sc_bus.no_intrs++; if (eintrs & EHCI_STS_IAA) { - DPRINTF(("ehci_intr1: door bell\n")); + DPRINTF("door bell", 0, 0, 0, 0); kpreempt_disable(); KASSERT(sc->sc_doorbell_si != NULL); softint_schedule(sc->sc_doorbell_si); @@ -662,9 +717,9 @@ ehci_intr1(ehci_softc_t *sc) eintrs &= ~EHCI_STS_IAA; } if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { - DPRINTFN(5,("ehci_intr1: %s %s\n", - eintrs & EHCI_STS_INT ? "INT" : "", - eintrs & EHCI_STS_ERRINT ? "ERRINT" : "")); + DPRINTF("INT=%d ERRINT=%d", + eintrs & EHCI_STS_INT ? 1 : 0, + eintrs & EHCI_STS_ERRINT ? 1 : 0, 0, 0); usb_schedsoftintr(&sc->sc_bus); eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); } @@ -689,7 +744,7 @@ ehci_intr1(ehci_softc_t *sc) device_xname(sc->sc_dev), eintrs); } - return (1); + return 1; } Static void @@ -706,10 +761,12 @@ Static void ehci_pcd(void *addr) { ehci_softc_t *sc = addr; - usbd_xfer_handle xfer; + struct usbd_xfer *xfer; u_char *p; int i, m; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + mutex_enter(&sc->sc_lock); xfer = sc->sc_intrxfer; @@ -718,17 +775,18 @@ ehci_pcd(void *addr) goto done; } - p = KERNADDR(&xfer->dmabuf, 0); - m = min(sc->sc_noport, xfer->length * 8 - 1); - memset(p, 0, xfer->length); + p = xfer->ux_buf; + m = min(sc->sc_noport, xfer->ux_length * 8 - 1); + memset(p, 0, xfer->ux_length); for (i = 1; i <= m; i++) { /* Pick out CHANGE bits from the status reg. */ if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) p[i/8] |= 1 << (i%8); + if (i % 8 == 7) + DPRINTF("change(%d)=0x%02x", i / 8, p[i/8], 0, 0); } - DPRINTF(("ehci_pcd: change=0x%02x\n", *p)); - xfer->actlen = xfer->length; - xfer->status = USBD_NORMAL_COMPLETION; + xfer->ux_actlen = xfer->ux_length; + xfer->ux_status = USBD_NORMAL_COMPLETION; usb_transfer_complete(xfer); @@ -740,12 +798,15 @@ Static void ehci_softintr(void *v) { struct usbd_bus *bus = v; - ehci_softc_t *sc = bus->hci_private; + ehci_softc_t *sc = EHCI_BUS2SC(bus); struct ehci_xfer *ex, *nextex; - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); + KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTFN(10,("%s: ehci_softintr\n", device_xname(sc->sc_dev))); + ex_completeq_t cq; + TAILQ_INIT(&cq); /* * The only explanation I can think of for why EHCI is as brain dead @@ -753,9 +814,38 @@ ehci_softintr(void *v) * An interrupt just tells us that something is done, we have no * clue what, so we need to scan through all active transfers. :-( */ - for (ex = TAILQ_FIRST(&sc->sc_intrhead); ex; ex = nextex) { - nextex = TAILQ_NEXT(ex, inext); - ehci_check_intr(sc, ex); + + /* + * ehci_idone will remove transfer from sc->sc_intrhead if it's + * complete and add to our cq list + * + */ + TAILQ_FOREACH_SAFE(ex, &sc->sc_intrhead, ex_next, nextex) { + switch (ex->ex_type) { + case EX_CTRL: + case EX_BULK: + case EX_INTR: + ehci_check_qh_intr(sc, ex, &cq); + break; + case EX_ISOC: + ehci_check_itd_intr(sc, ex, &cq); + break; + case EX_FS_ISOC: + ehci_check_sitd_intr(sc, ex, &cq); + break; + default: + KASSERT(false); + } + + } + + /* + * We abuse ex_next for the interrupt and complete lists and + * interrupt transfers will get re-added here so use + * the _SAFE version of TAILQ_FOREACH. + */ + TAILQ_FOREACH_SAFE(ex, &cq, ex_next, nextex) { + usb_transfer_complete(&ex->ex_xfer); } /* Schedule a callout to catch any dropped transactions. */ @@ -770,45 +860,26 @@ ehci_softintr(void *v) } } -/* Check for an interrupt. */ Static void -ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex) +ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) { - int attr; - - DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex)); + ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; + uint32_t status; - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - attr = ex->xfer.pipe->endpoint->edesc->bmAttributes; - if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS) - ehci_check_itd_intr(sc, ex); - else - ehci_check_qh_intr(sc, ex); - - return; -} - -Static void -ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex) -{ - ehci_soft_qtd_t *sqtd, *lsqtd; - __uint32_t status; - - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); + KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); - if (ex->sqtdstart == NULL) { - printf("ehci_check_qh_intr: not valid sqtd\n"); - return; + if (ex->ex_type == EX_CTRL) { + fsqtd = ex->ex_setup; + lsqtd = ex->ex_status; + } else { + fsqtd = ex->ex_sqtdstart; + lsqtd = ex->ex_sqtdend; } + KASSERTMSG(fsqtd != NULL && lsqtd != NULL, + "xfer %p xt %d fsqtd %p lsqtd %p", ex, ex->ex_type, fsqtd, lsqtd); - lsqtd = ex->sqtdend; -#ifdef DIAGNOSTIC - if (lsqtd == NULL) { - printf("ehci_check_qh_intr: lsqtd==0\n"); - return; - } -#endif /* * If the last TD is still active we need to check whether there * is an error somewhere in the middle, or whether there was a @@ -823,8 +894,10 @@ ehci_check_qh_intr(ehci_softc_t *sc, str lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); if (status & EHCI_QTD_ACTIVE) { - DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex)); - for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) { + DPRINTFN(10, "active ex=%p", ex, 0, 0, 0); + + /* last qTD has already been checked */ + for (sqtd = fsqtd; sqtd != lsqtd; sqtd = sqtd->nextqtd) { usb_syncmem(&sqtd->dma, sqtd->offs + offsetof(ehci_qtd_t, qtd_status), sizeof(sqtd->qtd.qtd_status), @@ -841,11 +914,6 @@ ehci_check_qh_intr(ehci_softc_t *sc, str goto done; /* Handle short packets */ if (EHCI_QTD_GET_BYTES(status) != 0) { - usbd_pipe_handle pipe = ex->xfer.pipe; - usb_endpoint_descriptor_t *ed = - pipe->endpoint->edesc; - uint8_t xt = UE_GET_XFERTYPE(ed->bmAttributes); - /* * If we get here for a control transfer then * we need to let the hardware complete the @@ -854,53 +922,52 @@ ehci_check_qh_intr(ehci_softc_t *sc, str * * Otherwise, we're done. */ - if (xt == UE_CONTROL) { + if (ex->ex_type == EX_CTRL) { break; } goto done; } } - DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", - ex, ex->sqtdstart)); + DPRINTFN(10, "ex=%p std=%p still active", ex, ex->ex_sqtdstart, + 0, 0); +#ifdef EHCI_DEBUG + DPRINTFN(5, "--- still active start ---", 0, 0, 0, 0); + ehci_dump_sqtds(ex->ex_sqtdstart); + DPRINTFN(5, "--- still active end ---", 0, 0, 0, 0); +#endif return; } done: - DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex)); - callout_stop(&ex->xfer.timeout_handle); - ehci_idone(ex); + DPRINTFN(10, "ex=%p done", ex, 0, 0, 0); + callout_stop(&ex->ex_xfer.ux_callout); + ehci_idone(ex, cq); } Static void -ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex) +ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) { ehci_soft_itd_t *itd; int i; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + KASSERT(mutex_owned(&sc->sc_lock)); - if (&ex->xfer != SIMPLEQ_FIRST(&ex->xfer.pipe->queue)) + if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) return; - if (ex->itdstart == NULL) { - printf("ehci_check_itd_intr: not valid itd\n"); - return; - } + KASSERTMSG(ex->ex_itdstart != NULL && ex->ex_itdend != NULL, + "xfer %p fitd %p litd %p", ex, ex->ex_itdstart, ex->ex_itdend); - itd = ex->itdend; -#ifdef DIAGNOSTIC - if (itd == NULL) { - printf("ehci_check_itd_intr: itdend == 0\n"); - return; - } -#endif + itd = ex->ex_itdend; /* * check no active transfers in last itd, meaning we're finished */ usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), - sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE | - BUS_DMASYNC_POSTREAD); + sizeof(itd->itd.itd_ctl), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE) @@ -911,79 +978,126 @@ ehci_check_itd_intr(ehci_softc_t *sc, st goto done; /* All 8 descriptors inactive, it's done */ } - DPRINTFN(12, ("ehci_check_itd_intr: ex %p itd %p still active\n", ex, - ex->itdstart)); + usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), + sizeof(itd->itd.itd_ctl), BUS_DMASYNC_PREREAD); + + DPRINTFN(10, "ex %p itd %p still active", ex, ex->ex_itdstart, 0, 0); return; done: - DPRINTFN(12, ("ehci_check_itd_intr: ex=%p done\n", ex)); - callout_stop(&ex->xfer.timeout_handle); - ehci_idone(ex); + DPRINTF("ex %p done", ex, 0, 0, 0); + callout_stop(&ex->ex_xfer.ux_callout); + ehci_idone(ex, cq); } -Static void -ehci_idone(struct ehci_xfer *ex) +void +ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) { - usbd_xfer_handle xfer = &ex->xfer; - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - struct ehci_softc *sc = xfer->pipe->device->bus->hci_private; - ehci_soft_qtd_t *sqtd, *lsqtd; - u_int32_t status = 0, nstatus = 0; - int actlen; + ehci_soft_sitd_t *sitd; - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); + KASSERT(mutex_owned(&sc->sc_lock)); -#ifdef DIAGNOSTIC - if (ex->isdone) { - printf("ehci_idone: ex=%p is done!\n", ex); -#ifdef EHCI_DEBUG - ehci_dump_exfer(ex); -#endif + if (&ex->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) + return; + + KASSERTMSG(ex->ex_sitdstart != NULL && ex->ex_sitdend != NULL, + "xfer %p fsitd %p lsitd %p", ex, ex->ex_sitdstart, ex->ex_sitdend); + + sitd = ex->ex_sitdend; + + /* + * check no active transfers in last sitd, meaning we're finished + */ + + usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), + sizeof(sitd->sitd.sitd_trans), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + bool active = ((le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE) != 0); + + usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), + sizeof(sitd->sitd.sitd_trans), BUS_DMASYNC_PREREAD); + + if (active) return; - } - ex->isdone = 1; -#endif - if (xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT) { - DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer)); + DPRINTFN(10, "ex=%p done", ex, 0, 0, 0); + callout_stop(&(ex->ex_xfer.ux_callout)); + ehci_idone(ex, cq); +} + + +Static void +ehci_idone(struct ehci_xfer *ex, ex_completeq_t *cq) +{ + struct usbd_xfer *xfer = &ex->ex_xfer; + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_softc *sc = EHCI_XFER2SC(xfer); + ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; + uint32_t status = 0, nstatus = 0; + int actlen = 0; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); + + DPRINTF("ex=%p", ex, 0, 0, 0); + + if (xfer->ux_status == USBD_CANCELLED || + xfer->ux_status == USBD_TIMEOUT) { + DPRINTF("aborted xfer=%p", xfer, 0, 0, 0); return; } +#ifdef DIAGNOSTIC #ifdef EHCI_DEBUG - DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe)); - if (ehcidebug > 10) - ehci_dump_sqtds(ex->sqtdstart); + if (ex->ex_isdone) { + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_exfer(ex); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); + } #endif + KASSERTMSG(!ex->ex_isdone, "xfer %p type %d status %d", xfer, + ex->ex_type, xfer->ux_status); + ex->ex_isdone = true; +#endif + + DPRINTF("xfer=%p, pipe=%p ready", xfer, epipe, 0, 0); /* The transfer is done, compute actual length and status. */ + if (ex->ex_type == EX_ISOC) { + /* HS isoc transfer */ - if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes) - == UE_ISOCHRONOUS) { - /* Isoc transfer */ struct ehci_soft_itd *itd; int i, nframes, len, uframes; nframes = 0; - actlen = 0; - i = xfer->pipe->endpoint->edesc->bInterval; +#ifdef EHCI_DEBUG + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_itds(ex->ex_itdstart); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); +#endif + + i = xfer->ux_pipe->up_endpoint->ue_edesc->bInterval; uframes = min(1 << (i - 1), USB_UFRAMES_PER_FRAME); - for (itd = ex->itdstart; itd != NULL; itd = itd->xfer_next) { - usb_syncmem(&itd->dma,itd->offs + offsetof(ehci_itd_t,itd_ctl), - sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE | - BUS_DMASYNC_POSTREAD); + for (itd = ex->ex_itdstart; itd != NULL; itd = itd->xfer_next) { + usb_syncmem(&itd->dma, + itd->offs + offsetof(ehci_itd_t,itd_ctl), + sizeof(itd->itd.itd_ctl), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); for (i = 0; i < EHCI_ITD_NUFRAMES; i += uframes) { - /* XXX - driver didn't fill in the frame full + /* + * XXX - driver didn't fill in the frame full * of uframes. This leads to scheduling * inefficiencies, but working around * this doubles complexity of tracking * an xfer. */ - if (nframes >= xfer->nframes) + if (nframes >= xfer->ux_nframes) break; status = le32toh(itd->itd.itd_ctl[i]); @@ -991,27 +1105,95 @@ ehci_idone(struct ehci_xfer *ex) if (EHCI_ITD_GET_STATUS(status) != 0) len = 0; /*No valid data on error*/ - xfer->frlengths[nframes++] = len; + xfer->ux_frlengths[nframes++] = len; actlen += len; } + usb_syncmem(&itd->dma, + itd->offs + offsetof(ehci_itd_t,itd_ctl), + sizeof(itd->itd.itd_ctl), BUS_DMASYNC_PREREAD); + + if (nframes >= xfer->ux_nframes) + break; + } + + xfer->ux_actlen = actlen; + xfer->ux_status = USBD_NORMAL_COMPLETION; + goto end; + } else if (ex->ex_type == EX_FS_ISOC) { + /* FS isoc transfer */ + struct ehci_soft_sitd *sitd; + int nframes, len; + + nframes = 0; + + for (sitd = ex->ex_sitdstart; sitd != NULL; + sitd = sitd->xfer_next) { + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_trans), + sizeof(sitd->sitd.sitd_trans), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + /* + * XXX - driver didn't fill in the frame full + * of uframes. This leads to scheduling + * inefficiencies, but working around + * this doubles complexity of tracking + * an xfer. + */ + if (nframes >= xfer->ux_nframes) + break; + + status = le32toh(sitd->sitd.sitd_trans); + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_trans), + sizeof(sitd->sitd.sitd_trans), BUS_DMASYNC_PREREAD); + + len = EHCI_SITD_GET_LEN(status); + if (status & (EHCI_SITD_ERR|EHCI_SITD_BUFERR| + EHCI_SITD_BABBLE|EHCI_SITD_XACTERR|EHCI_SITD_MISS)) { + /* No valid data on error */ + len = xfer->ux_frlengths[nframes]; + } - if (nframes >= xfer->nframes) + /* + * frlengths[i]: # of bytes to send + * len: # of bytes host didn't send + */ + xfer->ux_frlengths[nframes] -= len; + /* frlengths[i]: # of bytes host sent */ + actlen += xfer->ux_frlengths[nframes++]; + + if (nframes >= xfer->ux_nframes) break; } - xfer->actlen = actlen; - xfer->status = USBD_NORMAL_COMPLETION; + xfer->ux_actlen = actlen; + xfer->ux_status = USBD_NORMAL_COMPLETION; goto end; } + KASSERT(ex->ex_type == EX_CTRL || ex->ex_type == EX_INTR || + ex->ex_type == EX_BULK); /* Continue processing xfers using queue heads */ + if (ex->ex_type == EX_CTRL) { + fsqtd = ex->ex_setup; + lsqtd = ex->ex_status; + } else { + fsqtd = ex->ex_sqtdstart; + lsqtd = ex->ex_sqtdend; + } +#ifdef EHCI_DEBUG + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqtds(fsqtd); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); +#endif - lsqtd = ex->sqtdend; - actlen = 0; - for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd = sqtd->nextqtd) { + for (sqtd = fsqtd; sqtd != lsqtd->nextqtd; sqtd = sqtd->nextqtd) { usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); nstatus = le32toh(sqtd->qtd.qtd_status); + usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), + BUS_DMASYNC_PREREAD); if (nstatus & EHCI_QTD_ACTIVE) break; @@ -1020,7 +1202,6 @@ ehci_idone(struct ehci_xfer *ex) actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); } - /* * If there are left over TDs we need to update the toggle. * The default pipe doesn't need it since control transfers @@ -1029,38 +1210,44 @@ ehci_idone(struct ehci_xfer *ex) * packets within the qTD. */ if ((sqtd != lsqtd->nextqtd || EHCI_QTD_GET_BYTES(status)) && - xfer->pipe->device->default_pipe != xfer->pipe) { - DPRINTFN(2, ("ehci_idone: need toggle update " - "status=%08x nstatus=%08x\n", status, nstatus)); + xfer->ux_pipe->up_dev->ud_pipe0 != xfer->ux_pipe) { + DPRINTF("toggle update status=0x%08x nstatus=0x%08x", + status, nstatus, 0, 0); #if 0 ehci_dump_sqh(epipe->sqh); - ehci_dump_sqtds(ex->sqtdstart); + ehci_dump_sqtds(ex->ex_sqtdstart); #endif epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); } - DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", - xfer->length, actlen, status)); - xfer->actlen = actlen; + DPRINTF("len=%d actlen=%d status=0x%08x", xfer->ux_length, actlen, + status, 0); + xfer->ux_actlen = actlen; if (status & EHCI_QTD_HALTED) { #ifdef EHCI_DEBUG - char sbuf[128]; + DPRINTF("halted addr=%d endpt=0x%02x", + xfer->ux_pipe->up_dev->ud_addr, + xfer->ux_pipe->up_endpoint->ue_edesc->bEndpointAddress, + 0, 0); + DPRINTF("cerr=%d pid=%d", + EHCI_QTD_GET_CERR(status), EHCI_QTD_GET_PID(status), + 0, 0); + DPRINTF("active =%d halted=%d buferr=%d babble=%d", + status & EHCI_QTD_ACTIVE ? 1 : 0, + status & EHCI_QTD_HALTED ? 1 : 0, + status & EHCI_QTD_BUFERR ? 1 : 0, + status & EHCI_QTD_BABBLE ? 1 : 0); + + DPRINTF("xacterr=%d missed=%d split =%d ping =%d", + status & EHCI_QTD_XACTERR ? 1 : 0, + status & EHCI_QTD_MISSEDMICRO ? 1 : 0, + status & EHCI_QTD_SPLITXSTATE ? 1 : 0, + status & EHCI_QTD_PINGSTATE ? 1 : 0); - snprintb(sbuf, sizeof(sbuf), - "\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE" - "\4XACTERR\3MISSED\2SPLIT\1PING", - (u_int32_t)status); - - DPRINTFN(2, ("%s: error, addr=%d, endpt=0x%02x, " - "cerr=%d pid=%d stat=%s\n", __func__, - xfer->pipe->device->address, - xfer->pipe->endpoint->edesc->bEndpointAddress, - EHCI_QTD_GET_CERR(status), EHCI_QTD_GET_PID(status), sbuf)); - - if (ehcidebug > 2) { - ehci_dump_sqh(epipe->sqh); - ehci_dump_sqtds(ex->sqtdstart); - } + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(ex->ex_sqtdstart); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); #endif /* low&full speed has an extra error flag */ if (EHCI_QH_GET_EPS(epipe->sqh->qh.qh_endp) != @@ -1069,9 +1256,9 @@ ehci_idone(struct ehci_xfer *ex) else status &= EHCI_QTD_STATERRS; if (status == 0) /* no other errors means a stall */ { - xfer->status = USBD_STALLED; + xfer->ux_status = USBD_STALLED; } else { - xfer->status = USBD_IOERROR; /* more info XXX */ + xfer->ux_status = USBD_IOERROR; /* more info XXX */ } /* XXX need to reset TT on missed microframe */ if (status & EHCI_QTD_MISSEDMICRO) { @@ -1080,68 +1267,30 @@ ehci_idone(struct ehci_xfer *ex) device_xname(sc->sc_dev)); } } else { - xfer->status = USBD_NORMAL_COMPLETION; + xfer->ux_status = USBD_NORMAL_COMPLETION; } end: - /* XXX transfer_complete memcpys out transfer data (for in endpoints) - * during this call, before methods->done is called: dma sync required - * beforehand? */ - usb_transfer_complete(xfer); - DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex)); -} - -/* - * Wait here until controller claims to have an interrupt. - * Then call ehci_intr and return. Use timeout to avoid waiting - * too long. - */ -Static void -ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer) -{ - int timo; - u_int32_t intrs; - xfer->status = USBD_IN_PROGRESS; - for (timo = xfer->timeout; timo >= 0; timo--) { - usb_delay_ms(&sc->sc_bus, 1); - if (sc->sc_dying) - break; - intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) & - sc->sc_eintrs; - DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs)); -#ifdef EHCI_DEBUG - if (ehcidebug > 15) - ehci_dump_regs(sc); -#endif - if (intrs) { - mutex_spin_enter(&sc->sc_intr_lock); - ehci_intr1(sc); - mutex_spin_exit(&sc->sc_intr_lock); - if (xfer->status != USBD_IN_PROGRESS) - return; - } - } + ehci_del_intr_list(sc, ex); + TAILQ_INSERT_TAIL(cq, ex, ex_next); - /* Timeout */ - DPRINTF(("ehci_waitintr: timeout\n")); - xfer->status = USBD_TIMEOUT; - mutex_enter(&sc->sc_lock); - usb_transfer_complete(xfer); - mutex_exit(&sc->sc_lock); - /* XXX should free TD */ + DPRINTF("ex=%p done", ex, 0, 0, 0); } Static void ehci_poll(struct usbd_bus *bus) { - ehci_softc_t *sc = bus->hci_private; + ehci_softc_t *sc = EHCI_BUS2SC(bus); + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + #ifdef EHCI_DEBUG static int last; int new; new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); if (new != last) { - DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new)); + DPRINTF("intrs=0x%04x", new, 0, 0, 0); last = new; } #endif @@ -1167,11 +1316,13 @@ ehci_detach(struct ehci_softc *sc, int f { int rv = 0; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + if (sc->sc_child != NULL) rv = config_detach(sc->sc_child, flags); if (rv != 0) - return (rv); + return rv; callout_halt(&sc->sc_tmo_intrlist, NULL); callout_destroy(&sc->sc_tmo_intrlist); @@ -1197,7 +1348,7 @@ ehci_detach(struct ehci_softc *sc, int f EOWRITE4(sc, EHCI_CONFIGFLAG, 0); - return (rv); + return rv; } @@ -1232,8 +1383,10 @@ ehci_suspend(device_t dv, const pmf_qual int i; uint32_t cmd, hcr; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + mutex_spin_enter(&sc->sc_intr_lock); - sc->sc_bus.use_polling++; + sc->sc_bus.ub_usepolling++; mutex_spin_exit(&sc->sc_intr_lock); for (i = 1; i <= sc->sc_noport; i++) { @@ -1271,7 +1424,7 @@ ehci_suspend(device_t dv, const pmf_qual printf("%s: config timeout\n", device_xname(dv)); mutex_spin_enter(&sc->sc_intr_lock); - sc->sc_bus.use_polling--; + sc->sc_bus.ub_usepolling--; mutex_spin_exit(&sc->sc_intr_lock); return true; @@ -1284,6 +1437,8 @@ ehci_resume(device_t dv, const pmf_qual_ int i; uint32_t cmd, hcr; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + /* restore things in case the bios sucks */ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); @@ -1340,97 +1495,65 @@ ehci_shutdown(device_t self, int flags) { ehci_softc_t *sc = device_private(self); - DPRINTF(("ehci_shutdown: stopping the HC\n")); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); return true; } -Static usbd_status -ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) -{ - struct ehci_softc *sc = bus->hci_private; - usbd_status err; - - err = usb_allocmem_flags(&sc->sc_bus, size, 0, dma, USBMALLOC_MULTISEG); -#ifdef EHCI_DEBUG - if (err) - printf("ehci_allocm: usb_allocmem_flags()= %s (%d)\n", - usbd_errstr(err), err); -#endif - if (err == USBD_NOMEM) - err = usb_reserve_allocm(&sc->sc_dma_reserve, dma, size); -#ifdef EHCI_DEBUG - if (err) - printf("ehci_allocm: usb_reserve_allocm()= %s (%d)\n", - usbd_errstr(err), err); -#endif - return (err); -} - -Static void -ehci_freem(struct usbd_bus *bus, usb_dma_t *dma) -{ - struct ehci_softc *sc = bus->hci_private; - - if (dma->block->flags & USB_DMA_RESERVE) { - usb_reserve_freem(&sc->sc_dma_reserve, - dma); - return; - } - usb_freemem(&sc->sc_bus, dma); -} - -Static usbd_xfer_handle -ehci_allocx(struct usbd_bus *bus) +Static struct usbd_xfer * +ehci_allocx(struct usbd_bus *bus, unsigned int nframes) { - struct ehci_softc *sc = bus->hci_private; - usbd_xfer_handle xfer; + struct ehci_softc *sc = EHCI_BUS2SC(bus); + struct usbd_xfer *xfer; xfer = pool_cache_get(sc->sc_xferpool, PR_NOWAIT); if (xfer != NULL) { memset(xfer, 0, sizeof(struct ehci_xfer)); #ifdef DIAGNOSTIC - EXFER(xfer)->isdone = 1; - xfer->busy_free = XFER_BUSY; + struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); + ex->ex_isdone = true; + xfer->ux_state = XFER_BUSY; #endif } - return (xfer); + return xfer; } Static void -ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) +ehci_freex(struct usbd_bus *bus, struct usbd_xfer *xfer) { - struct ehci_softc *sc = bus->hci_private; + struct ehci_softc *sc = EHCI_BUS2SC(bus); + struct ehci_xfer *ex __diagused = EHCI_XFER2EXFER(xfer); + + KASSERTMSG(xfer->ux_state == XFER_BUSY, "xfer %p state %d\n", xfer, + xfer->ux_state); + KASSERT(ex->ex_isdone); #ifdef DIAGNOSTIC - if (xfer->busy_free != XFER_BUSY) { - printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer, - xfer->busy_free); - } - xfer->busy_free = XFER_FREE; - if (!EXFER(xfer)->isdone) { - printf("ehci_freex: !isdone\n"); - } + xfer->ux_state = XFER_FREE; #endif + pool_cache_put(sc->sc_xferpool, xfer); } Static void ehci_get_lock(struct usbd_bus *bus, kmutex_t **lock) { - struct ehci_softc *sc = bus->hci_private; + struct ehci_softc *sc = EHCI_BUS2SC(bus); *lock = &sc->sc_lock; } Static void -ehci_device_clear_toggle(usbd_pipe_handle pipe) +ehci_device_clear_toggle(struct usbd_pipe *pipe) { - struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n", - epipe, epipe->sqh->qh.qh_qtd.qtd_status)); + DPRINTF("epipe=%p status=0x%08x", epipe, + epipe->sqh->qh.qh_qtd.qtd_status, 0, 0); #ifdef EHCI_DEBUG if (ehcidebug) usbd_dump_pipe(pipe); @@ -1439,29 +1562,11 @@ ehci_device_clear_toggle(usbd_pipe_handl } Static void -ehci_noop(usbd_pipe_handle pipe) +ehci_noop(struct usbd_pipe *pipe) { } #ifdef EHCI_DEBUG -Static void -ehci_dump_regs(ehci_softc_t *sc) -{ - int i; - printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n", - EOREAD4(sc, EHCI_USBCMD), - EOREAD4(sc, EHCI_USBSTS), - EOREAD4(sc, EHCI_USBINTR)); - printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", - EOREAD4(sc, EHCI_FRINDEX), - EOREAD4(sc, EHCI_CTRLDSSEGMENT), - EOREAD4(sc, EHCI_PERIODICLISTBASE), - EOREAD4(sc, EHCI_ASYNCLISTADDR)); - for (i = 1; i <= sc->sc_noport; i++) - printf("port %d status=0x%08x\n", i, - EOREAD4(sc, EHCI_PORTSC(i))); -} - /* * Unused function - this is meant to be called from a kernel * debugger. @@ -1469,37 +1574,71 @@ ehci_dump_regs(ehci_softc_t *sc) void ehci_dump(void) { - ehci_dump_regs(theehci); + ehci_softc_t *sc = theehci; + int i; + printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), + EOREAD4(sc, EHCI_USBSTS), + EOREAD4(sc, EHCI_USBINTR)); + printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", + EOREAD4(sc, EHCI_FRINDEX), + EOREAD4(sc, EHCI_CTRLDSSEGMENT), + EOREAD4(sc, EHCI_PERIODICLISTBASE), + EOREAD4(sc, EHCI_ASYNCLISTADDR)); + for (i = 1; i <= sc->sc_noport; i++) + printf("port %d status=0x%08x\n", i, + EOREAD4(sc, EHCI_PORTSC(i))); } Static void -ehci_dump_link(ehci_link_t link, int type) +ehci_dump_regs(ehci_softc_t *sc) { - link = le32toh(link); - printf("0x%08x", link); - if (link & EHCI_LINK_TERMINATE) - printf(""); - else { - printf("<"); - if (type) { - switch (EHCI_LINK_TYPE(link)) { - case EHCI_LINK_ITD: printf("ITD"); break; - case EHCI_LINK_QH: printf("QH"); break; - case EHCI_LINK_SITD: printf("SITD"); break; - case EHCI_LINK_FSTN: printf("FSTN"); break; - } - } - printf(">"); - } -} + int i; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("cmd = 0x%08x sts = 0x%08x ien = 0x%08x", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), + EOREAD4(sc, EHCI_USBINTR), 0); + DPRINTF("frindex = 0x%08x ctrdsegm = 0x%08x periodic = 0x%08x " + "async = 0x%08x", + EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), + EOREAD4(sc, EHCI_PERIODICLISTBASE), + EOREAD4(sc, EHCI_ASYNCLISTADDR)); + for (i = 1; i <= sc->sc_noport; i += 2) { + if (i == sc->sc_noport) { + DPRINTF("port %d status = 0x%08x", i, + EOREAD4(sc, EHCI_PORTSC(i)), 0, 0); + } else { + DPRINTF( + "port %d status = 0x%08x port %d status = 0x%08x", + i, EOREAD4(sc, EHCI_PORTSC(i)), + i+1, EOREAD4(sc, EHCI_PORTSC(i+1))); + } + } +} + +#define ehci_dump_link(link, type) do { \ + DPRINTF(" link 0x%08x (T = %d):", \ + link, \ + link & EHCI_LINK_TERMINATE ? 1 : 0, 0, 0); \ + if (type) { \ + DPRINTF( \ + " ITD = %d QH = %d SITD = %d FSTN = %d",\ + EHCI_LINK_TYPE(link) == EHCI_LINK_ITD ? 1 : 0, \ + EHCI_LINK_TYPE(link) == EHCI_LINK_QH ? 1 : 0, \ + EHCI_LINK_TYPE(link) == EHCI_LINK_SITD ? 1 : 0, \ + EHCI_LINK_TYPE(link) == EHCI_LINK_FSTN ? 1 : 0); \ + } \ +} while(0) Static void ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) { + EHCIHIST_FUNC(); EHCIHIST_CALLED(); int i; - u_int32_t stop; + uint32_t stop = 0; - stop = 0; for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { ehci_dump_sqtd(sqtd); usb_syncmem(&sqtd->dma, @@ -1511,17 +1650,21 @@ ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) sqtd->offs + offsetof(ehci_qtd_t, qtd_next), sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); } - if (sqtd) - printf("dump aborted, too many TDs\n"); + if (!stop) + DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); } Static void ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) { + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); - printf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr); + + DPRINTFN(10, "QTD(%p) at 0x%08x:", sqtd, sqtd->physaddr, 0, 0); ehci_dump_qtd(&sqtd->qtd); + usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); } @@ -1529,58 +1672,106 @@ ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) Static void ehci_dump_qtd(ehci_qtd_t *qtd) { - u_int32_t s; - char sbuf[128]; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + uint32_t s = le32toh(qtd->qtd_status); - printf(" next="); ehci_dump_link(qtd->qtd_next, 0); - printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0); - printf("\n"); - s = le32toh(qtd->qtd_status); - snprintb(sbuf, sizeof(sbuf), - "\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR" - "\3MISSED\2SPLIT\1PING", EHCI_QTD_GET_STATUS(s)); - printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", - s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), - EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); - printf(" cerr=%d pid=%d stat=%s\n", EHCI_QTD_GET_CERR(s), - EHCI_QTD_GET_PID(s), sbuf); - for (s = 0; s < 5; s++) - printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s])); + DPRINTFN(10, + " next = 0x%08x altnext = 0x%08x status = 0x%08x", + qtd->qtd_next, qtd->qtd_altnext, s, 0); + DPRINTFN(10, + " toggle = %d ioc = %d bytes = %#x " + "c_page = %#x", EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_IOC(s), + EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_C_PAGE(s)); + DPRINTFN(10, + " cerr = %d pid = %d stat = %x", + EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), EHCI_QTD_GET_STATUS(s), + 0); + DPRINTFN(10, + "active =%d halted=%d buferr=%d babble=%d", + s & EHCI_QTD_ACTIVE ? 1 : 0, + s & EHCI_QTD_HALTED ? 1 : 0, + s & EHCI_QTD_BUFERR ? 1 : 0, + s & EHCI_QTD_BABBLE ? 1 : 0); + DPRINTFN(10, + "xacterr=%d missed=%d split =%d ping =%d", + s & EHCI_QTD_XACTERR ? 1 : 0, + s & EHCI_QTD_MISSEDMICRO ? 1 : 0, + s & EHCI_QTD_SPLITXSTATE ? 1 : 0, + s & EHCI_QTD_PINGSTATE ? 1 : 0); + DPRINTFN(10, + "buffer[0] = %#x buffer[1] = %#x " + "buffer[2] = %#x buffer[3] = %#x", + le32toh(qtd->qtd_buffer[0]), le32toh(qtd->qtd_buffer[1]), + le32toh(qtd->qtd_buffer[2]), le32toh(qtd->qtd_buffer[3])); + DPRINTFN(10, + "buffer[4] = %#x", le32toh(qtd->qtd_buffer[4]), 0, 0, 0); } Static void ehci_dump_sqh(ehci_soft_qh_t *sqh) { ehci_qh_t *qh = &sqh->qh; - u_int32_t endp, endphub; + ehci_link_t link; + uint32_t endp, endphub; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); - printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr); - printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n"); + + DPRINTFN(10, "QH(%p) at %#x:", sqh, sqh->physaddr, 0, 0); + link = le32toh(qh->qh_link); + ehci_dump_link(link, true); + endp = le32toh(qh->qh_endp); - printf(" endp=0x%08x\n", endp); - printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", - EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), - EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), - EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); - printf(" mpl=0x%x ctl=%d nrl=%d\n", - EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), - EHCI_QH_GET_NRL(endp)); + DPRINTFN(10, " endp = %#x", endp, 0, 0, 0); + DPRINTFN(10, " addr = 0x%02x inact = %d endpt = %d eps = %d", + EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), + EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp)); + DPRINTFN(10, " dtc = %d hrecl = %d", + EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp), 0, 0); + DPRINTFN(10, " ctl = %d nrl = %d mpl = %#x(%d)", + EHCI_QH_GET_CTL(endp),EHCI_QH_GET_NRL(endp), + EHCI_QH_GET_MPL(endp), EHCI_QH_GET_MPL(endp)); + endphub = le32toh(qh->qh_endphub); - printf(" endphub=0x%08x\n", endphub); - printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", - EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), - EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), - EHCI_QH_GET_MULT(endphub)); - printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n"); - printf("Overlay qTD:\n"); + DPRINTFN(10, " endphub = %#x", endphub, 0, 0, 0); + DPRINTFN(10, " smask = 0x%02x cmask = 0x%02x", + EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), 1, 0); + DPRINTFN(10, " huba = 0x%02x port = %d mult = %d", + EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), + EHCI_QH_GET_MULT(endphub), 0); + + link = le32toh(qh->qh_curqtd); + ehci_dump_link(link, false); + DPRINTFN(10, "Overlay qTD:", 0, 0, 0, 0); ehci_dump_qtd(&qh->qh_qtd); - usb_syncmem(&sqh->dma, sqh->offs, - sizeof(sqh->qh), BUS_DMASYNC_PREREAD); + + usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), + BUS_DMASYNC_PREREAD); +} + +Static void +ehci_dump_itds(ehci_soft_itd_t *itd) +{ + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + int i; + uint32_t stop = 0; + + for (i = 0; itd && i < 20 && !stop; itd = itd->xfer_next, i++) { + ehci_dump_itd(itd); + usb_syncmem(&itd->dma, + itd->offs + offsetof(ehci_itd_t, itd_next), + sizeof(itd->itd), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + stop = itd->itd.itd_next & htole32(EHCI_LINK_TERMINATE); + usb_syncmem(&itd->dma, + itd->offs + offsetof(ehci_itd_t, itd_next), + sizeof(itd->itd), BUS_DMASYNC_PREREAD); + } + if (!stop) + DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); } -#if notyet Static void ehci_dump_itd(struct ehci_soft_itd *itd) { @@ -1588,119 +1779,145 @@ ehci_dump_itd(struct ehci_soft_itd *itd) ehci_isoc_bufr_ptr_t b, b2, b3; int i; - printf("ITD: next phys=%X\n", itd->itd.itd_next); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("ITD: next phys = %#x", itd->itd.itd_next, 0, 0, 0); for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { t = le32toh(itd->itd.itd_ctl[i]); - printf("ITDctl %d: stat=%X len=%X ioc=%X pg=%X offs=%X\n", i, - EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t), + DPRINTF("ITDctl %d: stat = %x len = %x", + i, EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t), 0); + DPRINTF(" ioc = %x pg = %x offs = %x", EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t), - EHCI_ITD_GET_OFFS(t)); + EHCI_ITD_GET_OFFS(t), 0); } - printf("ITDbufr: "); + DPRINTF("ITDbufr: ", 0, 0, 0, 0); for (i = 0; i < EHCI_ITD_NBUFFERS; i++) - printf("%X,", EHCI_ITD_GET_BPTR(le32toh(itd->itd.itd_bufr[i]))); + DPRINTF(" %x", + EHCI_ITD_GET_BPTR(le32toh(itd->itd.itd_bufr[i])), 0, 0, 0); b = le32toh(itd->itd.itd_bufr[0]); b2 = le32toh(itd->itd.itd_bufr[1]); b3 = le32toh(itd->itd.itd_bufr[2]); - printf("\nep=%X daddr=%X dir=%d maxpkt=%X multi=%X\n", - EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2), - EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3)); + DPRINTF(" ep = %x daddr = %x dir = %d", + EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2), 0); + DPRINTF(" maxpkt = %x multi = %x", + EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3), 0, 0); } Static void ehci_dump_sitd(struct ehci_soft_itd *itd) { - printf("SITD %p next=%p prev=%p xfernext=%p physaddr=%X slot=%d\n", - itd, itd->u.frame_list.next, itd->u.frame_list.prev, - itd->xfer_next, itd->physaddr, itd->slot); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("SITD %p next = %p prev = %p", + itd, itd->frame_list.next, itd->frame_list.prev, 0); + DPRINTF(" xfernext=%p physaddr=%X slot=%d", + itd->xfer_next, itd->physaddr, itd->slot, 0); } -#endif -#ifdef DIAGNOSTIC Static void ehci_dump_exfer(struct ehci_xfer *ex) { - printf("ehci_dump_exfer: ex=%p sqtdstart=%p end=%p itdstart=%p end=%p isdone=%d\n", ex, ex->sqtdstart, ex->sqtdend, ex->itdstart, ex->itdend, ex->isdone); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("ex = %p type %d isdone", ex, ex->ex_type, + ex->ex_isdone, 0); + + switch (ex->ex_type) { + case EX_CTRL: + DPRINTF(" setup = %p data = %p status = %p", + ex->ex_setup, ex->ex_data, ex->ex_status, 0); + break; + case EX_BULK: + case EX_INTR: + DPRINTF(" qtdstart = %p qtdend = %p", + ex->ex_sqtdstart, ex->ex_sqtdend, 0, 0); + break; + case EX_ISOC: + DPRINTF(" itdstart = %p itdend = %p", + ex->ex_itdstart, ex->ex_itdend, 0, 0); + break; + case EX_FS_ISOC: + DPRINTF(" sitdstart = %p sitdend = %p", + ex->ex_sitdstart, ex->ex_sitdend, 0, 0); + break; + default: + DPRINTF(" unknown type", 0, 0, 0, 0); + } } #endif -#endif Static usbd_status -ehci_open(usbd_pipe_handle pipe) +ehci_open(struct usbd_pipe *pipe) { - usbd_device_handle dev = pipe->device; - ehci_softc_t *sc = dev->bus->hci_private; - usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; - u_int8_t addr = dev->address; - u_int8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes); - struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + struct usbd_device *dev = pipe->up_dev; + ehci_softc_t *sc = EHCI_PIPE2SC(pipe); + usb_endpoint_descriptor_t *ed = pipe->up_endpoint->ue_edesc; + uint8_t rhaddr = dev->ud_bus->ub_rhaddr; + uint8_t addr = dev->ud_addr; + uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes); + struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); ehci_soft_qh_t *sqh; usbd_status err; int ival, speed, naks; int hshubaddr, hshubport; - DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", - pipe, addr, ed->bEndpointAddress, sc->sc_addr)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - if (dev->myhsport) { + DPRINTF("pipe=%p, addr=%d, endpt=%d (%d)", pipe, addr, + ed->bEndpointAddress, rhaddr); + + if (dev->ud_myhsport) { /* * When directly attached FS/LS device while doing embedded * transaction translations and we are the hub, set the hub * address to 0 (us). */ if (!(sc->sc_flags & EHCIF_ETTF) - || (dev->myhsport->parent->address != sc->sc_addr)) { - hshubaddr = dev->myhsport->parent->address; + || (dev->ud_myhsport->up_parent->ud_addr != rhaddr)) { + hshubaddr = dev->ud_myhsport->up_parent->ud_addr; } else { hshubaddr = 0; } - hshubport = dev->myhsport->portno; + hshubport = dev->ud_myhsport->up_portno; } else { hshubaddr = 0; hshubport = 0; } if (sc->sc_dying) - return (USBD_IOERROR); + return USBD_IOERROR; /* toggle state needed for bulk endpoints */ - epipe->nexttoggle = pipe->endpoint->datatoggle; + epipe->nexttoggle = pipe->up_endpoint->ue_toggle; - if (addr == sc->sc_addr) { + if (addr == rhaddr) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: - pipe->methods = &ehci_root_ctrl_methods; + pipe->up_methods = &roothub_ctrl_methods; break; - case UE_DIR_IN | EHCI_INTR_ENDPT: - pipe->methods = &ehci_root_intr_methods; + case UE_DIR_IN | USBROOTHUB_INTR_ENDPT: + pipe->up_methods = &ehci_root_intr_methods; break; default: - DPRINTF(("ehci_open: bad bEndpointAddress 0x%02x\n", - ed->bEndpointAddress)); - return (USBD_INVAL); + DPRINTF("bad bEndpointAddress 0x%02x", + ed->bEndpointAddress, 0, 0, 0); + return USBD_INVAL; } - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; } /* XXX All this stuff is only valid for async. */ - switch (dev->speed) { + switch (dev->ud_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", dev->speed); + default: panic("ehci_open: bad device speed %d", dev->ud_speed); } - if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) { - aprint_error_dev(sc->sc_dev, "error opening low/full speed " - "isoc endpoint.\n"); - aprint_normal_dev(sc->sc_dev, "a low/full speed device is " - "attached to a USB2 hub, and transaction translations are " - "not yet supported.\n"); - aprint_normal_dev(sc->sc_dev, "reattach the device to the " - "root hub instead.\n"); - DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n", - hshubaddr, hshubport)); + if (speed == EHCI_QH_SPEED_LOW && xfertype == UE_ISOCHRONOUS) { + DPRINTF("hshubaddr=%d hshubport=%d", hshubaddr, hshubport, 0, + 0); return USBD_INVAL; } @@ -1716,7 +1933,7 @@ ehci_open(usbd_pipe_handle pipe) if (xfertype != UE_ISOCHRONOUS) { sqh = ehci_alloc_sqh(sc); if (sqh == NULL) - return (USBD_NOMEM); + return USBD_NOMEM; /* qh_link filled when the QH is added */ sqh->qh.qh_endp = htole32( EHCI_QH_SET_ADDR(addr) | @@ -1736,7 +1953,8 @@ ehci_open(usbd_pipe_handle pipe) sqh->qh.qh_endphub |= htole32( EHCI_QH_SET_PORT(hshubport) | EHCI_QH_SET_HUBA(hshubaddr) | - EHCI_QH_SET_CMASK(0x08) /* XXX */ + (xfertype == UE_INTERRUPT ? + EHCI_QH_SET_CMASK(0x08) : 0) ); sqh->qh.qh_curqtd = EHCI_NULL; /* Fill the overlay qTD */ @@ -1754,27 +1972,27 @@ ehci_open(usbd_pipe_handle pipe) switch (xfertype) { case UE_CONTROL: err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), - 0, &epipe->u.ctl.reqdma); + 0, &epipe->ctrl.reqdma); #ifdef EHCI_DEBUG if (err) printf("ehci_open: usb_allocmem()=%d\n", err); #endif if (err) goto bad; - pipe->methods = &ehci_device_ctrl_methods; + pipe->up_methods = &ehci_device_ctrl_methods; mutex_enter(&sc->sc_lock); ehci_add_qh(sc, sqh, sc->sc_async_head); mutex_exit(&sc->sc_lock); break; case UE_BULK: - pipe->methods = &ehci_device_bulk_methods; + pipe->up_methods = &ehci_device_bulk_methods; mutex_enter(&sc->sc_lock); ehci_add_qh(sc, sqh, sc->sc_async_head); mutex_exit(&sc->sc_lock); break; case UE_INTERRUPT: - pipe->methods = &ehci_device_intr_methods; - ival = pipe->interval; + pipe->up_methods = &ehci_device_intr_methods; + ival = pipe->up_interval; if (ival == USBD_DEFAULT_INTERVAL) { if (speed == EHCI_QH_SPEED_HIGH) { if (ed->bInterval > 16) { @@ -1794,7 +2012,11 @@ ehci_open(usbd_pipe_handle pipe) goto bad; break; case UE_ISOCHRONOUS: - pipe->methods = &ehci_device_isoc_methods; + pipe->up_serialise = false; + if (speed == EHCI_QH_SPEED_HIGH) + pipe->up_methods = &ehci_device_isoc_methods; + else + pipe->up_methods = &ehci_device_fs_isoc_methods; if (ed->bInterval == 0 || ed->bInterval > 16) { printf("ehci: opening pipe with invalid bInterval\n"); err = USBD_INVAL; @@ -1805,20 +2027,23 @@ ehci_open(usbd_pipe_handle pipe) err = USBD_INVAL; goto bad; } - epipe->u.isoc.next_frame = 0; - epipe->u.isoc.cur_xfers = 0; + epipe->isoc.next_frame = 0; + epipe->isoc.cur_xfers = 0; break; default: - DPRINTF(("ehci: bad xfer type %d\n", xfertype)); + DPRINTF("bad xfer type %d", xfertype, 0, 0, 0); err = USBD_INVAL; goto bad; } - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; bad: - if (sqh != NULL) + if (sqh != NULL) { + mutex_enter(&sc->sc_lock); ehci_free_sqh(sc, sqh); - return (err); + mutex_exit(&sc->sc_lock); + } + return err; } /* @@ -1830,22 +2055,27 @@ ehci_add_qh(ehci_softc_t *sc, ehci_soft_ KASSERT(mutex_owned(&sc->sc_lock)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), sizeof(head->qh.qh_link), BUS_DMASYNC_POSTWRITE); + sqh->next = head->next; sqh->qh.qh_link = head->qh.qh_link; + usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), sizeof(sqh->qh.qh_link), BUS_DMASYNC_PREWRITE); + head->next = sqh; head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); + usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), sizeof(head->qh.qh_link), BUS_DMASYNC_PREWRITE); #ifdef EHCI_DEBUG - if (ehcidebug > 5) { - printf("ehci_add_qh:\n"); - ehci_dump_sqh(sqh); - } + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); #endif } @@ -1878,7 +2108,7 @@ Static void ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) { int i; - u_int32_t status; + uint32_t status; /* Save toggle bit and ping status. */ usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), @@ -1922,350 +2152,196 @@ ehci_sync_hc(ehci_softc_t *sc) KASSERT(mutex_owned(&sc->sc_lock)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + if (sc->sc_dying) { - DPRINTFN(2,("ehci_sync_hc: dying\n")); + DPRINTF("dying", 0, 0, 0, 0); return; } - DPRINTFN(2,("ehci_sync_hc: enter\n")); /* ask for doorbell */ EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); - DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", - EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); + DPRINTF("cmd = 0x%08x sts = 0x%08x", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); + error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); /* bell wait */ - DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", - EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); + + DPRINTF("cmd = 0x%08x sts = 0x%08x ... done", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); #ifdef DIAGNOSTIC - if (error) - printf("ehci_sync_hc: cv_timedwait() = %d\n", error); + if (error == EWOULDBLOCK) { + printf("ehci_sync_hc: timed out\n"); + } else if (error) { + printf("ehci_sync_hc: cv_timedwait: error %d\n", error); + } #endif - DPRINTFN(2,("ehci_sync_hc: exit\n")); } Static void -ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer) +ehci_remove_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) { - struct ehci_soft_itd *itd, *prev; - prev = NULL; + KASSERT(mutex_owned(&sc->sc_lock)); - if (exfer->itdstart == NULL || exfer->itdend == NULL) - panic("ehci isoc xfer being freed, but with no itd chain\n"); + for (; itd != NULL; itd = itd->xfer_next) { + struct ehci_soft_itd *prev = itd->frame_list.prev; - for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) { - prev = itd->u.frame_list.prev; /* Unlink itd from hardware chain, or frame array */ if (prev == NULL) { /* We're at the table head */ - sc->sc_softitds[itd->slot] = itd->u.frame_list.next; + sc->sc_softitds[itd->slot] = itd->frame_list.next; sc->sc_flist[itd->slot] = itd->itd.itd_next; usb_syncmem(&sc->sc_fldma, sizeof(ehci_link_t) * itd->slot, - sizeof(ehci_link_t), + sizeof(ehci_link_t), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - if (itd->u.frame_list.next != NULL) - itd->u.frame_list.next->u.frame_list.prev = NULL; + if (itd->frame_list.next != NULL) + itd->frame_list.next->frame_list.prev = NULL; } else { /* XXX this part is untested... */ prev->itd.itd_next = itd->itd.itd_next; usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_next), - sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE); + sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE); - prev->u.frame_list.next = itd->u.frame_list.next; - if (itd->u.frame_list.next != NULL) - itd->u.frame_list.next->u.frame_list.prev = prev; + prev->frame_list.next = itd->frame_list.next; + if (itd->frame_list.next != NULL) + itd->frame_list.next->frame_list.prev = prev; } } +} - prev = NULL; - for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) { - if (prev != NULL) - ehci_free_itd(sc, prev); - prev = itd; +Static void +ehci_free_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) +{ + struct ehci_soft_itd *next; + + mutex_enter(&sc->sc_lock); + next = NULL; + for (; itd != NULL; itd = next) { + next = itd->xfer_next; + ehci_free_itd_locked(sc, itd); } - if (prev) - ehci_free_itd(sc, prev); - exfer->itdstart = NULL; - exfer->itdend = NULL; + mutex_exit(&sc->sc_lock); } -/***********/ - -/* - * Data structures and routines to emulate the root hub. - */ -Static usb_device_descriptor_t ehci_devd = { - USB_DEVICE_DESCRIPTOR_SIZE, - UDESC_DEVICE, /* type */ - {0x00, 0x02}, /* USB version */ - UDCLASS_HUB, /* class */ - UDSUBCLASS_HUB, /* subclass */ - UDPROTO_HSHUBSTT, /* protocol */ - 64, /* max packet */ - {0},{0},{0x00,0x01}, /* device id */ - 1,2,0, /* string indicies */ - 1 /* # of configurations */ -}; +Static void +ehci_remove_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) +{ -Static const 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 -}; + KASSERT(mutex_owned(&sc->sc_lock)); -Static const usb_config_descriptor_t ehci_confd = { - USB_CONFIG_DESCRIPTOR_SIZE, - UDESC_CONFIG, - {USB_CONFIG_DESCRIPTOR_SIZE + - USB_INTERFACE_DESCRIPTOR_SIZE + - USB_ENDPOINT_DESCRIPTOR_SIZE}, - 1, - 1, - 0, - UC_ATTR_MBO | UC_SELF_POWERED, - 0 /* max power */ -}; + for (; sitd != NULL; sitd = sitd->xfer_next) { + struct ehci_soft_sitd *prev = sitd->frame_list.prev; -Static const usb_interface_descriptor_t ehci_ifcd = { - USB_INTERFACE_DESCRIPTOR_SIZE, - UDESC_INTERFACE, - 0, - 0, - 1, - UICLASS_HUB, - UISUBCLASS_HUB, - UIPROTO_HSHUBSTT, - 0 -}; + /* Unlink sitd from hardware chain, or frame array */ + if (prev == NULL) { /* We're at the table head */ + sc->sc_softsitds[sitd->slot] = sitd->frame_list.next; + sc->sc_flist[sitd->slot] = sitd->sitd.sitd_next; + usb_syncmem(&sc->sc_fldma, + sizeof(ehci_link_t) * sitd->slot, + sizeof(ehci_link_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); -Static const usb_endpoint_descriptor_t ehci_endpd = { - USB_ENDPOINT_DESCRIPTOR_SIZE, - UDESC_ENDPOINT, - UE_DIR_IN | EHCI_INTR_ENDPT, - UE_INTERRUPT, - {8, 0}, /* max packet */ - 12 -}; + if (sitd->frame_list.next != NULL) + sitd->frame_list.next->frame_list.prev = NULL; + } else { + /* XXX this part is untested... */ + prev->sitd.sitd_next = sitd->sitd.sitd_next; + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_next), + sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE); -Static const usb_hub_descriptor_t ehci_hubd = { - USB_HUB_DESCRIPTOR_SIZE, - UDESC_HUB, - 0, - {0,0}, - 0, - 0, - {""}, - {""}, -}; + prev->frame_list.next = sitd->frame_list.next; + if (sitd->frame_list.next != NULL) + sitd->frame_list.next->frame_list.prev = prev; + } + } +} -/* - * Simulate a hardware hub by handling all the necessary requests. - */ -Static usbd_status -ehci_root_ctrl_transfer(usbd_xfer_handle xfer) +Static void +ehci_free_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - usbd_status err; - /* Insert last in queue. */ mutex_enter(&sc->sc_lock); - err = usb_insert_transfer(xfer); + struct ehci_soft_sitd *next = NULL; + for (; sitd != NULL; sitd = next) { + next = sitd->xfer_next; + ehci_free_sitd_locked(sc, sitd); + } mutex_exit(&sc->sc_lock); - if (err) - return (err); - - /* Pipe isn't running, start first */ - return (ehci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } -Static usbd_status -ehci_root_ctrl_start(usbd_xfer_handle xfer) +/***********/ + +Static int +ehci_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req, + void *buf, int buflen) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - usb_device_request_t *req; - void *buf = NULL; - int port, i; - int len, value, index, l, totlen = 0; - usb_port_status_t ps; + ehci_softc_t *sc = EHCI_BUS2SC(bus); usb_hub_descriptor_t hubd; - usbd_status err; - u_int32_t v; + usb_port_status_t ps; + uint16_t len, value, index; + int l, totlen = 0; + int port, i; + uint32_t v; - if (sc->sc_dying) - return (USBD_IOERROR); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); -#ifdef DIAGNOSTIC - if (!(xfer->rqflags & URQ_REQUEST)) - /* XXX panic */ - return (USBD_INVAL); -#endif - req = &xfer->request; + if (sc->sc_dying) + return -1; - DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n", - req->bmRequestType, req->bRequest)); + DPRINTF("type=0x%02x request=%02x", req->bmRequestType, req->bRequest, + 0, 0); len = UGETW(req->wLength); value = UGETW(req->wValue); index = UGETW(req->wIndex); - if (len != 0) - buf = KERNADDR(&xfer->dmabuf, 0); - #define C(x,y) ((x) | ((y) << 8)) - switch(C(req->bRequest, req->bmRequestType)) { - case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): - case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): - case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): - /* - * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops - * for the integrated root hub. - */ - break; - case C(UR_GET_CONFIG, UT_READ_DEVICE): - if (len > 0) { - *(u_int8_t *)buf = sc->sc_conf; - totlen = 1; - } - break; + switch (C(req->bRequest, req->bmRequestType)) { case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): - DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value)); if (len == 0) break; - switch(value >> 8) { - case UDESC_DEVICE: - if ((value & 0xff) != 0) { - err = USBD_IOERROR; - goto ret; - } - totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); - USETW(ehci_devd.idVendor, sc->sc_id_vendor); - memcpy(buf, &ehci_devd, l); + switch (value) { + case C(0, UDESC_DEVICE): { + usb_device_descriptor_t devd; + totlen = min(buflen, sizeof(devd)); + memcpy(&devd, buf, totlen); + USETW(devd.idVendor, sc->sc_id_vendor); + memcpy(buf, &devd, totlen); 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); + + } +#define sd ((usb_string_descriptor_t *)buf) + case C(1, UDESC_STRING): + /* Vendor */ + totlen = usb_makestrdesc(sd, len, sc->sc_vendor); 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; - goto ret; - } - 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); - totlen += l; - memcpy(buf, &ehci_ifcd, l); - buf = (char *)buf + l; - len -= l; - l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); - totlen += l; - memcpy(buf, &ehci_endpd, l); + case C(2, UDESC_STRING): + /* Product */ + totlen = usb_makestrdesc(sd, len, "EHCI root hub"); break; - case UDESC_STRING: -#define sd ((usb_string_descriptor_t *)buf) - switch (value & 0xff) { - case 0: /* Language table */ - totlen = usb_makelangtbl(sd, len); - break; - case 1: /* Vendor */ - totlen = usb_makestrdesc(sd, len, - sc->sc_vendor); - break; - case 2: /* Product */ - totlen = usb_makestrdesc(sd, len, - "EHCI root hub"); - break; - } #undef sd - break; default: - err = USBD_IOERROR; - goto ret; - } - break; - case C(UR_GET_INTERFACE, UT_READ_INTERFACE): - if (len > 0) { - *(u_int8_t *)buf = 0; - totlen = 1; - } - break; - case C(UR_GET_STATUS, UT_READ_DEVICE): - if (len > 1) { - USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED); - totlen = 2; - } - break; - case C(UR_GET_STATUS, UT_READ_INTERFACE): - case C(UR_GET_STATUS, UT_READ_ENDPOINT): - if (len > 1) { - USETW(((usb_status_t *)buf)->wStatus, 0); - totlen = 2; - } - break; - case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): - if (value >= USB_MAX_DEVICES) { - err = USBD_IOERROR; - goto ret; - } - sc->sc_addr = value; - break; - case C(UR_SET_CONFIG, UT_WRITE_DEVICE): - if (value != 0 && value != 1) { - err = USBD_IOERROR; - goto ret; + /* default from usbroothub */ + return buflen; } - sc->sc_conf = value; - break; - case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): - break; - case C(UR_SET_FEATURE, UT_WRITE_DEVICE): - case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): - case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): - err = USBD_IOERROR; - goto ret; - case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): - break; - case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; + /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): - DPRINTFN(4, ("ehci_root_ctrl_start: UR_CLEAR_PORT_FEATURE " - "port=%d feature=%d\n", - index, value)); + DPRINTF("UR_CLEAR_PORT_FEATURE port=%d feature=%d", index, + value, 0, 0); if (index < 1 || index > sc->sc_noport) { - err = USBD_IOERROR; - goto ret; + return -1; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port); - DPRINTFN(4, ("ehci_root_ctrl_start: portsc=0x%08x\n", v)); + DPRINTF("portsc=0x%08x", v, 0, 0, 0); v &= ~EHCI_PS_CLEAR; - switch(value) { + switch (value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v &~ EHCI_PS_PE); break; @@ -2289,12 +2365,10 @@ ehci_root_ctrl_start(usbd_xfer_handle xf EOWRITE4(sc, port, v &~ EHCI_PS_PP); break; case UHF_PORT_TEST: - DPRINTFN(2,("ehci_root_ctrl_start: clear port test " - "%d\n", index)); + DPRINTF("clear port test %d", index, 0, 0, 0); break; case UHF_PORT_INDICATOR: - DPRINTFN(2,("ehci_root_ctrl_start: clear port ind " - "%d\n", index)); + DPRINTF("clear port ind %d", index, 0, 0, 0); EOWRITE4(sc, port, v &~ EHCI_PS_PIC); break; case UHF_C_PORT_CONNECTION: @@ -2313,8 +2387,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xf sc->sc_isreset[index] = 0; break; default: - err = USBD_IOERROR; - goto ret; + return -1; } #if 0 switch(value) { @@ -2332,10 +2405,10 @@ ehci_root_ctrl_start(usbd_xfer_handle xf if (len == 0) break; if ((value & 0xff) != 0) { - err = USBD_IOERROR; - goto ret; + return -1; } - hubd = ehci_hubd; + totlen = min(buflen, sizeof(hubd)); + memcpy(&hubd, buf, totlen); hubd.bNbrPorts = sc->sc_noport; v = EOREAD4(sc, EHCI_HCSPARAMS); USETW(hubd.wHubCharacteristics, @@ -2346,31 +2419,26 @@ ehci_root_ctrl_start(usbd_xfer_handle xf for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; - l = min(len, hubd.bDescLength); - totlen = l; - memcpy(buf, &hubd, l); + totlen = min(totlen, hubd.bDescLength); + memcpy(buf, &hubd, totlen); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if (len != 4) { - err = USBD_IOERROR; - goto ret; + return -1; } memset(buf, 0, len); /* ? XXX */ totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): - DPRINTFN(8,("ehci_root_ctrl_start: get port status i=%d\n", - index)); + DPRINTF("get port status i=%d", index, 0, 0, 0); if (index < 1 || index > sc->sc_noport) { - err = USBD_IOERROR; - goto ret; + return -1; } if (len != 4) { - err = USBD_IOERROR; - goto ret; + return -1; } v = EOREAD4(sc, EHCI_PORTSC(index)); - DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n", v)); + DPRINTF("port status=0x%04x", v, 0, 0, 0); i = UPS_HIGH_SPEED; if (sc->sc_flags & EHCIF_ETTF) { @@ -2397,23 +2465,20 @@ ehci_root_ctrl_start(usbd_xfer_handle xf if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (sc->sc_isreset[index]) i |= UPS_C_PORT_RESET; USETW(ps.wPortChange, i); - l = min(len, sizeof ps); - memcpy(buf, &ps, l); - totlen = l; + totlen = min(len, sizeof(ps)); + memcpy(buf, &ps, totlen); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): - err = USBD_IOERROR; - goto ret; + return -1; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if (index < 1 || index > sc->sc_noport) { - err = USBD_IOERROR; - goto ret; + return -1; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port); - DPRINTFN(4, ("ehci_root_ctrl_start: portsc=0x%08x\n", v)); + DPRINTF("portsc=0x%08x", v, 0, 0, 0); v &= ~EHCI_PS_CLEAR; switch(value) { case UHF_PORT_ENABLE: @@ -2423,8 +2488,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xf EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_PORT_RESET: - DPRINTFN(5,("ehci_root_ctrl_start: reset port %d\n", - index)); + DPRINTF("reset port %d", index, 0, 0, 0); if (EHCI_PS_IS_LOWSPEED(v) && sc->sc_ncomp > 0 && !(sc->sc_flags & EHCIF_ETTF)) { @@ -2441,8 +2505,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xf /* Wait for reset to complete. */ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); if (sc->sc_dying) { - err = USBD_IOERROR; - goto ret; + return -1; } /* * An embedded transaction translator will automatically @@ -2457,17 +2520,16 @@ ehci_root_ctrl_start(usbd_xfer_handle xf usb_delay_ms(&sc->sc_bus, EHCI_PORT_RESET_COMPLETE); if (sc->sc_dying) { - err = USBD_IOERROR; - goto ret; + return -1; } } v = EOREAD4(sc, port); - DPRINTF(("ehci after reset, status=0x%08x\n", v)); + DPRINTF("ehci after reset, status=0x%08x", v, 0, 0, 0); if (v & EHCI_PS_PR) { printf("%s: port reset timeout\n", device_xname(sc->sc_dev)); - return (USBD_TIMEOUT); + return USBD_TIMEOUT; } if (!(v & EHCI_PS_PE)) { /* Not a high speed device, give up ownership.*/ @@ -2475,28 +2537,24 @@ ehci_root_ctrl_start(usbd_xfer_handle xf break; } sc->sc_isreset[index] = 1; - DPRINTF(("ehci port %d reset, status = 0x%08x\n", - index, v)); + DPRINTF("ehci port %d reset, status = 0x%08x", index, + v, 0, 0); break; case UHF_PORT_POWER: - DPRINTFN(2,("ehci_root_ctrl_start: set port power " - "%d (has PPC = %d)\n", index, - sc->sc_hasppc)); + DPRINTF("set port power %d (has PPC = %d)", index, + sc->sc_hasppc, 0, 0); if (sc->sc_hasppc) EOWRITE4(sc, port, v | EHCI_PS_PP); break; case UHF_PORT_TEST: - DPRINTFN(2,("ehci_root_ctrl_start: set port test " - "%d\n", index)); + DPRINTF("set port test %d", index, 0, 0, 0); break; case UHF_PORT_INDICATOR: - DPRINTFN(2,("ehci_root_ctrl_start: set port ind " - "%d\n", index)); + DPRINTF("set port ind %d", index, 0, 0, 0); EOWRITE4(sc, port, v | EHCI_PS_PIC); break; default: - err = USBD_IOERROR; - goto ret; + return -1; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): @@ -2505,26 +2563,26 @@ ehci_root_ctrl_start(usbd_xfer_handle xf case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: - err = USBD_IOERROR; - goto ret; + /* default from usbroothub */ + DPRINTF("returning %d (usbroothub default)", buflen, 0, 0, 0); + + return buflen; } - xfer->actlen = totlen; - err = USBD_NORMAL_COMPLETION; - ret: - mutex_enter(&sc->sc_lock); - xfer->status = err; - usb_transfer_complete(xfer); - mutex_exit(&sc->sc_lock); - return (USBD_IN_PROGRESS); + + DPRINTF("returning %d", totlen, 0, 0, 0); + + return totlen; } Static void ehci_disown(ehci_softc_t *sc, int index, int lowspeed) { int port; - u_int32_t v; + uint32_t v; - DPRINTF(("ehci_disown: index=%d lowspeed=%d\n", index, lowspeed)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("index=%d lowspeed=%d", index, lowspeed, 0, 0); #ifdef DIAGNOSTIC if (sc->sc_npcomp != 0) { int i = (index-1) / sc->sc_npcomp; @@ -2546,31 +2604,10 @@ ehci_disown(ehci_softc_t *sc, int index, EOWRITE4(sc, port, v | EHCI_PS_PO); } -/* Abort a root control request. */ -Static void -ehci_root_ctrl_abort(usbd_xfer_handle xfer) -{ - /* Nothing to do, all transfers are synchronous. */ -} - -/* Close the root pipe. */ -Static void -ehci_root_ctrl_close(usbd_pipe_handle pipe) -{ - DPRINTF(("ehci_root_ctrl_close\n")); - /* Nothing to do. */ -} - -Static void -ehci_root_ctrl_done(usbd_xfer_handle xfer) -{ - xfer->hcpriv = NULL; -} - Static usbd_status -ehci_root_intr_transfer(usbd_xfer_handle xfer) +ehci_root_intr_transfer(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); usbd_status err; /* Insert last in queue. */ @@ -2578,60 +2615,58 @@ ehci_root_intr_transfer(usbd_xfer_handle err = usb_insert_transfer(xfer); mutex_exit(&sc->sc_lock); if (err) - return (err); + return err; /* Pipe isn't running, start first */ - return (ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); + return ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); } Static usbd_status -ehci_root_intr_start(usbd_xfer_handle xfer) +ehci_root_intr_start(struct usbd_xfer *xfer) { - usbd_pipe_handle pipe = xfer->pipe; - ehci_softc_t *sc = pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); if (sc->sc_dying) - return (USBD_IOERROR); + return USBD_IOERROR; mutex_enter(&sc->sc_lock); sc->sc_intrxfer = xfer; mutex_exit(&sc->sc_lock); - return (USBD_IN_PROGRESS); + return USBD_IN_PROGRESS; } /* Abort a root interrupt request. */ Static void -ehci_root_intr_abort(usbd_xfer_handle xfer) +ehci_root_intr_abort(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); KASSERT(mutex_owned(&sc->sc_lock)); - KASSERT(xfer->pipe->intrxfer == xfer); + KASSERT(xfer->ux_pipe->up_intrxfer == xfer); sc->sc_intrxfer = NULL; - xfer->status = USBD_CANCELLED; + xfer->ux_status = USBD_CANCELLED; usb_transfer_complete(xfer); } /* Close the root pipe. */ Static void -ehci_root_intr_close(usbd_pipe_handle pipe) +ehci_root_intr_close(struct usbd_pipe *pipe) { - ehci_softc_t *sc = pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_PIPE2SC(pipe); - KASSERT(mutex_owned(&sc->sc_lock)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTF(("ehci_root_intr_close\n")); + KASSERT(mutex_owned(&sc->sc_lock)); sc->sc_intrxfer = NULL; } Static void -ehci_root_intr_done(usbd_xfer_handle xfer) +ehci_root_intr_done(struct usbd_xfer *xfer) { - xfer->hcpriv = NULL; } /************************/ @@ -2644,8 +2679,13 @@ ehci_alloc_sqh(ehci_softc_t *sc) int i, offs; usb_dma_t dma; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + mutex_enter(&sc->sc_lock); if (sc->sc_freeqhs == NULL) { - DPRINTFN(2, ("ehci_alloc_sqh: allocating chunk\n")); + DPRINTF("allocating chunk", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); + err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK, EHCI_PAGE_SIZE, &dma); #ifdef EHCI_DEBUG @@ -2653,8 +2693,10 @@ ehci_alloc_sqh(ehci_softc_t *sc) printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); #endif if (err) - return (NULL); - for(i = 0; i < EHCI_SQH_CHUNK; i++) { + return NULL; + + mutex_enter(&sc->sc_lock); + for (i = 0; i < EHCI_SQH_CHUNK; i++) { offs = i * EHCI_SQH_SIZE; sqh = KERNADDR(&dma, offs); sqh->physaddr = DMAADDR(&dma, offs); @@ -2666,14 +2708,18 @@ ehci_alloc_sqh(ehci_softc_t *sc) } sqh = sc->sc_freeqhs; sc->sc_freeqhs = sqh->next; + mutex_exit(&sc->sc_lock); + memset(&sqh->qh, 0, sizeof(ehci_qh_t)); sqh->next = NULL; - return (sqh); + return sqh; } Static void ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) { + KASSERT(mutex_owned(&sc->sc_lock)); + sqh->next = sc->sc_freeqhs; sc->sc_freeqhs = sqh; } @@ -2686,8 +2732,12 @@ ehci_alloc_sqtd(ehci_softc_t *sc) int i, offs; usb_dma_t dma; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + mutex_enter(&sc->sc_lock); if (sc->sc_freeqtds == NULL) { - DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n")); + DPRINTF("allocating chunk", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK, EHCI_PAGE_SIZE, &dma); @@ -2698,7 +2748,8 @@ ehci_alloc_sqtd(ehci_softc_t *sc) if (err) goto done; - for(i = 0; i < EHCI_SQTD_CHUNK; i++) { + mutex_enter(&sc->sc_lock); + for (i = 0; i < EHCI_SQTD_CHUNK; i++) { offs = i * EHCI_SQTD_SIZE; sqtd = KERNADDR(&dma, offs); sqtd->physaddr = DMAADDR(&dma, offs); @@ -2712,249 +2763,316 @@ ehci_alloc_sqtd(ehci_softc_t *sc) sqtd = sc->sc_freeqtds; sc->sc_freeqtds = sqtd->nextqtd; + mutex_exit(&sc->sc_lock); + memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); sqtd->nextqtd = NULL; sqtd->xfer = NULL; done: - return (sqtd); + return sqtd; } Static void ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd) { - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); - + mutex_enter(&sc->sc_lock); sqtd->nextqtd = sc->sc_freeqtds; sc->sc_freeqtds = sqtd; + mutex_exit(&sc->sc_lock); } -Static usbd_status -ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, - int alen, int rd, usbd_xfer_handle xfer, - ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep) -{ - ehci_soft_qtd_t *next, *cur; - ehci_physaddr_t nextphys; - u_int32_t qtdstatus; - int len, curlen, mps; - int i, tog; - int pages, pageoffs; - bus_size_t curoffs; - vaddr_t va, va_offs; - usb_dma_t *dma = &xfer->dmabuf; - u_int16_t flags = xfer->flags; - paddr_t a; - - DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); - - len = alen; - qtdstatus = EHCI_QTD_ACTIVE | - EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | - EHCI_QTD_SET_CERR(3) - /* IOC set below */ - /* BYTES set below */ - ; - mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); - tog = epipe->nexttoggle; - qtdstatus |= EHCI_QTD_SET_TOGGLE(tog); - - cur = ehci_alloc_sqtd(sc); - *sp = cur; - if (cur == NULL) - goto nomem; - - usb_syncmem(dma, 0, alen, - rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); - curoffs = 0; - for (;;) { - /* The EHCI hardware can handle at most 5 pages. */ - va_offs = (vaddr_t)KERNADDR(dma, curoffs); - va_offs = EHCI_PAGE_OFFSET(va_offs); - if (len-curoffs < EHCI_QTD_NBUFFERS*EHCI_PAGE_SIZE - va_offs) { - /* we can handle it in this QTD */ - curlen = len - curoffs; - } else { - /* must use multiple TDs, fill as much as possible. */ - curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE - va_offs; - - /* the length must be a multiple of the max size */ - curlen -= curlen % mps; - DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " - "curlen=%d\n", curlen)); -#ifdef DIAGNOSTIC - if (curlen == 0) - panic("ehci_alloc_sqtd_chain: curlen == 0"); -#endif - } - DPRINTFN(4,("ehci_alloc_sqtd_chain: len=%d curlen=%d " - "curoffs=%zu\n", len, curlen, (size_t)curoffs)); - - /* - * Allocate another transfer if there's more data left, - * or if force last short transfer flag is set and we're - * allocating a multiple of the max packet size. - */ - - if (curoffs + curlen != len || - ((curlen % mps) == 0 && !rd && curlen != 0 && - (flags & USBD_FORCE_SHORT_XFER))) { - next = ehci_alloc_sqtd(sc); - if (next == NULL) - goto nomem; - nextphys = htole32(next->physaddr); - } else { - next = NULL; - nextphys = EHCI_NULL; - } +Static int +ehci_alloc_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, + int alen, int rd, ehci_soft_qtd_t **sp) +{ + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + uint16_t flags = xfer->ux_flags; - /* Find number of pages we'll be using, insert dma addresses */ - pages = EHCI_PAGE(curlen + EHCI_PAGE_SIZE -1) >> 12; - KASSERT(pages <= EHCI_QTD_NBUFFERS); - pageoffs = EHCI_PAGE(curoffs); - for (i = 0; i < pages; i++) { - a = DMAADDR(dma, pageoffs + i * EHCI_PAGE_SIZE); - cur->qtd.qtd_buffer[i] = htole32(a & 0xFFFFF000); - /* Cast up to avoid compiler warnings */ - cur->qtd.qtd_buffer_hi[i] = htole32((uint64_t)a >> 32); - } + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - /* First buffer pointer requires a page offset to start at */ - va = (vaddr_t)KERNADDR(dma, curoffs); - cur->qtd.qtd_buffer[0] |= htole32(EHCI_PAGE_OFFSET(va)); + ASSERT_SLEEPABLE(); + KASSERT(sp); + KASSERT(alen != 0 || (!rd && (flags & USBD_FORCE_SHORT_XFER))); + + size_t nsqtd = (!rd && (flags & USBD_FORCE_SHORT_XFER)) ? 1 : 0; + nsqtd += ((alen + EHCI_PAGE_SIZE - 1) / EHCI_PAGE_SIZE); + exfer->ex_sqtds = kmem_zalloc(sizeof(ehci_soft_qtd_t *) * nsqtd, + KM_SLEEP); + exfer->ex_nsqtd = nsqtd; + + DPRINTF("xfer %p len %d nsqtd %d flags %x", xfer, alen, nsqtd, flags); + + for (size_t j = 0; j < exfer->ex_nsqtd;) { + ehci_soft_qtd_t *cur = ehci_alloc_sqtd(sc); + if (cur == NULL) + goto nomem; + exfer->ex_sqtds[j++] = cur; - cur->nextqtd = next; - cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys; - cur->qtd.qtd_status = - htole32(qtdstatus | EHCI_QTD_SET_BYTES(curlen)); cur->xfer = xfer; - cur->len = curlen; - - DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08zx end=0x%08zx\n", - (size_t)curoffs, (size_t)(curoffs + curlen))); + cur->len = 0; - /* adjust the toggle based on the number of packets in this - qtd */ - if (((curlen + mps - 1) / mps) & 1) { - tog ^= 1; - qtdstatus ^= EHCI_QTD_TOGGLE_MASK; - } - if (next == NULL) - break; - usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); - if (len) - curoffs += curlen; - cur = next; } - cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); - usb_syncmem(&cur->dma, cur->offs, sizeof(cur->qtd), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - *ep = cur; - epipe->nexttoggle = tog; - DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n", - *sp, *ep)); + *sp = exfer->ex_sqtds[0]; + DPRINTF("return sqtd=%p", *sp, 0, 0, 0); - return (USBD_NORMAL_COMPLETION); + return 0; nomem: - /* XXX free chain */ - DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n")); - return (USBD_NOMEM); + ehci_free_sqtds(sc, exfer); + kmem_free(exfer->ex_sqtds, sizeof(ehci_soft_qtd_t *) * nsqtd); + DPRINTF("no memory", 0, 0, 0, 0); + return ENOMEM; } Static void -ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd, - ehci_soft_qtd_t *sqtdend) +ehci_free_sqtds(ehci_softc_t *sc, struct ehci_xfer *exfer) { - ehci_soft_qtd_t *p; - int i; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + DPRINTF("exfer=%p", exfer, 0, 0, 0); + + mutex_enter(&sc->sc_lock); + for (size_t i = 0; i < exfer->ex_nsqtd; i++) { + ehci_soft_qtd_t *sqtd = exfer->ex_sqtds[i]; - DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n", - sqtd, sqtdend)); + if (sqtd == NULL) + break; - for (i = 0; sqtd != sqtdend; sqtd = p, i++) { - p = sqtd->nextqtd; - ehci_free_sqtd(sc, sqtd); + sqtd->nextqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd; } + mutex_exit(&sc->sc_lock); } -Static ehci_soft_itd_t * -ehci_alloc_itd(ehci_softc_t *sc) +Static void +ehci_append_sqtd(ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *prev) { - struct ehci_soft_itd *itd, *freeitd; - usbd_status err; - int i, offs, frindex, previndex; - usb_dma_t dma; + if (prev) { + prev->nextqtd = sqtd; + prev->qtd.qtd_next = htole32(sqtd->physaddr); + prev->qtd.qtd_altnext = prev->qtd.qtd_next; + usb_syncmem(&prev->dma, prev->offs, sizeof(prev->qtd), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + } +} - mutex_enter(&sc->sc_lock); +Static void +ehci_reset_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, + int length, int isread, int *toggle, ehci_soft_qtd_t **lsqtd) +{ + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + usb_dma_t *dma = &xfer->ux_dmabuf; + uint16_t flags = xfer->ux_flags; + ehci_soft_qtd_t *sqtd, *prev; + int tog = *toggle; + int mps = UGETW(xfer->ux_pipe->up_endpoint->ue_edesc->wMaxPacketSize); + int len = length; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + DPRINTF("xfer=%p len %d isread %d toggle %d", xfer, len, isread, tog); + DPRINTF(" VA %p", KERNADDR(&xfer->ux_dmabuf, 0), 0, 0, 0); - /* Find an itd that wasn't freed this frame or last frame. This can - * discard itds that were freed before frindex wrapped around - * XXX - can this lead to thrashing? Could fix by enabling wrap-around - * interrupt and fiddling with list when that happens */ - frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3; - previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize; - - freeitd = NULL; - LIST_FOREACH(itd, &sc->sc_freeitds, u.free_list) { - if (itd == NULL) - break; - if (itd->slot != frindex && itd->slot != previndex) { - freeitd = itd; - break; - } - } + KASSERT(length != 0 || (!isread && (flags & USBD_FORCE_SHORT_XFER))); - if (freeitd == NULL) { - DPRINTFN(2, ("ehci_alloc_itd allocating chunk\n")); - err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK, - EHCI_PAGE_SIZE, &dma); + const uint32_t qtdstatus = EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | + EHCI_QTD_SET_CERR(3) + ; - if (err) { - DPRINTF(("ehci_alloc_itd, alloc returned %d\n", err)); - mutex_exit(&sc->sc_lock); - return NULL; - } + sqtd = prev = NULL; + size_t curoffs = 0; + size_t j = 0; + for (; len != 0 && j < exfer->ex_nsqtd; prev = sqtd) { + sqtd = exfer->ex_sqtds[j++]; + DPRINTF("sqtd[%d]=%p prev %p", j, sqtd, prev, 0); - for (i = 0; i < EHCI_ITD_CHUNK; i++) { - offs = i * EHCI_ITD_SIZE; - itd = KERNADDR(&dma, offs); + /* + * The EHCI hardware can handle at most 5 pages and they do + * not have to be contiguous + */ + vaddr_t va = (vaddr_t)KERNADDR(dma, curoffs); + vaddr_t va_offs = EHCI_PAGE_OFFSET(va); + size_t curlen = len; + if (curlen >= EHCI_QTD_MAXTRANSFER - va_offs) { + /* must use multiple TDs, fill as much as possible. */ + curlen = EHCI_QTD_MAXTRANSFER - va_offs; + + /* the length must be a multiple of the max size */ + curlen -= curlen % mps; + } + KASSERT(curlen != 0); + DPRINTF(" len=%d curlen=%d curoffs=%zu", len, curlen, + curoffs, 0); + + /* Fill the qTD */ + sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL; + sqtd->qtd.qtd_status = htole32( + qtdstatus | + EHCI_QTD_SET_BYTES(curlen) | + EHCI_QTD_SET_TOGGLE(tog)); + + /* Find number of pages we'll be using, insert dma addresses */ + size_t pages = EHCI_NPAGES(curlen); + KASSERT(pages <= EHCI_QTD_NBUFFERS); + size_t pageoffs = EHCI_PAGE(curoffs); + for (size_t i = 0; i < pages; i++) { + paddr_t a = DMAADDR(dma, + pageoffs + i * EHCI_PAGE_SIZE); + sqtd->qtd.qtd_buffer[i] = htole32(EHCI_PAGE(a)); + /* Cast up to avoid compiler warnings */ + sqtd->qtd.qtd_buffer_hi[i] = htole32((uint64_t)a >> 32); + DPRINTF(" buffer[%d/%d] 0x%08x 0x%08x", i, pages, + le32toh(sqtd->qtd.qtd_buffer_hi[i]), + le32toh(sqtd->qtd.qtd_buffer[i])); + } + /* First buffer pointer requires a page offset to start at */ + sqtd->qtd.qtd_buffer[0] |= htole32(va_offs); + + usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + sqtd->len = curlen; + + DPRINTF(" va %p pa %p len %d", va, + DMAADDR(&xfer->ux_dmabuf, curoffs), curlen, 0); + + ehci_append_sqtd(sqtd, prev); + + if (((curlen + mps - 1) / mps) & 1) { + tog ^= 1; + } + + curoffs += curlen; + len -= curlen; + } + KASSERTMSG(len == 0, "xfer %p olen %d len %d mps %d ex_nsqtd %zu j %zu", + xfer, length, len, mps, exfer->ex_nsqtd, j); + + if (!isread && + (flags & USBD_FORCE_SHORT_XFER) && + length % mps == 0) { + /* Force a 0 length transfer at the end. */ + + KASSERTMSG(j < exfer->ex_nsqtd, "j=%zu nsqtd=%zu", j, + exfer->ex_nsqtd); + prev = sqtd; + sqtd = exfer->ex_sqtds[j++]; + memset(&sqtd->qtd, 0, sizeof(sqtd->qtd)); + sqtd->qtd.qtd_next = sqtd->qtd.qtd_altnext = EHCI_NULL; + sqtd->qtd.qtd_status = htole32( + qtdstatus | + EHCI_QTD_SET_BYTES(0) | + EHCI_QTD_SET_TOGGLE(tog)); + + usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + ehci_append_sqtd(sqtd, prev); + tog ^= 1; + } + + *lsqtd = sqtd; + *toggle = tog; +} + +Static ehci_soft_itd_t * +ehci_alloc_itd(ehci_softc_t *sc) +{ + struct ehci_soft_itd *itd, *freeitd; + usbd_status err; + usb_dma_t dma; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + mutex_enter(&sc->sc_lock); + + freeitd = LIST_FIRST(&sc->sc_freeitds); + if (freeitd == NULL) { + DPRINTF("allocating chunk", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); + err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK, + EHCI_PAGE_SIZE, &dma); + + if (err) { + DPRINTF("alloc returned %d", err, 0, 0, 0); + return NULL; + } + mutex_enter(&sc->sc_lock); + + for (int i = 0; i < EHCI_ITD_CHUNK; i++) { + int offs = i * EHCI_ITD_SIZE; + itd = KERNADDR(&dma, offs); itd->physaddr = DMAADDR(&dma, offs); itd->dma = dma; itd->offs = offs; - LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list); + LIST_INSERT_HEAD(&sc->sc_freeitds, itd, free_list); } freeitd = LIST_FIRST(&sc->sc_freeitds); } itd = freeitd; - LIST_REMOVE(itd, u.free_list); + LIST_REMOVE(itd, free_list); + mutex_exit(&sc->sc_lock); memset(&itd->itd, 0, sizeof(ehci_itd_t)); - usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_next), - sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE | - BUS_DMASYNC_PREREAD); - itd->u.frame_list.next = NULL; - itd->u.frame_list.prev = NULL; + itd->frame_list.next = NULL; + itd->frame_list.prev = NULL; itd->xfer_next = NULL; itd->slot = 0; - mutex_exit(&sc->sc_lock); - return itd; } -Static void -ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd) +Static ehci_soft_sitd_t * +ehci_alloc_sitd(ehci_softc_t *sc) { + struct ehci_soft_sitd *sitd, *freesitd; + usbd_status err; + int i, offs; + usb_dma_t dma; - KASSERT(mutex_owned(&sc->sc_lock)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + mutex_enter(&sc->sc_lock); + freesitd = LIST_FIRST(&sc->sc_freesitds); + if (freesitd == NULL) { + DPRINTF("allocating chunk", 0, 0, 0, 0); + mutex_exit(&sc->sc_lock); + err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * EHCI_SITD_CHUNK, + EHCI_PAGE_SIZE, &dma); + + if (err) { + DPRINTF("alloc returned %d", err, 0, 0, + 0); + return NULL; + } + + mutex_enter(&sc->sc_lock); + for (i = 0; i < EHCI_SITD_CHUNK; i++) { + offs = i * EHCI_SITD_SIZE; + sitd = KERNADDR(&dma, offs); + sitd->physaddr = DMAADDR(&dma, offs); + sitd->dma = dma; + sitd->offs = offs; + LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, free_list); + } + freesitd = LIST_FIRST(&sc->sc_freesitds); + } + + sitd = freesitd; + LIST_REMOVE(sitd, free_list); + mutex_exit(&sc->sc_lock); + + memset(&sitd->sitd, 0, sizeof(ehci_sitd_t)); - LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list); + sitd->frame_list.next = NULL; + sitd->frame_list.prev = NULL; + sitd->xfer_next = NULL; + sitd->slot = 0; + + return sitd; } /****************/ @@ -2964,10 +3082,10 @@ ehci_free_itd(ehci_softc_t *sc, ehci_sof * Assumes that there are no pending transactions. */ Static void -ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head) +ehci_close_pipe(struct usbd_pipe *pipe, ehci_soft_qh_t *head) { - struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; - ehci_softc_t *sc = pipe->device->bus->hci_private; + struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); + ehci_softc_t *sc = EHCI_PIPE2SC(pipe); ehci_soft_qh_t *sqh = epipe->sqh; KASSERT(mutex_owned(&sc->sc_lock)); @@ -2989,58 +3107,59 @@ ehci_close_pipe(usbd_pipe_handle pipe, e * XXXMRG this doesn't make sense anymore. */ Static void -ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) +ehci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) { -#define exfer EXFER(xfer) - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - ehci_softc_t *sc = epipe->pipe.device->bus->hci_private; + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); ehci_soft_qh_t *sqh = epipe->sqh; - ehci_soft_qtd_t *sqtd; + ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; ehci_physaddr_t cur; - u_int32_t qhstatus; + uint32_t qhstatus; int hit; int wake; - DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer=%p pipe=%p", xfer, epipe, 0, 0); KASSERT(mutex_owned(&sc->sc_lock)); + ASSERT_SLEEPABLE(); if (sc->sc_dying) { /* If we're dying, just do the software part. */ - xfer->status = status; /* make software ignore it */ - callout_stop(&xfer->timeout_handle); + xfer->ux_status = status; /* make software ignore it */ + callout_stop(&xfer->ux_callout); usb_transfer_complete(xfer); return; } - if (cpu_intr_p() || cpu_softintr_p()) - panic("ehci_abort_xfer: not in process context"); - /* * If an abort is already in progress then just wait for it to * complete and return. */ - if (xfer->hcflags & UXFER_ABORTING) { - DPRINTFN(2, ("ehci_abort_xfer: already aborting\n")); + if (xfer->ux_hcflags & UXFER_ABORTING) { + DPRINTF("already aborting", 0, 0, 0, 0); #ifdef DIAGNOSTIC if (status == USBD_TIMEOUT) printf("ehci_abort_xfer: TIMEOUT while aborting\n"); #endif /* Override the status which might be USBD_TIMEOUT. */ - xfer->status = status; - DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n")); - xfer->hcflags |= UXFER_ABORTWAIT; - while (xfer->hcflags & UXFER_ABORTING) - cv_wait(&xfer->hccv, &sc->sc_lock); + xfer->ux_status = status; + DPRINTF("waiting for abort to finish", 0, 0, 0, 0); + xfer->ux_hcflags |= UXFER_ABORTWAIT; + while (xfer->ux_hcflags & UXFER_ABORTING) + cv_wait(&xfer->ux_hccv, &sc->sc_lock); return; } - xfer->hcflags |= UXFER_ABORTING; + xfer->ux_hcflags |= UXFER_ABORTING; /* * Step 1: Make interrupt routine and hardware ignore xfer. */ - xfer->status = status; /* make software ignore it */ - callout_stop(&xfer->timeout_handle); + xfer->ux_status = status; /* make software ignore it */ + callout_stop(&xfer->ux_callout); + ehci_del_intr_list(sc, exfer); usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), @@ -3052,7 +3171,15 @@ ehci_abort_xfer(usbd_xfer_handle xfer, u sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), sizeof(sqh->qh.qh_qtd.qtd_status), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { + + if (exfer->ex_type == EX_CTRL) { + fsqtd = exfer->ex_setup; + lsqtd = exfer->ex_status; + } else { + fsqtd = exfer->ex_sqtdstart; + lsqtd = exfer->ex_sqtdend; + } + for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { usb_syncmem(&sqtd->dma, sqtd->offs + offsetof(ehci_qtd_t, qtd_status), sizeof(sqtd->qtd.qtd_status), @@ -3062,7 +3189,7 @@ ehci_abort_xfer(usbd_xfer_handle xfer, u sqtd->offs + offsetof(ehci_qtd_t, qtd_status), sizeof(sqtd->qtd.qtd_status), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - if (sqtd == exfer->sqtdend) + if (sqtd == lsqtd) break; } @@ -3090,15 +3217,15 @@ ehci_abort_xfer(usbd_xfer_handle xfer, u BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); hit = 0; - for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { + for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { hit |= cur == sqtd->physaddr; - if (sqtd == exfer->sqtdend) + if (sqtd == lsqtd) break; } sqtd = sqtd->nextqtd; /* Zap curqtd register if hardware pointed inside the xfer. */ if (hit && sqtd != NULL) { - DPRINTFN(1,("ehci_abort_xfer: cur=0x%08x\n", sqtd->physaddr)); + DPRINTF("cur=0x%08x", sqtd->physaddr, 0, 0, 0); sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */ usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_curqtd), @@ -3110,101 +3237,126 @@ ehci_abort_xfer(usbd_xfer_handle xfer, u sizeof(sqh->qh.qh_qtd.qtd_status), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } else { - DPRINTFN(1,("ehci_abort_xfer: no hit\n")); + DPRINTF("no hit", 0, 0, 0, 0); + usb_syncmem(&sqh->dma, + sqh->offs + offsetof(ehci_qh_t, qh_curqtd), + sizeof(sqh->qh.qh_curqtd), + BUS_DMASYNC_PREREAD); } /* * Step 4: Execute callback. */ #ifdef DIAGNOSTIC - exfer->isdone = 1; + exfer->ex_isdone = true; #endif - wake = xfer->hcflags & UXFER_ABORTWAIT; - xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); + wake = xfer->ux_hcflags & UXFER_ABORTWAIT; + xfer->ux_hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); usb_transfer_complete(xfer); if (wake) { - cv_broadcast(&xfer->hccv); + cv_broadcast(&xfer->ux_hccv); } KASSERT(mutex_owned(&sc->sc_lock)); -#undef exfer } Static void -ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status) +ehci_abort_isoc_xfer(struct usbd_xfer *xfer, usbd_status status) { ehci_isoc_trans_t trans_status; - struct ehci_pipe *epipe; struct ehci_xfer *exfer; ehci_softc_t *sc; struct ehci_soft_itd *itd; + struct ehci_soft_sitd *sitd; int i, wake; - epipe = (struct ehci_pipe *) xfer->pipe; - exfer = EXFER(xfer); - sc = epipe->pipe.device->bus->hci_private; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + exfer = EHCI_XFER2EXFER(xfer); + sc = EHCI_XFER2SC(xfer); - DPRINTF(("ehci_abort_isoc_xfer: xfer %p pipe %p\n", xfer, epipe)); + DPRINTF("xfer %p pipe %p", xfer, xfer->ux_pipe, 0, 0); KASSERT(mutex_owned(&sc->sc_lock)); if (sc->sc_dying) { - xfer->status = status; - callout_stop(&xfer->timeout_handle); + xfer->ux_status = status; + callout_stop(&xfer->ux_callout); usb_transfer_complete(xfer); return; } - if (xfer->hcflags & UXFER_ABORTING) { - DPRINTFN(2, ("ehci_abort_isoc_xfer: already aborting\n")); + if (xfer->ux_hcflags & UXFER_ABORTING) { + DPRINTF("already aborting", 0, 0, 0, 0); #ifdef DIAGNOSTIC if (status == USBD_TIMEOUT) printf("ehci_abort_isoc_xfer: TIMEOUT while aborting\n"); #endif - xfer->status = status; - DPRINTFN(2, ("ehci_abort_isoc_xfer: waiting for abort to finish\n")); - xfer->hcflags |= UXFER_ABORTWAIT; - while (xfer->hcflags & UXFER_ABORTING) - cv_wait(&xfer->hccv, &sc->sc_lock); + xfer->ux_status = status; + DPRINTF("waiting for abort to finish", 0, 0, 0, 0); + xfer->ux_hcflags |= UXFER_ABORTWAIT; + while (xfer->ux_hcflags & UXFER_ABORTING) + cv_wait(&xfer->ux_hccv, &sc->sc_lock); goto done; } - xfer->hcflags |= UXFER_ABORTING; + xfer->ux_hcflags |= UXFER_ABORTING; - xfer->status = status; - callout_stop(&xfer->timeout_handle); + xfer->ux_status = status; + callout_stop(&xfer->ux_callout); + ehci_del_intr_list(sc, exfer); + + if (xfer->ux_pipe->up_dev->ud_speed == USB_SPEED_HIGH) { + for (itd = exfer->ex_itdstart; itd != NULL; + itd = itd->xfer_next) { + usb_syncmem(&itd->dma, + itd->offs + offsetof(ehci_itd_t, itd_ctl), + sizeof(itd->itd.itd_ctl), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); - for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) { - usb_syncmem(&itd->dma, - itd->offs + offsetof(ehci_itd_t, itd_ctl), - sizeof(itd->itd.itd_ctl), - BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + for (i = 0; i < 8; i++) { + trans_status = le32toh(itd->itd.itd_ctl[i]); + trans_status &= ~EHCI_ITD_ACTIVE; + itd->itd.itd_ctl[i] = htole32(trans_status); + } - for (i = 0; i < 8; i++) { - trans_status = le32toh(itd->itd.itd_ctl[i]); - trans_status &= ~EHCI_ITD_ACTIVE; - itd->itd.itd_ctl[i] = htole32(trans_status); + usb_syncmem(&itd->dma, + itd->offs + offsetof(ehci_itd_t, itd_ctl), + sizeof(itd->itd.itd_ctl), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } + } else { + for (sitd = exfer->ex_sitdstart; sitd != NULL; + sitd = sitd->xfer_next) { + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), + sizeof(sitd->sitd.sitd_buffer), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); - usb_syncmem(&itd->dma, - itd->offs + offsetof(ehci_itd_t, itd_ctl), - sizeof(itd->itd.itd_ctl), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + trans_status = le32toh(sitd->sitd.sitd_trans); + trans_status &= ~EHCI_SITD_ACTIVE; + sitd->sitd.sitd_trans = htole32(trans_status); + + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), + sizeof(sitd->sitd.sitd_buffer), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + } } - sc->sc_softwake = 1; - usb_schedsoftintr(&sc->sc_bus); + sc->sc_softwake = 1; + usb_schedsoftintr(&sc->sc_bus); cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); #ifdef DIAGNOSTIC - exfer->isdone = 1; + exfer->ex_isdone = true; #endif - wake = xfer->hcflags & UXFER_ABORTWAIT; - xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); + wake = xfer->ux_hcflags & UXFER_ABORTWAIT; + xfer->ux_hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); usb_transfer_complete(xfer); if (wake) { - cv_broadcast(&xfer->hccv); + cv_broadcast(&xfer->ux_hccv); } done: @@ -3215,37 +3367,42 @@ done: Static void ehci_timeout(void *addr) { - struct ehci_xfer *exfer = addr; - struct ehci_pipe *epipe = (struct ehci_pipe *)exfer->xfer.pipe; - ehci_softc_t *sc = epipe->pipe.device->bus->hci_private; + struct usbd_xfer *xfer = addr; + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + struct usbd_pipe *pipe = xfer->ux_pipe; + struct usbd_device *dev = pipe->up_dev; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTF(("ehci_timeout: exfer=%p\n", exfer)); + DPRINTF("exfer %p", exfer, 0, 0, 0); #ifdef EHCI_DEBUG - if (ehcidebug > 1) - usbd_dump_pipe(exfer->xfer.pipe); + if (ehcidebug >= 2) + usbd_dump_pipe(pipe); #endif if (sc->sc_dying) { mutex_enter(&sc->sc_lock); - ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT); + ehci_abort_xfer(xfer, USBD_TIMEOUT); mutex_exit(&sc->sc_lock); return; } /* Execute the abort in a process context. */ - usb_init_task(&exfer->abort_task, ehci_timeout_task, addr, + usb_init_task(&exfer->ex_aborttask, ehci_timeout_task, xfer, USB_TASKQ_MPSAFE); - usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task, - USB_TASKQ_HC); + usb_add_task(dev, &exfer->ex_aborttask, USB_TASKQ_HC); } Static void ehci_timeout_task(void *addr) { - usbd_xfer_handle xfer = addr; - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; + struct usbd_xfer *xfer = addr; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTF(("ehci_timeout_task: xfer=%p\n", xfer)); + DPRINTF("xfer=%p", xfer, 0, 0, 0); mutex_enter(&sc->sc_lock); ehci_abort_xfer(xfer, USBD_TIMEOUT); @@ -3254,10 +3411,98 @@ ehci_timeout_task(void *addr) /************************/ +Static int +ehci_device_ctrl_init(struct usbd_xfer *xfer) +{ + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + usb_device_request_t *req = &xfer->ux_request; + ehci_soft_qtd_t *setup, *status, *next; + int isread = req->bmRequestType & UT_READ; + int len = xfer->ux_bufsize; + int err; + + exfer->ex_type = EX_CTRL; + exfer->ex_status = NULL; + exfer->ex_data = NULL; + exfer->ex_setup = ehci_alloc_sqtd(sc); + if (exfer->ex_setup == NULL) { + err = ENOMEM; + goto bad1; + } + exfer->ex_status = ehci_alloc_sqtd(sc); + if (exfer->ex_status == NULL) { + err = ENOMEM; + goto bad2; + } + setup = exfer->ex_setup; + status = exfer->ex_status; + exfer->ex_nsqtd = 0; + next = status; + /* Set up data transaction */ + if (len != 0) { + err = ehci_alloc_sqtd_chain(sc, xfer, len, isread, + &exfer->ex_data); + if (err) + goto bad3; + next = exfer->ex_data; + } + + /* Clear toggle */ + setup->qtd.qtd_status = htole32( + EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | + EHCI_QTD_SET_TOGGLE(0) | + EHCI_QTD_SET_BYTES(sizeof(*req)) + ); + setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->ctrl.reqdma, 0)); + setup->qtd.qtd_buffer_hi[0] = 0; + setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); + setup->nextqtd = next; + setup->xfer = xfer; + setup->len = sizeof(*req); + + status->qtd.qtd_status = htole32( + EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | + EHCI_QTD_SET_TOGGLE(1) | + EHCI_QTD_IOC + ); + status->qtd.qtd_buffer[0] = 0; + status->qtd.qtd_buffer_hi[0] = 0; + status->qtd.qtd_next = status->qtd.qtd_altnext = EHCI_NULL; + status->nextqtd = NULL; + status->xfer = xfer; + status->len = 0; + + return 0; +bad3: + ehci_free_sqtd(sc, exfer->ex_status); +bad2: + ehci_free_sqtd(sc, exfer->ex_setup); +bad1: + return err; +} + +Static void +ehci_device_ctrl_fini(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); + + KASSERT(ex->ex_type == EX_CTRL); + + ehci_free_sqtd(sc, ex->ex_setup); + ehci_free_sqtd(sc, ex->ex_status); + ehci_free_sqtds(sc, ex); + if (ex->ex_nsqtd) + kmem_free(ex->ex_sqtds, + sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd); +} + Static usbd_status -ehci_device_ctrl_transfer(usbd_xfer_handle xfer) +ehci_device_ctrl_transfer(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); usbd_status err; /* Insert last in queue. */ @@ -3265,280 +3510,273 @@ ehci_device_ctrl_transfer(usbd_xfer_hand err = usb_insert_transfer(xfer); mutex_exit(&sc->sc_lock); if (err) - return (err); + return err; /* Pipe isn't running, start first */ - return (ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); + return ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); } Static usbd_status -ehci_device_ctrl_start(usbd_xfer_handle xfer) +ehci_device_ctrl_start(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - usbd_status err; + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + usb_device_request_t *req = &xfer->ux_request; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + ehci_soft_qtd_t *setup, *status, *next; + ehci_soft_qh_t *sqh; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + KASSERT(xfer->ux_rqflags & URQ_REQUEST); if (sc->sc_dying) - return (USBD_IOERROR); + return USBD_IOERROR; + + const int isread = req->bmRequestType & UT_READ; + const int len = UGETW(req->wLength); + + DPRINTF("type=0x%02x, request=0x%02x, wValue=0x%04x, wIndex=0x%04x", + req->bmRequestType, req->bRequest, UGETW(req->wValue), + UGETW(req->wIndex)); + DPRINTF("len=%d, addr=%d, endpt=%d", len, epipe->pipe.up_dev->ud_addr, + epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress, 0); + + sqh = epipe->sqh; + + KASSERTMSG(EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)) == epipe->pipe.up_dev->ud_addr, + "address QH %" __PRIuBIT " pipe %d\n", + EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)), + epipe->pipe.up_dev->ud_addr); + KASSERTMSG(EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)) == + UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize), + "MPS QH %" __PRIuBIT " pipe %d\n", + EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)), + UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize)); + + setup = exfer->ex_setup; + status = exfer->ex_status; + + DPRINTF("setup %p status %p data %p", setup, status, exfer->ex_data, 0); + KASSERTMSG(setup != NULL && status != NULL, + "Failed memory allocation, setup %p status %p", + setup, status); + + memcpy(KERNADDR(&epipe->ctrl.reqdma, 0), req, sizeof(*req)); + usb_syncmem(&epipe->ctrl.reqdma, 0, sizeof(*req), BUS_DMASYNC_PREWRITE); + + /* Clear toggle */ + setup->qtd.qtd_status &= ~htole32( + EHCI_QTD_STATUS_MASK | + EHCI_QTD_BYTES_MASK | + EHCI_QTD_TOGGLE_MASK | + EHCI_QTD_CERR_MASK + ); + setup->qtd.qtd_status |= htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(0) | + EHCI_QTD_SET_BYTES(sizeof(*req)) + ); + setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->ctrl.reqdma, 0)); + setup->qtd.qtd_buffer_hi[0] = 0; + + next = status; + status->qtd.qtd_status &= ~htole32( + EHCI_QTD_STATUS_MASK | + EHCI_QTD_PID_MASK | + EHCI_QTD_BYTES_MASK | + EHCI_QTD_TOGGLE_MASK | + EHCI_QTD_CERR_MASK + ); + status->qtd.qtd_status |= htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(1) | + EHCI_QTD_SET_BYTES(0) | + EHCI_QTD_IOC + ); + KASSERT(status->qtd.qtd_status & htole32(EHCI_QTD_TOGGLE_MASK)); + KASSERT(exfer->ex_isdone); #ifdef DIAGNOSTIC - if (!(xfer->rqflags & URQ_REQUEST)) { - /* XXX panic */ - printf("ehci_device_ctrl_transfer: not a request\n"); - return (USBD_INVAL); + exfer->ex_isdone = false; +#endif + + /* Set up data transaction */ + if (len != 0) { + ehci_soft_qtd_t *end; + + /* Start toggle at 1. */ + int toggle = 1; + next = exfer->ex_data; + KASSERTMSG(next != NULL, "Failed memory allocation"); + ehci_reset_sqtd_chain(sc, xfer, len, isread, &toggle, &end); + end->nextqtd = status; + end->qtd.qtd_next = end->qtd.qtd_altnext = + htole32(status->physaddr); + + usb_syncmem(&end->dma, end->offs, sizeof(end->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + usb_syncmem(&xfer->ux_dmabuf, 0, len, + isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); } + + setup->nextqtd = next; + setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); + + usb_syncmem(&setup->dma, setup->offs, sizeof(setup->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + usb_syncmem(&status->dma, status->offs, sizeof(status->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + KASSERT(status->qtd.qtd_status & htole32(EHCI_QTD_TOGGLE_MASK)); + +#ifdef EHCI_DEBUG + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(setup); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); #endif - err = ehci_device_request(xfer); - if (err) { - return (err); + mutex_enter(&sc->sc_lock); + + /* Insert qTD in QH list - also does usb_syncmem(sqh) */ + ehci_set_qh_qtd(sqh, setup); + if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), + ehci_timeout, xfer); } + ehci_add_intr_list(sc, exfer); + xfer->ux_status = USBD_IN_PROGRESS; + mutex_exit(&sc->sc_lock); - if (sc->sc_bus.use_polling) - ehci_waitintr(sc, xfer); +#if 0 +#ifdef EHCI_DEBUG + DPRINTFN(10, "status=%x, dump:", EOREAD4(sc, EHCI_USBSTS), 0, 0, 0); +// delay(10000); + ehci_dump_regs(sc); + ehci_dump_sqh(sc->sc_async_head); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(setup); +#endif +#endif - return (USBD_IN_PROGRESS); + return USBD_IN_PROGRESS; } Static void -ehci_device_ctrl_done(usbd_xfer_handle xfer) +ehci_device_ctrl_done(struct usbd_xfer *xfer) { - struct ehci_xfer *ex = EXFER(xfer); - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - usb_device_request_t *req = &xfer->request; + ehci_softc_t *sc __diagused = EHCI_XFER2SC(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + usb_device_request_t *req = &xfer->ux_request; int len = UGETW(req->wLength); int rd = req->bmRequestType & UT_READ; - DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + DPRINTF("xfer=%p", xfer, 0, 0, 0); - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); - -#ifdef DIAGNOSTIC - if (!(xfer->rqflags & URQ_REQUEST)) { - panic("ehci_ctrl_done: not a request"); - } -#endif + KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); + KASSERT(xfer->ux_rqflags & URQ_REQUEST); - if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { - ehci_del_intr_list(sc, ex); /* remove from active list */ - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); - usb_syncmem(&epipe->u.ctl.reqdma, 0, sizeof *req, - BUS_DMASYNC_POSTWRITE); - if (len) - usb_syncmem(&xfer->dmabuf, 0, len, - rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); - } + usb_syncmem(&epipe->ctrl.reqdma, 0, sizeof(*req), + BUS_DMASYNC_POSTWRITE); + if (len) + usb_syncmem(&xfer->ux_dmabuf, 0, len, + rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); - DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen)); + DPRINTF("length=%d", xfer->ux_actlen, 0, 0, 0); } /* Abort a device control request. */ Static void -ehci_device_ctrl_abort(usbd_xfer_handle xfer) +ehci_device_ctrl_abort(struct usbd_xfer *xfer) { - DPRINTF(("ehci_device_ctrl_abort: xfer=%p\n", xfer)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer=%p", xfer, 0, 0, 0); ehci_abort_xfer(xfer, USBD_CANCELLED); } /* Close a device control pipe. */ Static void -ehci_device_ctrl_close(usbd_pipe_handle pipe) +ehci_device_ctrl_close(struct usbd_pipe *pipe) { - ehci_softc_t *sc = pipe->device->bus->hci_private; - /*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/ + ehci_softc_t *sc = EHCI_PIPE2SC(pipe); + /*struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe);*/ + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); KASSERT(mutex_owned(&sc->sc_lock)); - DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe)); + DPRINTF("pipe=%p", pipe, 0, 0, 0); ehci_close_pipe(pipe, sc->sc_async_head); } -Static usbd_status -ehci_device_request(usbd_xfer_handle xfer) +/* + * Some EHCI chips from VIA seem to trigger interrupts before writing back the + * qTD status, or miss signalling occasionally under heavy load. If the host + * machine is too fast, we we can miss transaction completion - when we scan + * the active list the transaction still seems to be active. This generally + * exhibits itself as a umass stall that never recovers. + * + * We work around this behaviour by setting up this callback after any softintr + * that completes with transactions still pending, giving us another chance to + * check for completion after the writeback has taken place. + */ +Static void +ehci_intrlist_timeout(void *arg) { -#define exfer EXFER(xfer) - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - usb_device_request_t *req = &xfer->request; - usbd_device_handle dev = epipe->pipe.device; - ehci_softc_t *sc = dev->bus->hci_private; - ehci_soft_qtd_t *setup, *stat, *next; - ehci_soft_qh_t *sqh; - int isread; - int len; - usbd_status err; + ehci_softc_t *sc = arg; - isread = req->bmRequestType & UT_READ; - len = UGETW(req->wLength); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - DPRINTFN(3,("ehci_device_request: type=0x%02x, request=0x%02x, " - "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", - req->bmRequestType, req->bRequest, UGETW(req->wValue), - UGETW(req->wIndex), len, dev->address, - epipe->pipe.endpoint->edesc->bEndpointAddress)); - - setup = ehci_alloc_sqtd(sc); - if (setup == NULL) { - err = USBD_NOMEM; - goto bad1; - } - stat = ehci_alloc_sqtd(sc); - if (stat == NULL) { - err = USBD_NOMEM; - goto bad2; - } + usb_schedsoftintr(&sc->sc_bus); +} - mutex_enter(&sc->sc_lock); +/************************/ - sqh = epipe->sqh; +Static int +ehci_device_bulk_init(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc; + int endpt = ed->bEndpointAddress; + int isread = UE_GET_DIR(endpt) == UE_DIR_IN; + int len = xfer->ux_bufsize; + int err = 0; - KASSERTMSG(EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)) == dev->address, - "address QH %d pipe %d\n", - EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)), dev->address); - KASSERTMSG(EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)) == - UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize), - "MPS QH %d pipe %d\n", - EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)), - UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)); + exfer->ex_type = EX_BULK; + exfer->ex_nsqtd = 0; + err = ehci_alloc_sqtd_chain(sc, xfer, len, isread, + &exfer->ex_sqtdstart); - /* Set up data transaction */ - if (len != 0) { - ehci_soft_qtd_t *end; + return err; +} - /* Start toggle at 1. */ - epipe->nexttoggle = 1; - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, - &next, &end); - if (err) - goto bad3; - end->qtd.qtd_status &= htole32(~EHCI_QTD_IOC); - end->nextqtd = stat; - end->qtd.qtd_next = end->qtd.qtd_altnext = - htole32(stat->physaddr); - usb_syncmem(&end->dma, end->offs, sizeof(end->qtd), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - } else { - next = stat; - } +Static void +ehci_device_bulk_fini(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); - memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); - usb_syncmem(&epipe->u.ctl.reqdma, 0, sizeof *req, BUS_DMASYNC_PREWRITE); + KASSERT(ex->ex_type == EX_BULK); - /* Clear toggle */ - setup->qtd.qtd_status = htole32( - EHCI_QTD_ACTIVE | - EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | - EHCI_QTD_SET_CERR(3) | - EHCI_QTD_SET_TOGGLE(0) | - EHCI_QTD_SET_BYTES(sizeof *req) - ); - setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0)); - setup->qtd.qtd_buffer_hi[0] = 0; - setup->nextqtd = next; - setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); - setup->xfer = xfer; - setup->len = sizeof *req; - usb_syncmem(&setup->dma, setup->offs, sizeof(setup->qtd), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - - stat->qtd.qtd_status = htole32( - EHCI_QTD_ACTIVE | - EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | - EHCI_QTD_SET_CERR(3) | - EHCI_QTD_SET_TOGGLE(1) | - EHCI_QTD_IOC - ); - stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ - stat->qtd.qtd_buffer_hi[0] = 0; /* XXX not needed? */ - stat->nextqtd = NULL; - stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL; - stat->xfer = xfer; - stat->len = 0; - usb_syncmem(&stat->dma, stat->offs, sizeof(stat->qtd), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - -#ifdef EHCI_DEBUG - if (ehcidebug > 5) { - DPRINTF(("ehci_device_request:\n")); - ehci_dump_sqh(sqh); - ehci_dump_sqtds(setup); - } -#endif - - exfer->sqtdstart = setup; - exfer->sqtdend = stat; -#ifdef DIAGNOSTIC - if (!exfer->isdone) { - printf("ehci_device_request: not done, exfer=%p\n", exfer); - } - exfer->isdone = 0; -#endif - - /* Insert qTD in QH list. */ - ehci_set_qh_qtd(sqh, setup); /* also does usb_syncmem(sqh) */ - if (xfer->timeout && !sc->sc_bus.use_polling) { - callout_reset(&xfer->timeout_handle, mstohz(xfer->timeout), - ehci_timeout, xfer); - } - ehci_add_intr_list(sc, exfer); - xfer->status = USBD_IN_PROGRESS; - mutex_exit(&sc->sc_lock); - -#ifdef EHCI_DEBUG - if (ehcidebug > 10) { - DPRINTF(("ehci_device_request: status=%x\n", - EOREAD4(sc, EHCI_USBSTS))); - delay(10000); - ehci_dump_regs(sc); - ehci_dump_sqh(sc->sc_async_head); - ehci_dump_sqh(sqh); - ehci_dump_sqtds(setup); - } -#endif - - return (USBD_NORMAL_COMPLETION); - - bad3: - mutex_exit(&sc->sc_lock); - ehci_free_sqtd(sc, stat); - bad2: - ehci_free_sqtd(sc, setup); - bad1: - DPRINTFN(-1,("ehci_device_request: no memory\n")); - mutex_enter(&sc->sc_lock); - xfer->status = err; - usb_transfer_complete(xfer); - mutex_exit(&sc->sc_lock); - return (err); -#undef exfer + ehci_free_sqtds(sc, ex); + if (ex->ex_nsqtd) + kmem_free(ex->ex_sqtds, sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd); } -/* - * Some EHCI chips from VIA seem to trigger interrupts before writing back the - * qTD status, or miss signalling occasionally under heavy load. If the host - * machine is too fast, we we can miss transaction completion - when we scan - * the active list the transaction still seems to be active. This generally - * exhibits itself as a umass stall that never recovers. - * - * We work around this behaviour by setting up this callback after any softintr - * that completes with transactions still pending, giving us another chance to - * check for completion after the writeback has taken place. - */ -Static void -ehci_intrlist_timeout(void *arg) -{ - ehci_softc_t *sc = arg; - - DPRINTF(("ehci_intrlist_timeout\n")); - usb_schedsoftintr(&sc->sc_bus); -} - -/************************/ - Static usbd_status -ehci_device_bulk_transfer(usbd_xfer_handle xfer) +ehci_device_bulk_transfer(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); usbd_status err; /* Insert last in queue. */ @@ -3546,108 +3784,98 @@ ehci_device_bulk_transfer(usbd_xfer_hand err = usb_insert_transfer(xfer); mutex_exit(&sc->sc_lock); if (err) - return (err); + return err; /* Pipe isn't running, start first */ - return (ehci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); + return ehci_device_bulk_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); } Static usbd_status -ehci_device_bulk_start(usbd_xfer_handle xfer) +ehci_device_bulk_start(struct usbd_xfer *xfer) { -#define exfer EXFER(xfer) - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - usbd_device_handle dev = epipe->pipe.device; - ehci_softc_t *sc = dev->bus->hci_private; - ehci_soft_qtd_t *data, *dataend; + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); ehci_soft_qh_t *sqh; - usbd_status err; + ehci_soft_qtd_t *end; int len, isread, endpt; - DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n", - xfer, xfer->length, xfer->flags)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer=%p len=%d flags=%d", xfer, xfer->ux_length, + xfer->ux_flags, 0); if (sc->sc_dying) - return (USBD_IOERROR); + return USBD_IOERROR; + + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(xfer->ux_length <= xfer->ux_bufsize); + + len = xfer->ux_length; + endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; + isread = UE_GET_DIR(endpt) == UE_DIR_IN; + sqh = epipe->sqh; + KASSERT(exfer->ex_isdone); #ifdef DIAGNOSTIC - if (xfer->rqflags & URQ_REQUEST) - panic("ehci_device_bulk_start: a request"); + exfer->ex_isdone = false; #endif + /* Take lock here to protect nexttoggle */ mutex_enter(&sc->sc_lock); - len = xfer->length; - endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; - isread = UE_GET_DIR(endpt) == UE_DIR_IN; - sqh = epipe->sqh; - - epipe->u.bulk.length = len; + ehci_reset_sqtd_chain(sc, xfer, len, isread, &epipe->nexttoggle, &end); - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, - &dataend); - if (err) { - DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n")); - xfer->status = err; - usb_transfer_complete(xfer); - mutex_exit(&sc->sc_lock); - return (err); - } + exfer->ex_sqtdend = end; + end->qtd.qtd_status |= htole32(EHCI_QTD_IOC); + usb_syncmem(&end->dma, end->offs, sizeof(end->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); #ifdef EHCI_DEBUG - if (ehcidebug > 5) { - DPRINTF(("ehci_device_bulk_start: data(1)\n")); - ehci_dump_sqh(sqh); - ehci_dump_sqtds(data); - } + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(exfer->ex_sqtdstart); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); #endif - /* Set up interrupt info. */ - exfer->sqtdstart = data; - exfer->sqtdend = dataend; -#ifdef DIAGNOSTIC - if (!exfer->isdone) { - printf("ehci_device_bulk_start: not done, ex=%p\n", exfer); - } - exfer->isdone = 0; -#endif - - ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */ - if (xfer->timeout && !sc->sc_bus.use_polling) { - callout_reset(&xfer->timeout_handle, mstohz(xfer->timeout), + usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, + isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + /* also does usb_syncmem(sqh) */ + ehci_set_qh_qtd(sqh, exfer->ex_sqtdstart); + if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), ehci_timeout, xfer); } ehci_add_intr_list(sc, exfer); - xfer->status = USBD_IN_PROGRESS; + xfer->ux_status = USBD_IN_PROGRESS; mutex_exit(&sc->sc_lock); +#if 0 #ifdef EHCI_DEBUG - if (ehcidebug > 10) { - DPRINTF(("ehci_device_bulk_start: data(2)\n")); - delay(10000); - DPRINTF(("ehci_device_bulk_start: data(3)\n")); - ehci_dump_regs(sc); + DPRINTFN(5, "data(2)", 0, 0, 0, 0); +// delay(10000); + DPRINTFN(5, "data(3)", 0, 0, 0, 0); + ehci_dump_regs(sc); #if 0 - printf("async_head:\n"); - ehci_dump_sqh(sc->sc_async_head); + printf("async_head:\n"); + ehci_dump_sqh(sc->sc_async_head); +#endif + DPRINTF("sqh:", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(exfer->ex_sqtdstart); #endif - printf("sqh:\n"); - ehci_dump_sqh(sqh); - ehci_dump_sqtds(data); - } #endif - if (sc->sc_bus.use_polling) - ehci_waitintr(sc, xfer); - - return (USBD_IN_PROGRESS); -#undef exfer + return USBD_IN_PROGRESS; } Static void -ehci_device_bulk_abort(usbd_xfer_handle xfer) +ehci_device_bulk_abort(struct usbd_xfer *xfer) { - DPRINTF(("ehci_device_bulk_abort: xfer=%p\n", xfer)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer %p", xfer, 0, 0, 0); ehci_abort_xfer(xfer, USBD_CANCELLED); } @@ -3655,40 +3883,38 @@ ehci_device_bulk_abort(usbd_xfer_handle * Close a device bulk pipe. */ Static void -ehci_device_bulk_close(usbd_pipe_handle pipe) +ehci_device_bulk_close(struct usbd_pipe *pipe) { - ehci_softc_t *sc = pipe->device->bus->hci_private; - struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_softc_t *sc = EHCI_PIPE2SC(pipe); + struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); KASSERT(mutex_owned(&sc->sc_lock)); - DPRINTF(("ehci_device_bulk_close: pipe=%p\n", pipe)); - pipe->endpoint->datatoggle = epipe->nexttoggle; + DPRINTF("pipe=%p", pipe, 0, 0, 0); + pipe->up_endpoint->ue_toggle = epipe->nexttoggle; ehci_close_pipe(pipe, sc->sc_async_head); } Static void -ehci_device_bulk_done(usbd_xfer_handle xfer) +ehci_device_bulk_done(struct usbd_xfer *xfer) { - struct ehci_xfer *ex = EXFER(xfer); - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - int endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; + ehci_softc_t *sc __diagused = EHCI_XFER2SC(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + int endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; int rd = UE_GET_DIR(endpt) == UE_DIR_IN; - DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n", - xfer, xfer->actlen)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - KASSERT(mutex_owned(&sc->sc_lock)); + DPRINTF("xfer=%p, actlen=%d", xfer, xfer->ux_actlen, 0, 0); - if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { - ehci_del_intr_list(sc, ex); /* remove from active list */ - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); - usb_syncmem(&xfer->dmabuf, 0, xfer->length, - rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); - } + KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); + + usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, + rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); - DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen)); + DPRINTF("length=%d", xfer->ux_actlen, 0, 0, 0); } /************************/ @@ -3715,13 +3941,54 @@ ehci_device_setintr(ehci_softc_t *sc, eh ehci_add_qh(sc, sqh, isp->sqh); mutex_exit(&sc->sc_lock); - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; +} + + +Static int +ehci_device_intr_init(struct usbd_xfer *xfer) +{ + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc; + int endpt = ed->bEndpointAddress; + int isread = UE_GET_DIR(endpt) == UE_DIR_IN; + int len = xfer->ux_bufsize; + int err; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer=%p len=%d flags=%d", xfer, xfer->ux_length, + xfer->ux_flags, 0); + + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(len != 0); + + exfer->ex_type = EX_INTR; + exfer->ex_nsqtd = 0; + err = ehci_alloc_sqtd_chain(sc, xfer, len, isread, + &exfer->ex_sqtdstart); + + return err; +} + +Static void +ehci_device_intr_fini(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); + + KASSERT(ex->ex_type == EX_INTR); + + ehci_free_sqtds(sc, ex); + if (ex->ex_nsqtd) + kmem_free(ex->ex_sqtds, sizeof(ehci_soft_qtd_t *) * ex->ex_nsqtd); } Static usbd_status -ehci_device_intr_transfer(usbd_xfer_handle xfer) +ehci_device_intr_transfer(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); usbd_status err; /* Insert last in queue. */ @@ -3729,108 +3996,98 @@ ehci_device_intr_transfer(usbd_xfer_hand err = usb_insert_transfer(xfer); mutex_exit(&sc->sc_lock); if (err) - return (err); + return err; /* * Pipe isn't running (otherwise err would be USBD_INPROG), * so start it first. */ - return (ehci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); + return ehci_device_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); } Static usbd_status -ehci_device_intr_start(usbd_xfer_handle xfer) +ehci_device_intr_start(struct usbd_xfer *xfer) { -#define exfer EXFER(xfer) - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - usbd_device_handle dev = xfer->pipe->device; - ehci_softc_t *sc = dev->bus->hci_private; - ehci_soft_qtd_t *data, *dataend; + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + ehci_soft_qtd_t *end; ehci_soft_qh_t *sqh; - usbd_status err; int len, isread, endpt; - DPRINTFN(2, ("ehci_device_intr_start: xfer=%p len=%d flags=%d\n", - xfer, xfer->length, xfer->flags)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer=%p len=%d flags=%d", xfer, xfer->ux_length, + xfer->ux_flags, 0); if (sc->sc_dying) - return (USBD_IOERROR); + return USBD_IOERROR; + + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(xfer->ux_length <= xfer->ux_bufsize); + + len = xfer->ux_length; + endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; + isread = UE_GET_DIR(endpt) == UE_DIR_IN; + sqh = epipe->sqh; + KASSERT(exfer->ex_isdone); #ifdef DIAGNOSTIC - if (xfer->rqflags & URQ_REQUEST) - panic("ehci_device_intr_start: a request"); + exfer->ex_isdone = false; #endif + /* Take lock to protect nexttoggle */ mutex_enter(&sc->sc_lock); - len = xfer->length; - endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; - isread = UE_GET_DIR(endpt) == UE_DIR_IN; - sqh = epipe->sqh; + ehci_reset_sqtd_chain(sc, xfer, len, isread, &epipe->nexttoggle, &end); - epipe->u.intr.length = len; - - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, - &dataend); - if (err) { - DPRINTFN(-1, ("ehci_device_intr_start: no memory\n")); - xfer->status = err; - usb_transfer_complete(xfer); - mutex_exit(&sc->sc_lock); - return (err); - } + end->qtd.qtd_status |= htole32(EHCI_QTD_IOC); + usb_syncmem(&end->dma, end->offs, sizeof(end->qtd), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + exfer->ex_sqtdend = end; #ifdef EHCI_DEBUG - if (ehcidebug > 5) { - DPRINTF(("ehci_device_intr_start: data(1)\n")); - ehci_dump_sqh(sqh); - ehci_dump_sqtds(data); - } -#endif - - /* Set up interrupt info. */ - exfer->sqtdstart = data; - exfer->sqtdend = dataend; -#ifdef DIAGNOSTIC - if (!exfer->isdone) { - printf("ehci_device_intr_start: not done, ex=%p\n", exfer); - } - exfer->isdone = 0; + DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(exfer->ex_sqtdstart); + DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); #endif - ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */ - if (xfer->timeout && !sc->sc_bus.use_polling) { - callout_reset(&xfer->timeout_handle, mstohz(xfer->timeout), + usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, + isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + /* also does usb_syncmem(sqh) */ + ehci_set_qh_qtd(sqh, exfer->ex_sqtdstart); + if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), ehci_timeout, xfer); } ehci_add_intr_list(sc, exfer); - xfer->status = USBD_IN_PROGRESS; + xfer->ux_status = USBD_IN_PROGRESS; mutex_exit(&sc->sc_lock); +#if 0 #ifdef EHCI_DEBUG - if (ehcidebug > 10) { - DPRINTF(("ehci_device_intr_start: data(2)\n")); - delay(10000); - DPRINTF(("ehci_device_intr_start: data(3)\n")); - ehci_dump_regs(sc); - printf("sqh:\n"); - ehci_dump_sqh(sqh); - ehci_dump_sqtds(data); - } + DPRINTFN(5, "data(2)", 0, 0, 0, 0); +// delay(10000); + DPRINTFN(5, "data(3)", 0, 0, 0, 0); + ehci_dump_regs(sc); + DPRINTFN(5, "sqh:", 0, 0, 0, 0); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(exfer->ex_sqtdstart); +#endif #endif - if (sc->sc_bus.use_polling) - ehci_waitintr(sc, xfer); - - return (USBD_IN_PROGRESS); -#undef exfer + return USBD_IN_PROGRESS; } Static void -ehci_device_intr_abort(usbd_xfer_handle xfer) +ehci_device_intr_abort(struct usbd_xfer *xfer) { - DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer)); - KASSERT(xfer->pipe->intrxfer == xfer); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer=%p", xfer, 0, 0, 0); + KASSERT(xfer->ux_pipe->up_intrxfer == xfer); /* * XXX - abort_xfer uses ehci_sync_hc, which syncs via the advance @@ -3841,10 +4098,10 @@ ehci_device_intr_abort(usbd_xfer_handle } Static void -ehci_device_intr_close(usbd_pipe_handle pipe) +ehci_device_intr_close(struct usbd_pipe *pipe) { - ehci_softc_t *sc = pipe->device->bus->hci_private; - struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_softc_t *sc = EHCI_PIPE2SC(pipe); + struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); struct ehci_soft_islot *isp; KASSERT(mutex_owned(&sc->sc_lock)); @@ -3854,120 +4111,531 @@ ehci_device_intr_close(usbd_pipe_handle } Static void -ehci_device_intr_done(usbd_xfer_handle xfer) +ehci_device_intr_done(struct usbd_xfer *xfer) { -#define exfer EXFER(xfer) - struct ehci_xfer *ex = EXFER(xfer); - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; - ehci_soft_qtd_t *data, *dataend; - ehci_soft_qh_t *sqh; - usbd_status err; - int len, isread, endpt; + ehci_softc_t *sc __diagused = EHCI_XFER2SC(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + int isread, endpt; - DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n", - xfer, xfer->actlen)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); - KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); + DPRINTF("xfer=%p, actlen=%d", xfer, xfer->ux_actlen, + 0, 0); - if (xfer->pipe->repeat) { - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); - len = epipe->u.intr.length; - xfer->length = len; - endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; - isread = UE_GET_DIR(endpt) == UE_DIR_IN; - usb_syncmem(&xfer->dmabuf, 0, len, - isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); - sqh = epipe->sqh; + endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; + isread = UE_GET_DIR(endpt) == UE_DIR_IN; + usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, + isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); +} - err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, - &data, &dataend); - if (err) { - DPRINTFN(-1, ("ehci_device_intr_done: no memory\n")); - xfer->status = err; - return; +/************************/ +Static int +ehci_device_fs_isoc_init(struct usbd_xfer *xfer) +{ + struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(xfer->ux_pipe); + struct usbd_device *dev = xfer->ux_pipe->up_dev; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_soft_sitd_t *sitd, *prev, *start, *stop; + int i, k, frames; + u_int huba, dir; + int err; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + start = NULL; + sitd = NULL; + + DPRINTF("xfer %p len %d flags %d", xfer, xfer->ux_length, + xfer->ux_flags, 0); + + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(xfer->ux_nframes != 0); + KASSERT(exfer->ex_isdone); + + exfer->ex_type = EX_FS_ISOC; + /* + * Step 1: Allocate and initialize sitds. + */ + i = epipe->pipe.up_endpoint->ue_edesc->bInterval; + if (i > 16 || i == 0) { + /* Spec page 271 says intervals > 16 are invalid */ + DPRINTF("bInterval %d invalid", i, 0, 0, 0); + + return EINVAL; + } + + frames = xfer->ux_nframes; + for (i = 0, prev = NULL; i < frames; i++, prev = sitd) { + sitd = ehci_alloc_sitd(sc); + if (sitd == NULL) { + err = ENOMEM; + goto fail; } - /* Set up interrupt info. */ - exfer->sqtdstart = data; - exfer->sqtdend = dataend; + if (prev) + prev->xfer_next = sitd; + else + start = sitd; + + huba = dev->ud_myhsport->up_parent->ud_addr; + +#if 0 + if (sc->sc_flags & EHCIF_FREESCALE) { + // Set hub address to 0 if embedded TT is used. + if (huba == sc->sc_addr) + huba = 0; + } +#endif + + k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; + dir = UE_GET_DIR(k) ? 1 : 0; + sitd->sitd.sitd_endp = + htole32(EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) | + EHCI_SITD_SET_DADDR(dev->ud_addr) | + EHCI_SITD_SET_PORT(dev->ud_myhsport->up_portno) | + EHCI_SITD_SET_HUBA(huba) | + EHCI_SITD_SET_DIR(dir)); + + sitd->sitd.sitd_back = htole32(EHCI_LINK_TERMINATE); + } /* End of frame */ + + sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC); + + stop = sitd; + stop->xfer_next = NULL; + exfer->ex_sitdstart = start; + exfer->ex_sitdend = stop; + + return 0; + +fail: + mutex_enter(&sc->sc_lock); + ehci_soft_sitd_t *next; + for (sitd = start; sitd; sitd = next) { + next = sitd->xfer_next; + ehci_free_sitd_locked(sc, sitd); + } + mutex_exit(&sc->sc_lock); + + return err; +} + +Static void +ehci_device_fs_isoc_fini(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); + + KASSERT(ex->ex_type == EX_FS_ISOC); + + ehci_free_sitd_chain(sc, ex->ex_sitdstart); +} + +Static usbd_status +ehci_device_fs_isoc_transfer(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + usbd_status __diagused err; + + mutex_enter(&sc->sc_lock); + err = usb_insert_transfer(xfer); + mutex_exit(&sc->sc_lock); + + KASSERT(err == USBD_NORMAL_COMPLETION); + + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer);; + struct usbd_device *dev = xfer->ux_pipe->up_dev;; + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_soft_sitd_t *sitd; + usb_dma_t *dma_buf; + int i, j, k, frames; + int offs, total_length; + int frindex; + u_int dir; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + sitd = NULL; + total_length = 0; + + + DPRINTF("xfer %p len %d flags %d", xfer, xfer->ux_length, + xfer->ux_flags, 0); + + if (sc->sc_dying) + return USBD_IOERROR; + + /* + * To avoid complication, don't allow a request right now that'll span + * the entire frame table. To within 4 frames, to allow some leeway + * on either side of where the hc currently is. + */ + if (epipe->pipe.up_endpoint->ue_edesc->bInterval * + xfer->ux_nframes >= sc->sc_flsize - 4) { + printf("ehci: isoc descriptor requested that spans the entire" + "frametable, too many frames\n"); + return USBD_INVAL; + } + + KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths); + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(exfer->ex_isdone); #ifdef DIAGNOSTIC - if (!exfer->isdone) { - printf("ehci_device_intr_done: not done, ex=%p\n", - exfer); + exfer->ex_isdone = false; +#endif + + /* + * Step 1: Initialize sitds. + */ + + frames = xfer->ux_nframes; + dma_buf = &xfer->ux_dmabuf; + offs = 0; + + for (sitd = exfer->ex_sitdstart, i = 0; i < frames; + i++, sitd = sitd->xfer_next) { + KASSERT(sitd != NULL); + KASSERT(xfer->ux_frlengths[i] <= 0x3ff); + + sitd->sitd.sitd_trans = htole32(EHCI_SITD_ACTIVE | + EHCI_SITD_SET_LEN(xfer->ux_frlengths[i])); + + /* Set page0 index and offset - TP and T-offset are set below */ + sitd->sitd.sitd_buffer[0] = htole32(DMAADDR(dma_buf, offs)); + + total_length += xfer->ux_frlengths[i]; + offs += xfer->ux_frlengths[i]; + + sitd->sitd.sitd_buffer[1] = + htole32(EHCI_SITD_SET_BPTR(DMAADDR(dma_buf, offs - 1))); + + u_int huba __diagused = dev->ud_myhsport->up_parent->ud_addr; + +#if 0 + if (sc->sc_flags & EHCIF_FREESCALE) { + // Set hub address to 0 if embedded TT is used. + if (huba == sc->sc_addr) + huba = 0; } - exfer->isdone = 0; #endif - ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */ - if (xfer->timeout && !sc->sc_bus.use_polling) { - callout_reset(&xfer->timeout_handle, - mstohz(xfer->timeout), ehci_timeout, xfer); + k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; + dir = UE_GET_DIR(k) ? 1 : 0; + KASSERT(sitd->sitd.sitd_endp == htole32( + EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) | + EHCI_SITD_SET_DADDR(dev->ud_addr) | + EHCI_SITD_SET_PORT(dev->ud_myhsport->up_portno) | + EHCI_SITD_SET_HUBA(huba) | + EHCI_SITD_SET_DIR(dir))); + KASSERT(sitd->sitd.sitd_back == htole32(EHCI_LINK_TERMINATE)); + + uint8_t sa = 0; + uint8_t sb = 0; + u_int temp, tlen; + + if (dir == 0) { /* OUT */ + temp = 0; + tlen = xfer->ux_frlengths[i]; + if (tlen <= 188) { + temp |= 1; /* T-count = 1, TP = ALL */ + tlen = 1; + } else { + tlen += 187; + tlen /= 188; + temp |= tlen; /* T-count = [1..6] */ + temp |= 8; /* TP = Begin */ + } + sitd->sitd.sitd_buffer[1] |= htole32(temp); + + tlen += sa; + + if (tlen >= 8) { + sb = 0; + } else { + sb = (1 << tlen); + } + + sa = (1 << sa); + sa = (sb - sa) & 0x3F; + sb = 0; + } else { + sb = (-(4 << sa)) & 0xFE; + sa = (1 << sa) & 0x3F; + sa = 0x01; + sb = 0xfc; } - xfer->status = USBD_IN_PROGRESS; - } else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { - ehci_del_intr_list(sc, ex); /* remove from active list */ - ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); - endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; - isread = UE_GET_DIR(endpt) == UE_DIR_IN; - usb_syncmem(&xfer->dmabuf, 0, xfer->length, - isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + sitd->sitd.sitd_sched = htole32( + EHCI_SITD_SET_SMASK(sa) | + EHCI_SITD_SET_CMASK(sb) + ); + + usb_syncmem(&sitd->dma, sitd->offs, sizeof(ehci_sitd_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + } /* End of frame */ + + sitd = exfer->ex_sitdend; + sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC); + + usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), + sizeof(sitd->sitd.sitd_trans), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + usb_syncmem(&exfer->ex_xfer.ux_dmabuf, 0, total_length, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* + * Part 2: Transfer descriptors have now been set up, now they must + * be scheduled into the periodic frame list. Erk. Not wanting to + * complicate matters, transfer is denied if the transfer spans + * more than the period frame list. + */ + + mutex_enter(&sc->sc_lock); + + /* Start inserting frames */ + if (epipe->isoc.cur_xfers > 0) { + frindex = epipe->isoc.next_frame; + } else { + frindex = EOREAD4(sc, EHCI_FRINDEX); + frindex = frindex >> 3; /* Erase microframe index */ + frindex += 2; } -#undef exfer + + if (frindex >= sc->sc_flsize) + frindex &= (sc->sc_flsize - 1); + + /* Whats the frame interval? */ + i = epipe->pipe.up_endpoint->ue_edesc->bInterval; + + for (sitd = exfer->ex_sitdstart, j = 0; j < frames; + j++, sitd = sitd->xfer_next) { + KASSERT(sitd); + + usb_syncmem(&sc->sc_fldma, + sizeof(ehci_link_t) * frindex, + sizeof(ehci_link_t), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + sitd->sitd.sitd_next = sc->sc_flist[frindex]; + if (sitd->sitd.sitd_next == 0) + /* + * FIXME: frindex table gets initialized to NULL + * or EHCI_NULL? + */ + sitd->sitd.sitd_next = EHCI_NULL; + + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_next), + sizeof(ehci_sitd_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + sc->sc_flist[frindex] = + htole32(EHCI_LINK_SITD | sitd->physaddr); + + usb_syncmem(&sc->sc_fldma, + sizeof(ehci_link_t) * frindex, + sizeof(ehci_link_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + sitd->frame_list.next = sc->sc_softsitds[frindex]; + sc->sc_softsitds[frindex] = sitd; + if (sitd->frame_list.next != NULL) + sitd->frame_list.next->frame_list.prev = sitd; + sitd->slot = frindex; + sitd->frame_list.prev = NULL; + + frindex += i; + if (frindex >= sc->sc_flsize) + frindex -= sc->sc_flsize; + } + + epipe->isoc.cur_xfers++; + epipe->isoc.next_frame = frindex; + + ehci_add_intr_list(sc, exfer); + xfer->ux_status = USBD_IN_PROGRESS; + + mutex_exit(&sc->sc_lock); + + return USBD_IN_PROGRESS; +} + +Static void +ehci_device_fs_isoc_abort(struct usbd_xfer *xfer) +{ + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer = %p", xfer, 0, 0, 0); + ehci_abort_isoc_xfer(xfer, USBD_CANCELLED); +} + +Static void +ehci_device_fs_isoc_close(struct usbd_pipe *pipe) +{ + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("nothing in the pipe to free?", 0, 0, 0, 0); +} + +Static void +ehci_device_fs_isoc_done(struct usbd_xfer *xfer) +{ + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + + KASSERT(mutex_owned(&sc->sc_lock)); + + epipe->isoc.cur_xfers--; + ehci_remove_sitd_chain(sc, exfer->ex_itdstart); + + usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); } + /************************/ -Static usbd_status -ehci_device_isoc_transfer(usbd_xfer_handle xfer) + +Static int +ehci_device_isoc_init(struct usbd_xfer *xfer) { - ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; - usbd_status err; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_soft_itd_t *itd, *prev, *start, *stop; + int i, j, k; + int frames, ufrperframe; + int err; + + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + start = NULL; + prev = NULL; + itd = NULL; + + KASSERT(xfer->ux_nframes != 0); + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(exfer->ex_isdone); + + exfer->ex_type = EX_ISOC; + + /* + * Step 1: Allocate and initialize itds, how many do we need? + * One per transfer if interval >= 8 microframes, less if we use + * multiple microframes per frame. + */ + i = epipe->pipe.up_endpoint->ue_edesc->bInterval; + if (i > 16 || i == 0) { + /* Spec page 271 says intervals > 16 are invalid */ + DPRINTF("bInterval %d invalid", i, 0, 0, 0); + return USBD_INVAL; + } + + ufrperframe = max(1, USB_UFRAMES_PER_FRAME / (1 << (i - 1))); + frames = (xfer->ux_nframes + (ufrperframe - 1)) / ufrperframe; + + for (i = 0, prev = NULL; i < frames; i++, prev = itd) { + itd = ehci_alloc_itd(sc); + if (itd == NULL) { + err = ENOMEM; + goto fail; + } + + if (prev != NULL) { + /* Maybe not as it's updated by the scheduling? */ + prev->itd.itd_next = + htole32(itd->physaddr | EHCI_LINK_ITD); + + prev->xfer_next = itd; + } else { + start = itd; + } + + /* + * Other special values + */ + k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; + itd->itd.itd_bufr[0] = htole32( + EHCI_ITD_SET_EP(UE_GET_ADDR(k)) | + EHCI_ITD_SET_DADDR(epipe->pipe.up_dev->ud_addr)); + + k = (UE_GET_DIR(epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress)) + ? 1 : 0; + j = UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize); + itd->itd.itd_bufr[1] |= htole32( + EHCI_ITD_SET_DIR(k) | + EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j))); + + /* FIXME: handle invalid trans - should be done in openpipe */ + itd->itd.itd_bufr[2] |= + htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1)); + } /* End of frame */ + + stop = itd; + stop->xfer_next = NULL; + + exfer->ex_itdstart = start; + exfer->ex_itdend = stop; + + return 0; +fail: mutex_enter(&sc->sc_lock); - err = usb_insert_transfer(xfer); + ehci_soft_itd_t *next; + for (itd = start; itd; itd = next) { + next = itd->xfer_next; + ehci_free_itd_locked(sc, itd); + } mutex_exit(&sc->sc_lock); - if (err && err != USBD_IN_PROGRESS) - return err; - return ehci_device_isoc_start(xfer); + return err; + +} + +Static void +ehci_device_isoc_fini(struct usbd_xfer *xfer) +{ + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); + + KASSERT(ex->ex_type == EX_ISOC); + + ehci_free_itd_chain(sc, ex->ex_itdstart); } Static usbd_status -ehci_device_isoc_start(usbd_xfer_handle xfer) +ehci_device_isoc_transfer(struct usbd_xfer *xfer) { - struct ehci_pipe *epipe; - ehci_softc_t *sc; - struct ehci_xfer *exfer; - ehci_soft_itd_t *itd, *prev, *start, *stop; + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + usbd_status __diagused err; + + mutex_enter(&sc->sc_lock); + err = usb_insert_transfer(xfer); + mutex_exit(&sc->sc_lock); + + KASSERT(err == USBD_NORMAL_COMPLETION); + + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_soft_itd_t *itd, *prev; usb_dma_t *dma_buf; - int i, j, k, frames, uframes, ufrperframe; + int i, j; + int frames, uframes, ufrperframe; int trans_count, offs, total_length; int frindex; - start = NULL; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + prev = NULL; itd = NULL; trans_count = 0; total_length = 0; - exfer = (struct ehci_xfer *) xfer; - sc = xfer->pipe->device->bus->hci_private; - epipe = (struct ehci_pipe *)xfer->pipe; - /* - * To allow continuous transfers, above we start all transfers - * immediately. However, we're still going to get usbd_start_next call - * this when another xfer completes. So, check if this is already - * in progress or not - */ - - if (exfer->itdstart != NULL) - return USBD_IN_PROGRESS; - - DPRINTFN(2, ("ehci_device_isoc_start: xfer %p len %d flags %d\n", - xfer, xfer->length, xfer->flags)); + DPRINTF("xfer %p flags %d", xfer, xfer->ux_flags, 0, 0); if (sc->sc_dying) return USBD_IOERROR; @@ -3977,60 +4645,55 @@ ehci_device_isoc_start(usbd_xfer_handle * the entire frame table. To within 4 frames, to allow some leeway * on either side of where the hc currently is. */ - if ((1 << (epipe->pipe.endpoint->edesc->bInterval)) * - xfer->nframes >= (sc->sc_flsize - 4) * 8) { + if ((1 << (epipe->pipe.up_endpoint->ue_edesc->bInterval)) * + xfer->ux_nframes >= (sc->sc_flsize - 4) * 8) { + DPRINTF( + "isoc descriptor spans entire frametable", 0, 0, 0, 0); printf("ehci: isoc descriptor requested that spans the entire frametable, too many frames\n"); return USBD_INVAL; } + KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths); + KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); + KASSERT(exfer->ex_isdone); #ifdef DIAGNOSTIC - if (xfer->rqflags & URQ_REQUEST) - panic("ehci_device_isoc_start: request\n"); - - if (!exfer->isdone) - printf("ehci_device_isoc_start: not done, ex = %p\n", exfer); - exfer->isdone = 0; + exfer->ex_isdone = false; #endif /* - * Step 1: Allocate and initialize itds, how many do we need? - * One per transfer if interval >= 8 microframes, fewer if we use - * multiple microframes per frame. + * Step 1: Re-Initialize itds */ - i = epipe->pipe.endpoint->edesc->bInterval; + i = epipe->pipe.up_endpoint->ue_edesc->bInterval; if (i > 16 || i == 0) { /* Spec page 271 says intervals > 16 are invalid */ - DPRINTF(("ehci_device_isoc_start: bInvertal %d invalid\n", i)); + DPRINTF("bInterval %d invalid", i, 0, 0, 0); return USBD_INVAL; } ufrperframe = max(1, USB_UFRAMES_PER_FRAME / (1 << (i - 1))); - frames = (xfer->nframes + (ufrperframe - 1)) / ufrperframe; + frames = (xfer->ux_nframes + (ufrperframe - 1)) / ufrperframe; uframes = USB_UFRAMES_PER_FRAME / ufrperframe; if (frames == 0) { - DPRINTF(("ehci_device_isoc_start: frames == 0\n")); + DPRINTF("frames == 0", 0, 0, 0, 0); return USBD_INVAL; } - dma_buf = &xfer->dmabuf; + dma_buf = &xfer->ux_dmabuf; offs = 0; - for (i = 0; i < frames; i++) { + itd = exfer->ex_itdstart; + for (i = 0; i < frames; i++, itd = itd->xfer_next) { int froffs = offs; - itd = ehci_alloc_itd(sc); if (prev != NULL) { prev->itd.itd_next = htole32(itd->physaddr | EHCI_LINK_ITD); - usb_syncmem(&itd->dma, - itd->offs + offsetof(ehci_itd_t, itd_next), - sizeof(itd->itd.itd_next), BUS_DMASYNC_POSTWRITE); - + usb_syncmem(&prev->dma, + prev->offs + offsetof(ehci_itd_t, itd_next), + sizeof(prev->itd.itd_next), BUS_DMASYNC_POSTWRITE); prev->xfer_next = itd; - } else { - start = itd; } /* @@ -4044,27 +4707,29 @@ ehci_device_isoc_start(usbd_xfer_handle addr = EHCI_PAGE(addr); addr /= EHCI_PAGE_SIZE; - /* This gets the initial offset into the first page, + /* + * This gets the initial offset into the first page, * looks how far further along the current uframe * offset is. Works out how many pages that is. */ itd->itd.itd_ctl[j] = htole32 ( EHCI_ITD_ACTIVE | - EHCI_ITD_SET_LEN(xfer->frlengths[trans_count]) | + EHCI_ITD_SET_LEN(xfer->ux_frlengths[trans_count]) | EHCI_ITD_SET_PG(addr) | EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,offs)))); - total_length += xfer->frlengths[trans_count]; - offs += xfer->frlengths[trans_count]; + total_length += xfer->ux_frlengths[trans_count]; + offs += xfer->ux_frlengths[trans_count]; trans_count++; - if (trans_count >= xfer->nframes) { /*Set IOC*/ + if (trans_count >= xfer->ux_nframes) { /*Set IOC*/ itd->itd.itd_ctl[j] |= htole32(EHCI_ITD_IOC); break; } } - /* Step 1.75, set buffer pointers. To simplify matters, all + /* + * Step 1.75, set buffer pointers. To simplify matters, all * pointers are filled out for the next 7 hardware pages in * the dma block, so no need to worry what pages to cover * and what to not. @@ -4076,28 +4741,25 @@ ehci_device_isoc_start(usbd_xfer_handle * of buffer */ int page_offs = EHCI_PAGE(froffs + (EHCI_PAGE_SIZE * j)); - if (page_offs >= dma_buf->block->size) + if (page_offs >= dma_buf->udma_block->size) break; - unsigned long long page = DMAADDR(dma_buf, page_offs); + uint64_t page = DMAADDR(dma_buf, page_offs); page = EHCI_PAGE(page); - itd->itd.itd_bufr[j] = - htole32(EHCI_ITD_SET_BPTR(page)); - itd->itd.itd_bufr_hi[j] = - htole32(page >> 32); + itd->itd.itd_bufr[j] = htole32(EHCI_ITD_SET_BPTR(page)); + itd->itd.itd_bufr_hi[j] = htole32(page >> 32); } - /* * Other special values */ - k = epipe->pipe.endpoint->edesc->bEndpointAddress; + int k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; itd->itd.itd_bufr[0] |= htole32(EHCI_ITD_SET_EP(UE_GET_ADDR(k)) | - EHCI_ITD_SET_DADDR(epipe->pipe.device->address)); + EHCI_ITD_SET_DADDR(epipe->pipe.up_dev->ud_addr)); - k = (UE_GET_DIR(epipe->pipe.endpoint->edesc->bEndpointAddress)) + k = (UE_GET_DIR(epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress)) ? 1 : 0; - j = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + j = UGETW(epipe->pipe.up_endpoint->ue_edesc->wMaxPacketSize); itd->itd.itd_bufr[1] |= htole32(EHCI_ITD_SET_DIR(k) | EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j))); @@ -4105,20 +4767,14 @@ ehci_device_isoc_start(usbd_xfer_handle itd->itd.itd_bufr[2] |= htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1)); - usb_syncmem(&itd->dma, - itd->offs + offsetof(ehci_itd_t, itd_next), - sizeof(ehci_itd_t), + usb_syncmem(&itd->dma, itd->offs, sizeof(ehci_itd_t), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); prev = itd; } /* End of frame */ - stop = itd; - stop->xfer_next = NULL; - exfer->isoc_len = total_length; - - usb_syncmem(&exfer->xfer.dmabuf, 0, total_length, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + usb_syncmem(&exfer->ex_xfer.ux_dmabuf, 0, total_length, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * Part 2: Transfer descriptors have now been set up, now they must @@ -4130,8 +4786,8 @@ ehci_device_isoc_start(usbd_xfer_handle mutex_enter(&sc->sc_lock); /* Start inserting frames */ - if (epipe->u.isoc.cur_xfers > 0) { - frindex = epipe->u.isoc.next_frame; + if (epipe->isoc.cur_xfers > 0) { + frindex = epipe->isoc.next_frame; } else { frindex = EOREAD4(sc, EHCI_FRINDEX); frindex = frindex >> 3; /* Erase microframe index */ @@ -4142,41 +4798,47 @@ ehci_device_isoc_start(usbd_xfer_handle frindex &= (sc->sc_flsize - 1); /* What's the frame interval? */ - i = (1 << (epipe->pipe.endpoint->edesc->bInterval - 1)); + i = (1 << (epipe->pipe.up_endpoint->ue_edesc->bInterval - 1)); if (i / USB_UFRAMES_PER_FRAME == 0) i = 1; else i /= USB_UFRAMES_PER_FRAME; - itd = start; + itd = exfer->ex_itdstart; for (j = 0; j < frames; j++) { - if (itd == NULL) - panic("ehci: unexpectedly ran out of isoc itds, isoc_start\n"); + KASSERTMSG(itd != NULL, "frame %d\n", j); + + usb_syncmem(&sc->sc_fldma, + sizeof(ehci_link_t) * frindex, + sizeof(ehci_link_t), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); itd->itd.itd_next = sc->sc_flist[frindex]; if (itd->itd.itd_next == 0) - /* FIXME: frindex table gets initialized to NULL - * or EHCI_NULL? */ + /* + * FIXME: frindex table gets initialized to NULL + * or EHCI_NULL? + */ itd->itd.itd_next = EHCI_NULL; usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_next), - sizeof(itd->itd.itd_next), + sizeof(itd->itd.itd_next), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); sc->sc_flist[frindex] = htole32(EHCI_LINK_ITD | itd->physaddr); usb_syncmem(&sc->sc_fldma, sizeof(ehci_link_t) * frindex, - sizeof(ehci_link_t), + sizeof(ehci_link_t), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - itd->u.frame_list.next = sc->sc_softitds[frindex]; + itd->frame_list.next = sc->sc_softitds[frindex]; sc->sc_softitds[frindex] = itd; - if (itd->u.frame_list.next != NULL) - itd->u.frame_list.next->u.frame_list.prev = itd; + if (itd->frame_list.next != NULL) + itd->frame_list.next->frame_list.prev = itd; itd->slot = frindex; - itd->u.frame_list.prev = NULL; + itd->frame_list.prev = NULL; frindex += i; if (frindex >= sc->sc_flsize) @@ -4185,60 +4847,45 @@ ehci_device_isoc_start(usbd_xfer_handle itd = itd->xfer_next; } - epipe->u.isoc.cur_xfers++; - epipe->u.isoc.next_frame = frindex; - - exfer->itdstart = start; - exfer->itdend = stop; - exfer->sqtdstart = NULL; - exfer->sqtdend = NULL; + epipe->isoc.cur_xfers++; + epipe->isoc.next_frame = frindex; ehci_add_intr_list(sc, exfer); - xfer->status = USBD_IN_PROGRESS; - xfer->done = 0; - mutex_exit(&sc->sc_lock); + xfer->ux_status = USBD_IN_PROGRESS; - if (sc->sc_bus.use_polling) { - printf("Starting ehci isoc xfer with polling. Bad idea?\n"); - ehci_waitintr(sc, xfer); - } + mutex_exit(&sc->sc_lock); return USBD_IN_PROGRESS; } Static void -ehci_device_isoc_abort(usbd_xfer_handle xfer) +ehci_device_isoc_abort(struct usbd_xfer *xfer) { - DPRINTFN(1, ("ehci_device_isoc_abort: xfer = %p\n", xfer)); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("xfer = %p", xfer, 0, 0, 0); ehci_abort_isoc_xfer(xfer, USBD_CANCELLED); } Static void -ehci_device_isoc_close(usbd_pipe_handle pipe) +ehci_device_isoc_close(struct usbd_pipe *pipe) { - DPRINTFN(1, ("ehci_device_isoc_close: nothing in the pipe to free?\n")); + EHCIHIST_FUNC(); EHCIHIST_CALLED(); + + DPRINTF("nothing in the pipe to free?", 0, 0, 0, 0); } Static void -ehci_device_isoc_done(usbd_xfer_handle xfer) +ehci_device_isoc_done(struct usbd_xfer *xfer) { - struct ehci_xfer *exfer; - ehci_softc_t *sc; - struct ehci_pipe *epipe; - - exfer = EXFER(xfer); - sc = xfer->pipe->device->bus->hci_private; - epipe = (struct ehci_pipe *) xfer->pipe; + struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); + ehci_softc_t *sc = EHCI_XFER2SC(xfer); + struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); KASSERT(mutex_owned(&sc->sc_lock)); - epipe->u.isoc.cur_xfers--; - if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) { - ehci_del_intr_list(sc, exfer); - ehci_rem_free_itd_chain(sc, exfer); - } - - usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_POSTWRITE | - BUS_DMASYNC_POSTREAD); - + epipe->isoc.cur_xfers--; + ehci_remove_itd_chain(sc, exfer->ex_sitdstart); + usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); }