version 1.228, 2014/08/05 10:33:46 |
version 1.228.2.3, 2018/08/25 14:57:35 |
Line 58 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 58 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include "ohci.h" |
#include "ohci.h" |
#include "uhci.h" |
#include "uhci.h" |
|
|
|
#ifdef _KERNEL_OPT |
|
#include "opt_usb.h" |
|
#endif |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
|
|
#include <sys/bus.h> |
|
#include <sys/cpu.h> |
|
#include <sys/device.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
#include <sys/kmem.h> |
#include <sys/kmem.h> |
#include <sys/device.h> |
#include <sys/mutex.h> |
#include <sys/select.h> |
|
#include <sys/proc.h> |
#include <sys/proc.h> |
#include <sys/queue.h> |
#include <sys/queue.h> |
#include <sys/mutex.h> |
#include <sys/select.h> |
#include <sys/bus.h> |
#include <sys/sysctl.h> |
#include <sys/cpu.h> |
#include <sys/systm.h> |
|
|
#include <machine/endian.h> |
#include <machine/endian.h> |
|
|
#include <dev/usb/usb.h> |
#include <dev/usb/usb.h> |
#include <dev/usb/usbdi.h> |
#include <dev/usb/usbdi.h> |
#include <dev/usb/usbdivar.h> |
#include <dev/usb/usbdivar.h> |
|
#include <dev/usb/usbhist.h> |
#include <dev/usb/usb_mem.h> |
#include <dev/usb/usb_mem.h> |
#include <dev/usb/usb_quirks.h> |
#include <dev/usb/usb_quirks.h> |
|
|
#include <dev/usb/ehcireg.h> |
#include <dev/usb/ehcireg.h> |
#include <dev/usb/ehcivar.h> |
#include <dev/usb/ehcivar.h> |
#include <dev/usb/usbroothub_subr.h> |
#include <dev/usb/usbroothub.h> |
|
|
#ifdef EHCI_DEBUG |
|
static void __printflike(1, 2) |
#ifdef USB_DEBUG |
ehciprintf(const char *fmt, ...) |
#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); |
return; |
vprintf(fmt, ap); |
fail: |
va_end(ap); |
aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); |
} |
} |
|
|
#define DPRINTF(x) do { if (ehcidebug) ehciprintf x; } while(0) |
#endif /* EHCI_DEBUG */ |
#define DPRINTFN(n,x) do { if (ehcidebug>(n)) ehciprintf x; } while (0) |
#endif /* USB_DEBUG */ |
int ehcidebug = 0; |
|
#else |
#define DPRINTF(FMT,A,B,C,D) USBHIST_LOG(ehcidebug,FMT,A,B,C,D) |
#define DPRINTF(x) |
#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(ehcidebug,N,FMT,A,B,C,D) |
#define DPRINTFN(n,x) |
#define EHCIHIST_FUNC() USBHIST_FUNC() |
#endif |
#define EHCIHIST_CALLED() USBHIST_CALLED(ehcidebug) |
|
|
struct ehci_pipe { |
struct ehci_pipe { |
struct usbd_pipe pipe; |
struct usbd_pipe pipe; |
Line 107 struct ehci_pipe { |
|
Line 137 struct ehci_pipe { |
|
|
|
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
union { |
union { |
ehci_soft_qtd_t *qtd; |
|
/* ehci_soft_itd_t *itd; */ |
|
} tail; |
|
union { |
|
/* Control pipe */ |
/* Control pipe */ |
struct { |
struct { |
usb_dma_t reqdma; |
usb_dma_t reqdma; |
} ctl; |
} ctrl; |
/* Interrupt pipe */ |
/* Interrupt pipe */ |
struct { |
struct { |
u_int length; |
u_int length; |
} intr; |
} intr; |
/* Bulk pipe */ |
|
struct { |
|
u_int length; |
|
} bulk; |
|
/* Iso pipe */ |
/* Iso pipe */ |
struct { |
struct { |
u_int next_frame; |
u_int next_frame; |
u_int cur_xfers; |
u_int cur_xfers; |
} isoc; |
} 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_poll(struct usbd_bus *); |
Static void ehci_softintr(void *); |
Static void ehci_softintr(void *); |
Static int ehci_intr1(ehci_softc_t *); |
Static int ehci_intr1(ehci_softc_t *); |
Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle); |
Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *, |
Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *); |
ex_completeq_t *); |
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_check_itd_intr(ehci_softc_t *, struct ehci_xfer *); |
ex_completeq_t *); |
Static void ehci_idone(struct ehci_xfer *); |
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(void *); |
Static void ehci_timeout_task(void *); |
Static void ehci_timeout_task(void *); |
Static void ehci_intrlist_timeout(void *); |
Static void ehci_intrlist_timeout(void *); |
Static void ehci_doorbell(void *); |
Static void ehci_doorbell(void *); |
Static void ehci_pcd(void *); |
Static void ehci_pcd(void *); |
|
|
Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t); |
Static struct usbd_xfer * |
Static void ehci_freem(struct usbd_bus *, usb_dma_t *); |
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 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_intr_transfer(struct usbd_xfer *); |
Static usbd_status ehci_root_ctrl_start(usbd_xfer_handle); |
Static usbd_status ehci_root_intr_start(struct usbd_xfer *); |
Static void ehci_root_ctrl_abort(usbd_xfer_handle); |
Static void ehci_root_intr_abort(struct usbd_xfer *); |
Static void ehci_root_ctrl_close(usbd_pipe_handle); |
Static void ehci_root_intr_close(struct usbd_pipe *); |
Static void ehci_root_ctrl_done(usbd_xfer_handle); |
Static void ehci_root_intr_done(struct usbd_xfer *); |
|
|
Static usbd_status ehci_root_intr_transfer(usbd_xfer_handle); |
Static int ehci_device_ctrl_init(struct usbd_xfer *); |
Static usbd_status ehci_root_intr_start(usbd_xfer_handle); |
Static void ehci_device_ctrl_fini(struct usbd_xfer *); |
Static void ehci_root_intr_abort(usbd_xfer_handle); |
Static usbd_status ehci_device_ctrl_transfer(struct usbd_xfer *); |
Static void ehci_root_intr_close(usbd_pipe_handle); |
Static usbd_status ehci_device_ctrl_start(struct usbd_xfer *); |
Static void ehci_root_intr_done(usbd_xfer_handle); |
Static void ehci_device_ctrl_abort(struct usbd_xfer *); |
|
Static void ehci_device_ctrl_close(struct usbd_pipe *); |
Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle); |
Static void ehci_device_ctrl_done(struct usbd_xfer *); |
Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle); |
|
Static void ehci_device_ctrl_abort(usbd_xfer_handle); |
Static int ehci_device_bulk_init(struct usbd_xfer *); |
Static void ehci_device_ctrl_close(usbd_pipe_handle); |
Static void ehci_device_bulk_fini(struct usbd_xfer *); |
Static void ehci_device_ctrl_done(usbd_xfer_handle); |
Static usbd_status ehci_device_bulk_transfer(struct usbd_xfer *); |
|
Static usbd_status ehci_device_bulk_start(struct usbd_xfer *); |
Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle); |
Static void ehci_device_bulk_abort(struct usbd_xfer *); |
Static usbd_status ehci_device_bulk_start(usbd_xfer_handle); |
Static void ehci_device_bulk_close(struct usbd_pipe *); |
Static void ehci_device_bulk_abort(usbd_xfer_handle); |
Static void ehci_device_bulk_done(struct usbd_xfer *); |
Static void ehci_device_bulk_close(usbd_pipe_handle); |
|
Static void ehci_device_bulk_done(usbd_xfer_handle); |
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(usbd_xfer_handle); |
Static usbd_status ehci_device_intr_transfer(struct usbd_xfer *); |
Static usbd_status ehci_device_intr_start(usbd_xfer_handle); |
Static usbd_status ehci_device_intr_start(struct usbd_xfer *); |
Static void ehci_device_intr_abort(usbd_xfer_handle); |
Static void ehci_device_intr_abort(struct usbd_xfer *); |
Static void ehci_device_intr_close(usbd_pipe_handle); |
Static void ehci_device_intr_close(struct usbd_pipe *); |
Static void ehci_device_intr_done(usbd_xfer_handle); |
Static void ehci_device_intr_done(struct usbd_xfer *); |
|
|
Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle); |
Static int ehci_device_isoc_init(struct usbd_xfer *); |
Static usbd_status ehci_device_isoc_start(usbd_xfer_handle); |
Static void ehci_device_isoc_fini(struct usbd_xfer *); |
Static void ehci_device_isoc_abort(usbd_xfer_handle); |
Static usbd_status ehci_device_isoc_transfer(struct usbd_xfer *); |
Static void ehci_device_isoc_close(usbd_pipe_handle); |
Static void ehci_device_isoc_abort(struct usbd_xfer *); |
Static void ehci_device_isoc_done(usbd_xfer_handle); |
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_device_clear_toggle(struct usbd_pipe *); |
Static void ehci_noop(usbd_pipe_handle pipe); |
Static void ehci_noop(struct usbd_pipe *); |
|
|
Static void ehci_disown(ehci_softc_t *, int, int); |
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 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 void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *); |
Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *, |
Static int ehci_alloc_sqtd_chain(ehci_softc_t *, |
ehci_softc_t *, int, int, usbd_xfer_handle, |
struct usbd_xfer *, int, int, ehci_soft_qtd_t **); |
ehci_soft_qtd_t **, ehci_soft_qtd_t **); |
Static void ehci_free_sqtds(ehci_softc_t *, struct ehci_xfer *); |
Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *, |
|
ehci_soft_qtd_t *); |
Static void ehci_reset_sqtd_chain(ehci_softc_t *, struct usbd_xfer *, |
|
int, int, int *, ehci_soft_qtd_t **); |
Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *sc); |
Static void ehci_append_sqtd(ehci_soft_qtd_t *, ehci_soft_qtd_t *); |
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 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 *, |
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 *, |
Static void ehci_add_qh(ehci_softc_t *, ehci_soft_qh_t *, |
ehci_soft_qh_t *); |
ehci_soft_qh_t *); |
Line 224 Static void ehci_rem_qh(ehci_softc_t *, |
|
Line 277 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_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *); |
Static void ehci_sync_hc(ehci_softc_t *); |
Static void ehci_sync_hc(ehci_softc_t *); |
|
|
Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *); |
Static void ehci_close_pipe(struct usbd_pipe *, ehci_soft_qh_t *); |
Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status); |
Static void ehci_abort_xfer(struct usbd_xfer *, usbd_status); |
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
Static void ehci_dump_regs(ehci_softc_t *); |
|
void ehci_dump(void); |
|
Static ehci_softc_t *theehci; |
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_sqtds(ehci_soft_qtd_t *); |
Static void ehci_dump_sqtd(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_qtd(ehci_qtd_t *); |
Static void ehci_dump_sqh(ehci_soft_qh_t *); |
Static void ehci_dump_sqh(ehci_soft_qh_t *); |
#if notyet |
Static void ehci_dump_sitd(struct ehci_soft_itd *); |
Static void ehci_dump_sitd(struct ehci_soft_itd *itd); |
Static void ehci_dump_itds(ehci_soft_itd_t *); |
Static void ehci_dump_itd(struct ehci_soft_itd *); |
Static void ehci_dump_itd(struct ehci_soft_itd *); |
#endif |
|
#ifdef DIAGNOSTIC |
|
Static void ehci_dump_exfer(struct ehci_xfer *); |
Static void ehci_dump_exfer(struct ehci_xfer *); |
#endif |
#endif |
#endif |
|
|
|
#define EHCI_NULL htole32(EHCI_LINK_TERMINATE) |
#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, ex_next); |
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) |
|
|
|
Static const struct usbd_bus_methods ehci_bus_methods = { |
static inline void |
.open_pipe = ehci_open, |
ehci_del_intr_list(ehci_softc_t *sc, struct ehci_xfer *ex) |
.soft_intr = ehci_softintr, |
{ |
.do_poll = ehci_poll, |
|
.allocm = ehci_allocm, |
TAILQ_REMOVE(&sc->sc_intrhead, ex, ex_next); |
.freem = ehci_freem, |
} |
.allocx = ehci_allocx, |
|
.freex = ehci_freex, |
|
.get_lock = ehci_get_lock, |
|
.new_device = NULL, |
|
}; |
|
|
|
Static const struct usbd_pipe_methods ehci_root_ctrl_methods = { |
Static const struct usbd_bus_methods ehci_bus_methods = { |
.transfer = ehci_root_ctrl_transfer, |
.ubm_open = ehci_open, |
.start = ehci_root_ctrl_start, |
.ubm_softint = ehci_softintr, |
.abort = ehci_root_ctrl_abort, |
.ubm_dopoll = ehci_poll, |
.close = ehci_root_ctrl_close, |
.ubm_allocx = ehci_allocx, |
.cleartoggle = ehci_noop, |
.ubm_freex = ehci_freex, |
.done = ehci_root_ctrl_done, |
.ubm_getlock = ehci_get_lock, |
|
.ubm_rhctrl = ehci_roothub_ctrl, |
}; |
}; |
|
|
Static const struct usbd_pipe_methods ehci_root_intr_methods = { |
Static const struct usbd_pipe_methods ehci_root_intr_methods = { |
.transfer = ehci_root_intr_transfer, |
.upm_transfer = ehci_root_intr_transfer, |
.start = ehci_root_intr_start, |
.upm_start = ehci_root_intr_start, |
.abort = ehci_root_intr_abort, |
.upm_abort = ehci_root_intr_abort, |
.close = ehci_root_intr_close, |
.upm_close = ehci_root_intr_close, |
.cleartoggle = ehci_noop, |
.upm_cleartoggle = ehci_noop, |
.done = ehci_root_intr_done, |
.upm_done = ehci_root_intr_done, |
}; |
}; |
|
|
Static const struct usbd_pipe_methods ehci_device_ctrl_methods = { |
Static const struct usbd_pipe_methods ehci_device_ctrl_methods = { |
.transfer = ehci_device_ctrl_transfer, |
.upm_init = ehci_device_ctrl_init, |
.start = ehci_device_ctrl_start, |
.upm_fini = ehci_device_ctrl_fini, |
.abort = ehci_device_ctrl_abort, |
.upm_transfer = ehci_device_ctrl_transfer, |
.close = ehci_device_ctrl_close, |
.upm_start = ehci_device_ctrl_start, |
.cleartoggle = ehci_noop, |
.upm_abort = ehci_device_ctrl_abort, |
.done = ehci_device_ctrl_done, |
.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 = { |
Static const struct usbd_pipe_methods ehci_device_intr_methods = { |
.transfer = ehci_device_intr_transfer, |
.upm_init = ehci_device_intr_init, |
.start = ehci_device_intr_start, |
.upm_fini = ehci_device_intr_fini, |
.abort = ehci_device_intr_abort, |
.upm_transfer = ehci_device_intr_transfer, |
.close = ehci_device_intr_close, |
.upm_start = ehci_device_intr_start, |
.cleartoggle = ehci_device_clear_toggle, |
.upm_abort = ehci_device_intr_abort, |
.done = ehci_device_intr_done, |
.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 = { |
Static const struct usbd_pipe_methods ehci_device_bulk_methods = { |
.transfer = ehci_device_bulk_transfer, |
.upm_init = ehci_device_bulk_init, |
.start = ehci_device_bulk_start, |
.upm_fini = ehci_device_bulk_fini, |
.abort = ehci_device_bulk_abort, |
.upm_transfer = ehci_device_bulk_transfer, |
.close = ehci_device_bulk_close, |
.upm_start = ehci_device_bulk_start, |
.cleartoggle = ehci_device_clear_toggle, |
.upm_abort = ehci_device_bulk_abort, |
.done = ehci_device_bulk_done, |
.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 = { |
Static const struct usbd_pipe_methods ehci_device_isoc_methods = { |
.transfer = ehci_device_isoc_transfer, |
.upm_init = ehci_device_isoc_init, |
.start = ehci_device_isoc_start, |
.upm_fini = ehci_device_isoc_fini, |
.abort = ehci_device_isoc_abort, |
.upm_transfer = ehci_device_isoc_transfer, |
.close = ehci_device_isoc_close, |
.upm_abort = ehci_device_isoc_abort, |
.cleartoggle = ehci_noop, |
.upm_close = ehci_device_isoc_close, |
.done = ehci_device_isoc_done, |
.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] = { |
static const uint8_t revbits[EHCI_MAX_POLLRATE] = { |
Line 335 static const uint8_t revbits[EHCI_MAX_PO |
|
Line 396 static const uint8_t revbits[EHCI_MAX_PO |
|
0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f, |
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) |
ehci_init(ehci_softc_t *sc) |
{ |
{ |
u_int32_t vers, sparams, cparams, hcr; |
uint32_t vers, sparams, cparams, hcr; |
u_int i; |
u_int i; |
usbd_status err; |
usbd_status err; |
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
u_int ncomp; |
u_int ncomp; |
|
|
DPRINTF(("ehci_init: start\n")); |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
theehci = sc; |
theehci = sc; |
#endif |
#endif |
|
|
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); |
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, "ehcidb"); |
cv_init(&sc->sc_doorbell, "ehcidi"); |
|
|
|
sc->sc_xferpool = pool_cache_init(sizeof(struct ehci_xfer), 0, 0, 0, |
sc->sc_xferpool = pool_cache_init(sizeof(struct ehci_xfer), 0, 0, 0, |
"ehcixfer", NULL, IPL_USB, NULL, NULL, NULL); |
"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); |
ehci_doorbell, sc); |
KASSERT(sc->sc_doorbell_si != NULL); |
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); |
ehci_pcd, sc); |
KASSERT(sc->sc_pcd_si != NULL); |
KASSERT(sc->sc_pcd_si != NULL); |
|
|
Line 368 ehci_init(ehci_softc_t *sc) |
|
Line 428 ehci_init(ehci_softc_t *sc) |
|
|
|
vers = EREAD2(sc, EHCI_HCIVERSION); |
vers = EREAD2(sc, EHCI_HCIVERSION); |
aprint_verbose("%s: EHCI version %x.%x\n", device_xname(sc->sc_dev), |
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); |
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); |
sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); |
ncomp = EHCI_HCS_N_CC(sparams); |
ncomp = EHCI_HCS_N_CC(sparams); |
if (ncomp != sc->sc_ncomp) { |
if (ncomp != sc->sc_ncomp) { |
Line 395 ehci_init(ehci_softc_t *sc) |
|
Line 455 ehci_init(ehci_softc_t *sc) |
|
aprint_normal("\n"); |
aprint_normal("\n"); |
} |
} |
sc->sc_noport = EHCI_HCS_N_PORTS(sparams); |
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); |
sc->sc_hasppc = EHCI_HCS_PPC(sparams); |
|
|
|
cparams = EREAD4(sc, EHCI_HCCPARAMS); |
|
DPRINTF("cparams=%#x", cparams, 0, 0, 0); |
|
|
if (EHCI_HCC_64BIT(cparams)) { |
if (EHCI_HCC_64BIT(cparams)) { |
/* MUST clear segment register if 64 bit capable. */ |
/* 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, |
sc->sc_bus.ub_revision = USBREV_2_0; |
USB_MEM_RESERVE); |
sc->sc_bus.ub_usedma = true; |
|
sc->sc_bus.ub_dmaflags = USBMALLOC_MULTISEG; |
|
|
/* Reset the controller */ |
/* 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 */ |
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ |
usb_delay_ms(&sc->sc_bus, 1); |
usb_delay_ms(&sc->sc_bus, 1); |
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); |
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); |
Line 422 ehci_init(ehci_softc_t *sc) |
|
Line 488 ehci_init(ehci_softc_t *sc) |
|
} |
} |
if (hcr) { |
if (hcr) { |
aprint_error("%s: reset timeout\n", device_xname(sc->sc_dev)); |
aprint_error("%s: reset timeout\n", device_xname(sc->sc_dev)); |
return (USBD_IOERROR); |
return EIO; |
} |
} |
if (sc->sc_vendor_init) |
if (sc->sc_vendor_init) |
sc->sc_vendor_init(sc); |
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 */ |
/* XXX need proper intr scheduling */ |
sc->sc_rand = 96; |
sc->sc_rand = 96; |
|
|
Line 446 ehci_init(ehci_softc_t *sc) |
|
Line 501 ehci_init(ehci_softc_t *sc) |
|
case 0: sc->sc_flsize = 1024; break; |
case 0: sc->sc_flsize = 1024; break; |
case 1: sc->sc_flsize = 512; break; |
case 1: sc->sc_flsize = 512; break; |
case 2: sc->sc_flsize = 256; 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), |
err = usb_allocmem(&sc->sc_bus, sc->sc_flsize * sizeof(ehci_link_t), |
EHCI_FLALIGN_ALIGN, &sc->sc_fldma); |
EHCI_FLALIGN_ALIGN, &sc->sc_fldma); |
if (err) |
if (err) |
return (err); |
return err; |
DPRINTF(("%s: flsize=%d\n", device_xname(sc->sc_dev),sc->sc_flsize)); |
DPRINTF("flsize=%d", sc->sc_flsize, 0, 0, 0); |
sc->sc_flist = KERNADDR(&sc->sc_fldma, 0); |
sc->sc_flist = KERNADDR(&sc->sc_fldma, 0); |
|
|
for (i = 0; i < sc->sc_flsize; i++) { |
for (i = 0; i < sc->sc_flsize; i++) { |
Line 466 ehci_init(ehci_softc_t *sc) |
|
Line 521 ehci_init(ehci_softc_t *sc) |
|
if (sc->sc_softitds == NULL) |
if (sc->sc_softitds == NULL) |
return ENOMEM; |
return ENOMEM; |
LIST_INIT(&sc->sc_freeitds); |
LIST_INIT(&sc->sc_freeitds); |
|
LIST_INIT(&sc->sc_freesitds); |
TAILQ_INIT(&sc->sc_intrhead); |
TAILQ_INIT(&sc->sc_intrhead); |
|
|
/* Set up the bus struct. */ |
/* Set up the bus struct. */ |
sc->sc_bus.methods = &ehci_bus_methods; |
sc->sc_bus.ub_methods = &ehci_bus_methods; |
sc->sc_bus.pipe_size = sizeof(struct ehci_pipe); |
sc->sc_bus.ub_pipesize= sizeof(struct ehci_pipe); |
|
|
sc->sc_eintrs = EHCI_NORMAL_INTRS; |
sc->sc_eintrs = EHCI_NORMAL_INTRS; |
|
|
Line 481 ehci_init(ehci_softc_t *sc) |
|
Line 537 ehci_init(ehci_softc_t *sc) |
|
for (i = 0; i < EHCI_INTRQHS; i++) { |
for (i = 0; i < EHCI_INTRQHS; i++) { |
sqh = ehci_alloc_sqh(sc); |
sqh = ehci_alloc_sqh(sc); |
if (sqh == NULL) { |
if (sqh == NULL) { |
err = USBD_NOMEM; |
err = ENOMEM; |
goto bad1; |
goto bad1; |
} |
} |
sc->sc_islots[i].sqh = sqh; |
sc->sc_islots[i].sqh = sqh; |
Line 499 ehci_init(ehci_softc_t *sc) |
|
Line 555 ehci_init(ehci_softc_t *sc) |
|
EHCI_LINK_QH); |
EHCI_LINK_QH); |
} |
} |
sqh->qh.qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); |
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->qh.qh_curqtd = EHCI_NULL; |
sqh->next = NULL; |
|
sqh->qh.qh_qtd.qtd_next = EHCI_NULL; |
sqh->qh.qh_qtd.qtd_next = EHCI_NULL; |
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; |
sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); |
sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); |
Line 524 ehci_init(ehci_softc_t *sc) |
|
Line 580 ehci_init(ehci_softc_t *sc) |
|
/* Allocate dummy QH that starts the async list. */ |
/* Allocate dummy QH that starts the async list. */ |
sqh = ehci_alloc_sqh(sc); |
sqh = ehci_alloc_sqh(sc); |
if (sqh == NULL) { |
if (sqh == NULL) { |
err = USBD_NOMEM; |
err = ENOMEM; |
goto bad1; |
goto bad1; |
} |
} |
/* Fill the QH */ |
/* Fill the QH */ |
Line 542 ehci_init(ehci_softc_t *sc) |
|
Line 598 ehci_init(ehci_softc_t *sc) |
|
usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug) { |
DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
ehci_dump_sqh(sqh); |
ehci_dump_sqh(sqh); |
} |
DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
#endif |
#endif |
|
|
/* Point to async list */ |
/* Point to async list */ |
Line 572 ehci_init(ehci_softc_t *sc) |
|
Line 628 ehci_init(ehci_softc_t *sc) |
|
} |
} |
if (hcr) { |
if (hcr) { |
aprint_error("%s: run timeout\n", device_xname(sc->sc_dev)); |
aprint_error("%s: run timeout\n", device_xname(sc->sc_dev)); |
return (USBD_IOERROR); |
return EIO; |
} |
} |
|
|
/* Enable interrupts */ |
/* Enable interrupts */ |
DPRINTFN(1,("ehci_init: enabling\n")); |
DPRINTF("enabling interupts", 0, 0, 0, 0); |
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); |
|
|
return (USBD_NORMAL_COMPLETION); |
return 0; |
|
|
#if 0 |
#if 0 |
bad2: |
bad2: |
Line 587 ehci_init(ehci_softc_t *sc) |
|
Line 643 ehci_init(ehci_softc_t *sc) |
|
#endif |
#endif |
bad1: |
bad1: |
usb_freemem(&sc->sc_bus, &sc->sc_fldma); |
usb_freemem(&sc->sc_bus, &sc->sc_fldma); |
return (err); |
return err; |
} |
} |
|
|
int |
int |
Line 596 ehci_intr(void *v) |
|
Line 652 ehci_intr(void *v) |
|
ehci_softc_t *sc = v; |
ehci_softc_t *sc = v; |
int ret = 0; |
int ret = 0; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
if (sc == NULL) |
if (sc == NULL) |
return 0; |
return 0; |
|
|
Line 605 ehci_intr(void *v) |
|
Line 663 ehci_intr(void *v) |
|
goto done; |
goto done; |
|
|
/* If we get an interrupt while polling, then just ignore it. */ |
/* If we get an interrupt while polling, then just ignore it. */ |
if (sc->sc_bus.use_polling) { |
if (sc->sc_bus.ub_usepolling) { |
u_int32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
uint32_t intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
|
|
if (intrs) |
if (intrs) |
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
#ifdef DIAGNOSTIC |
DPRINTFN(16, "ignored interrupt while polling", 0, 0, 0, 0); |
DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n")); |
|
#endif |
|
goto done; |
goto done; |
} |
} |
|
|
|
|
Static int |
Static int |
ehci_intr1(ehci_softc_t *sc) |
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. */ |
/* In case the interrupt occurs before initialization has completed. */ |
if (sc == NULL) { |
if (sc == NULL) { |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
printf("ehci_intr1: sc == NULL\n"); |
printf("ehci_intr1: sc == NULL\n"); |
#endif |
#endif |
return (0); |
return 0; |
} |
} |
|
|
KASSERT(mutex_owned(&sc->sc_intr_lock)); |
KASSERT(mutex_owned(&sc->sc_intr_lock)); |
|
|
intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
if (!intrs) |
if (!intrs) |
return (0); |
return 0; |
|
|
eintrs = intrs & sc->sc_eintrs; |
eintrs = intrs & sc->sc_eintrs; |
DPRINTFN(7, ("ehci_intr1: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", |
DPRINTF("sc=%p intrs=%#x(%#x) eintrs=%#x", sc, intrs, |
sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), |
EOREAD4(sc, EHCI_USBSTS), eintrs); |
(u_int)eintrs)); |
|
if (!eintrs) |
if (!eintrs) |
return (0); |
return 0; |
|
|
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ |
sc->sc_bus.no_intrs++; |
|
if (eintrs & EHCI_STS_IAA) { |
if (eintrs & EHCI_STS_IAA) { |
DPRINTF(("ehci_intr1: door bell\n")); |
DPRINTF("door bell", 0, 0, 0, 0); |
kpreempt_disable(); |
kpreempt_disable(); |
KASSERT(sc->sc_doorbell_si != NULL); |
KASSERT(sc->sc_doorbell_si != NULL); |
softint_schedule(sc->sc_doorbell_si); |
softint_schedule(sc->sc_doorbell_si); |
Line 662 ehci_intr1(ehci_softc_t *sc) |
|
Line 716 ehci_intr1(ehci_softc_t *sc) |
|
eintrs &= ~EHCI_STS_IAA; |
eintrs &= ~EHCI_STS_IAA; |
} |
} |
if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { |
if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { |
DPRINTFN(5,("ehci_intr1: %s %s\n", |
DPRINTF("INT=%d ERRINT=%d", |
eintrs & EHCI_STS_INT ? "INT" : "", |
eintrs & EHCI_STS_INT ? 1 : 0, |
eintrs & EHCI_STS_ERRINT ? "ERRINT" : "")); |
eintrs & EHCI_STS_ERRINT ? 1 : 0, 0, 0); |
usb_schedsoftintr(&sc->sc_bus); |
usb_schedsoftintr(&sc->sc_bus); |
eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); |
eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); |
} |
} |
Line 689 ehci_intr1(ehci_softc_t *sc) |
|
Line 743 ehci_intr1(ehci_softc_t *sc) |
|
device_xname(sc->sc_dev), eintrs); |
device_xname(sc->sc_dev), eintrs); |
} |
} |
|
|
return (1); |
return 1; |
} |
} |
|
|
Static void |
Static void |
ehci_doorbell(void *addr) |
ehci_doorbell(void *addr) |
{ |
{ |
ehci_softc_t *sc = addr; |
ehci_softc_t *sc = addr; |
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
cv_broadcast(&sc->sc_doorbell); |
cv_broadcast(&sc->sc_doorbell); |
|
|
ehci_pcd(void *addr) |
ehci_pcd(void *addr) |
{ |
{ |
ehci_softc_t *sc = addr; |
ehci_softc_t *sc = addr; |
usbd_xfer_handle xfer; |
struct usbd_xfer *xfer; |
u_char *p; |
u_char *p; |
int i, m; |
int i, m; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
xfer = sc->sc_intrxfer; |
xfer = sc->sc_intrxfer; |
|
|
Line 718 ehci_pcd(void *addr) |
|
Line 775 ehci_pcd(void *addr) |
|
goto done; |
goto done; |
} |
} |
|
|
p = KERNADDR(&xfer->dmabuf, 0); |
p = xfer->ux_buf; |
m = min(sc->sc_noport, xfer->length * 8 - 1); |
m = min(sc->sc_noport, xfer->ux_length * 8 - 1); |
memset(p, 0, xfer->length); |
memset(p, 0, xfer->ux_length); |
for (i = 1; i <= m; i++) { |
for (i = 1; i <= m; i++) { |
/* Pick out CHANGE bits from the status reg. */ |
/* Pick out CHANGE bits from the status reg. */ |
if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) |
if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) |
p[i/8] |= 1 << (i%8); |
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->ux_actlen = xfer->ux_length; |
xfer->actlen = xfer->length; |
xfer->ux_status = USBD_NORMAL_COMPLETION; |
xfer->status = USBD_NORMAL_COMPLETION; |
|
|
|
usb_transfer_complete(xfer); |
usb_transfer_complete(xfer); |
|
|
|
|
ehci_softintr(void *v) |
ehci_softintr(void *v) |
{ |
{ |
struct usbd_bus *bus = 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; |
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 |
* The only explanation I can think of for why EHCI is as brain dead |
Line 753 ehci_softintr(void *v) |
|
Line 814 ehci_softintr(void *v) |
|
* An interrupt just tells us that something is done, we have no |
* An interrupt just tells us that something is done, we have no |
* clue what, so we need to scan through all active transfers. :-( |
* 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. */ |
/* Schedule a callout to catch any dropped transactions. */ |
Line 763 ehci_softintr(void *v) |
|
Line 853 ehci_softintr(void *v) |
|
!TAILQ_EMPTY(&sc->sc_intrhead)) |
!TAILQ_EMPTY(&sc->sc_intrhead)) |
callout_reset(&sc->sc_tmo_intrlist, |
callout_reset(&sc->sc_tmo_intrlist, |
hz, ehci_intrlist_timeout, sc); |
hz, ehci_intrlist_timeout, sc); |
|
|
if (sc->sc_softwake) { |
|
sc->sc_softwake = 0; |
|
cv_broadcast(&sc->sc_softwake_cv); |
|
} |
|
} |
} |
|
|
/* Check for an interrupt. */ |
|
Static void |
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; |
ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; |
|
uint32_t status; |
DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex)); |
|
|
|
KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); |
|
|
|
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 |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
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) { |
if (ex->ex_type == EX_CTRL) { |
printf("ehci_check_qh_intr: not valid sqtd\n"); |
fsqtd = ex->ex_setup; |
return; |
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 |
* 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 |
* is an error somewhere in the middle, or whether there was a |
Line 823 ehci_check_qh_intr(ehci_softc_t *sc, str |
|
Line 889 ehci_check_qh_intr(ehci_softc_t *sc, str |
|
lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
lsqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); |
sizeof(lsqtd->qtd.qtd_status), BUS_DMASYNC_PREREAD); |
if (status & EHCI_QTD_ACTIVE) { |
if (status & EHCI_QTD_ACTIVE) { |
DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex)); |
DPRINTFN(10, "active ex=%p", ex, 0, 0, 0); |
for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) { |
|
|
/* last qTD has already been checked */ |
|
for (sqtd = fsqtd; sqtd != lsqtd; sqtd = sqtd->nextqtd) { |
usb_syncmem(&sqtd->dma, |
usb_syncmem(&sqtd->dma, |
sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sizeof(sqtd->qtd.qtd_status), |
sizeof(sqtd->qtd.qtd_status), |
Line 841 ehci_check_qh_intr(ehci_softc_t *sc, str |
|
Line 909 ehci_check_qh_intr(ehci_softc_t *sc, str |
|
goto done; |
goto done; |
/* Handle short packets */ |
/* Handle short packets */ |
if (EHCI_QTD_GET_BYTES(status) != 0) { |
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 |
* If we get here for a control transfer then |
* we need to let the hardware complete the |
* we need to let the hardware complete the |
Line 854 ehci_check_qh_intr(ehci_softc_t *sc, str |
|
Line 917 ehci_check_qh_intr(ehci_softc_t *sc, str |
|
* |
* |
* Otherwise, we're done. |
* Otherwise, we're done. |
*/ |
*/ |
if (xt == UE_CONTROL) { |
if (ex->ex_type == EX_CTRL) { |
break; |
break; |
} |
} |
goto done; |
goto done; |
} |
} |
} |
} |
DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", |
DPRINTFN(10, "ex=%p std=%p still active", ex, ex->ex_sqtdstart, |
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; |
return; |
} |
} |
done: |
done: |
DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex)); |
DPRINTFN(10, "ex=%p done", ex, 0, 0, 0); |
callout_stop(&ex->xfer.timeout_handle); |
ehci_idone(ex, cq); |
ehci_idone(ex); |
|
} |
} |
|
|
Static void |
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; |
ehci_soft_itd_t *itd; |
int i; |
int i; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
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; |
return; |
|
|
if (ex->itdstart == NULL) { |
KASSERTMSG(ex->ex_itdstart != NULL && ex->ex_itdend != NULL, |
printf("ehci_check_itd_intr: not valid itd\n"); |
"xfer %p fitd %p litd %p", ex, ex->ex_itdstart, ex->ex_itdend); |
return; |
|
} |
|
|
|
itd = ex->itdend; |
itd = ex->ex_itdend; |
#ifdef DIAGNOSTIC |
|
if (itd == NULL) { |
|
printf("ehci_check_itd_intr: itdend == 0\n"); |
|
return; |
|
} |
|
#endif |
|
|
|
/* |
/* |
* check no active transfers in last itd, meaning we're finished |
* check no active transfers in last itd, meaning we're finished |
*/ |
*/ |
|
|
usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), |
usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), |
sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE | |
sizeof(itd->itd.itd_ctl), |
BUS_DMASYNC_POSTREAD); |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
|
|
for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { |
for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { |
if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE) |
if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE) |
Line 911 ehci_check_itd_intr(ehci_softc_t *sc, st |
|
Line 972 ehci_check_itd_intr(ehci_softc_t *sc, st |
|
goto done; /* All 8 descriptors inactive, it's done */ |
goto done; /* All 8 descriptors inactive, it's done */ |
} |
} |
|
|
DPRINTFN(12, ("ehci_check_itd_intr: ex %p itd %p still active\n", ex, |
usb_syncmem(&itd->dma, itd->offs + offsetof(ehci_itd_t, itd_ctl), |
ex->itdstart)); |
sizeof(itd->itd.itd_ctl), BUS_DMASYNC_PREREAD); |
|
|
|
DPRINTFN(10, "ex %p itd %p still active", ex, ex->ex_itdstart, 0, 0); |
return; |
return; |
done: |
done: |
DPRINTFN(12, ("ehci_check_itd_intr: ex=%p done\n", ex)); |
DPRINTF("ex %p done", ex, 0, 0, 0); |
callout_stop(&ex->xfer.timeout_handle); |
ehci_idone(ex, cq); |
ehci_idone(ex); |
|
} |
} |
|
|
Static void |
void |
ehci_idone(struct ehci_xfer *ex) |
ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex, ex_completeq_t *cq) |
{ |
{ |
usbd_xfer_handle xfer = &ex->xfer; |
ehci_soft_sitd_t *sitd; |
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; |
|
|
|
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->ex_xfer != SIMPLEQ_FIRST(&ex->ex_xfer.ux_pipe->up_queue)) |
if (ex->isdone) { |
|
printf("ehci_idone: ex=%p is done!\n", ex); |
|
#ifdef EHCI_DEBUG |
|
ehci_dump_exfer(ex); |
|
#endif |
|
return; |
return; |
} |
|
ex->isdone = 1; |
|
#endif |
|
|
|
if (xfer->status == USBD_CANCELLED || |
KASSERTMSG(ex->ex_sitdstart != NULL && ex->ex_sitdend != NULL, |
xfer->status == USBD_TIMEOUT) { |
"xfer %p fsitd %p lsitd %p", ex, ex->ex_sitdstart, ex->ex_sitdend); |
DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer)); |
|
|
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; |
|
|
|
DPRINTFN(10, "ex=%p done", ex, 0, 0, 0); |
|
ehci_idone(ex, cq); |
|
} |
|
|
|
|
|
Static void |
|
ehci_idone(struct ehci_xfer *ex, ex_completeq_t *cq) |
|
{ |
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
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; |
|
|
|
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); |
|
|
|
DPRINTF("ex=%p", ex, 0, 0, 0); |
|
|
|
/* |
|
* If software has completed it, either by cancellation |
|
* or timeout, drop it on the floor. |
|
*/ |
|
if (xfer->ux_status != USBD_IN_PROGRESS) { |
|
KASSERT(xfer->ux_status == USBD_CANCELLED || |
|
xfer->ux_status == USBD_TIMEOUT); |
|
DPRINTF("aborted xfer=%p", xfer, 0, 0, 0); |
return; |
return; |
} |
} |
|
|
|
/* |
|
* Cancel the timeout and the task, which have not yet |
|
* run. If they have already fired, at worst they are |
|
* waiting for the lock. They will see that the xfer |
|
* is no longer in progress and give up. |
|
*/ |
|
callout_stop(&xfer->ux_callout); |
|
usb_rem_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask); |
|
|
|
#ifdef DIAGNOSTIC |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe)); |
if (ex->ex_isdone) { |
if (ehcidebug > 10) |
DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
ehci_dump_sqtds(ex->sqtdstart); |
ehci_dump_exfer(ex); |
|
DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
|
} |
#endif |
#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. */ |
/* 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; |
struct ehci_soft_itd *itd; |
int i, nframes, len, uframes; |
int i, nframes, len, uframes; |
|
|
nframes = 0; |
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); |
uframes = min(1 << (i - 1), USB_UFRAMES_PER_FRAME); |
|
|
for (itd = ex->itdstart; itd != NULL; itd = itd->xfer_next) { |
for (itd = ex->ex_itdstart; itd != NULL; itd = itd->xfer_next) { |
usb_syncmem(&itd->dma,itd->offs + offsetof(ehci_itd_t,itd_ctl), |
usb_syncmem(&itd->dma, |
sizeof(itd->itd.itd_ctl), BUS_DMASYNC_POSTWRITE | |
itd->offs + offsetof(ehci_itd_t,itd_ctl), |
BUS_DMASYNC_POSTREAD); |
sizeof(itd->itd.itd_ctl), |
|
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
|
|
for (i = 0; i < EHCI_ITD_NUFRAMES; i += uframes) { |
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 |
* of uframes. This leads to scheduling |
* inefficiencies, but working around |
* inefficiencies, but working around |
* this doubles complexity of tracking |
* this doubles complexity of tracking |
* an xfer. |
* an xfer. |
*/ |
*/ |
if (nframes >= xfer->nframes) |
if (nframes >= xfer->ux_nframes) |
break; |
break; |
|
|
status = le32toh(itd->itd.itd_ctl[i]); |
status = le32toh(itd->itd.itd_ctl[i]); |
Line 991 ehci_idone(struct ehci_xfer *ex) |
|
Line 1110 ehci_idone(struct ehci_xfer *ex) |
|
if (EHCI_ITD_GET_STATUS(status) != 0) |
if (EHCI_ITD_GET_STATUS(status) != 0) |
len = 0; /*No valid data on error*/ |
len = 0; /*No valid data on error*/ |
|
|
xfer->frlengths[nframes++] = len; |
xfer->ux_frlengths[nframes++] = len; |
actlen += 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]; |
|
} |
|
|
|
/* |
|
* 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->nframes) |
if (nframes >= xfer->ux_nframes) |
break; |
break; |
} |
} |
|
|
xfer->actlen = actlen; |
xfer->ux_actlen = actlen; |
xfer->status = USBD_NORMAL_COMPLETION; |
xfer->ux_status = USBD_NORMAL_COMPLETION; |
goto end; |
goto end; |
} |
} |
|
KASSERT(ex->ex_type == EX_CTRL || ex->ex_type == EX_INTR || |
|
ex->ex_type == EX_BULK); |
|
|
/* Continue processing xfers using queue heads */ |
/* 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; |
for (sqtd = fsqtd; sqtd != lsqtd->nextqtd; sqtd = sqtd->nextqtd) { |
actlen = 0; |
|
for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd = sqtd->nextqtd) { |
|
usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), |
usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
nstatus = le32toh(sqtd->qtd.qtd_status); |
nstatus = le32toh(sqtd->qtd.qtd_status); |
|
usb_syncmem(&sqtd->dma, sqtd->offs, sizeof(sqtd->qtd), |
|
BUS_DMASYNC_PREREAD); |
if (nstatus & EHCI_QTD_ACTIVE) |
if (nstatus & EHCI_QTD_ACTIVE) |
break; |
break; |
|
|
Line 1020 ehci_idone(struct ehci_xfer *ex) |
|
Line 1207 ehci_idone(struct ehci_xfer *ex) |
|
actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); |
actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); |
} |
} |
|
|
|
|
/* |
/* |
* If there are left over TDs we need to update the toggle. |
* If there are left over TDs we need to update the toggle. |
* The default pipe doesn't need it since control transfers |
* The default pipe doesn't need it since control transfers |
Line 1029 ehci_idone(struct ehci_xfer *ex) |
|
Line 1215 ehci_idone(struct ehci_xfer *ex) |
|
* packets within the qTD. |
* packets within the qTD. |
*/ |
*/ |
if ((sqtd != lsqtd->nextqtd || EHCI_QTD_GET_BYTES(status)) && |
if ((sqtd != lsqtd->nextqtd || EHCI_QTD_GET_BYTES(status)) && |
xfer->pipe->device->default_pipe != xfer->pipe) { |
xfer->ux_pipe->up_dev->ud_pipe0 != xfer->ux_pipe) { |
DPRINTFN(2, ("ehci_idone: need toggle update " |
DPRINTF("toggle update status=0x%08x nstatus=0x%08x", |
"status=%08x nstatus=%08x\n", status, nstatus)); |
status, nstatus, 0, 0); |
#if 0 |
#if 0 |
ehci_dump_sqh(epipe->sqh); |
ehci_dump_sqh(epipe->sqh); |
ehci_dump_sqtds(ex->sqtdstart); |
ehci_dump_sqtds(ex->ex_sqtdstart); |
#endif |
#endif |
epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); |
epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); |
} |
} |
|
|
DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", |
DPRINTF("len=%d actlen=%d status=0x%08x", xfer->ux_length, actlen, |
xfer->length, actlen, status)); |
status, 0); |
xfer->actlen = actlen; |
xfer->ux_actlen = actlen; |
if (status & EHCI_QTD_HALTED) { |
if (status & EHCI_QTD_HALTED) { |
#ifdef EHCI_DEBUG |
#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), |
DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
"\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE" |
ehci_dump_sqh(epipe->sqh); |
"\4XACTERR\3MISSED\2SPLIT\1PING", |
ehci_dump_sqtds(ex->ex_sqtdstart); |
(u_int32_t)status); |
DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
|
|
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); |
|
} |
|
#endif |
#endif |
/* low&full speed has an extra error flag */ |
/* low&full speed has an extra error flag */ |
if (EHCI_QH_GET_EPS(epipe->sqh->qh.qh_endp) != |
if (EHCI_QH_GET_EPS(epipe->sqh->qh.qh_endp) != |
Line 1069 ehci_idone(struct ehci_xfer *ex) |
|
Line 1261 ehci_idone(struct ehci_xfer *ex) |
|
else |
else |
status &= EHCI_QTD_STATERRS; |
status &= EHCI_QTD_STATERRS; |
if (status == 0) /* no other errors means a stall */ { |
if (status == 0) /* no other errors means a stall */ { |
xfer->status = USBD_STALLED; |
xfer->ux_status = USBD_STALLED; |
} else { |
} else { |
xfer->status = USBD_IOERROR; /* more info XXX */ |
xfer->ux_status = USBD_IOERROR; /* more info XXX */ |
} |
} |
/* XXX need to reset TT on missed microframe */ |
/* XXX need to reset TT on missed microframe */ |
if (status & EHCI_QTD_MISSEDMICRO) { |
if (status & EHCI_QTD_MISSEDMICRO) { |
Line 1080 ehci_idone(struct ehci_xfer *ex) |
|
Line 1272 ehci_idone(struct ehci_xfer *ex) |
|
device_xname(sc->sc_dev)); |
device_xname(sc->sc_dev)); |
} |
} |
} else { |
} else { |
xfer->status = USBD_NORMAL_COMPLETION; |
xfer->ux_status = USBD_NORMAL_COMPLETION; |
} |
} |
|
|
end: |
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; |
ehci_del_intr_list(sc, ex); |
for (timo = xfer->timeout; timo >= 0; timo--) { |
TAILQ_INSERT_TAIL(cq, ex, ex_next); |
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; |
|
} |
|
} |
|
|
|
/* Timeout */ |
DPRINTF("ex=%p done", ex, 0, 0, 0); |
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 */ |
|
} |
} |
|
|
Static void |
Static void |
ehci_poll(struct usbd_bus *bus) |
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 |
#ifdef EHCI_DEBUG |
static int last; |
static int last; |
int new; |
int new; |
new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); |
if (new != last) { |
if (new != last) { |
DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new)); |
DPRINTF("intrs=0x%04x", new, 0, 0, 0); |
last = new; |
last = new; |
} |
} |
#endif |
#endif |
Line 1167 ehci_detach(struct ehci_softc *sc, int f |
|
Line 1321 ehci_detach(struct ehci_softc *sc, int f |
|
{ |
{ |
int rv = 0; |
int rv = 0; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
if (sc->sc_child != NULL) |
if (sc->sc_child != NULL) |
rv = config_detach(sc->sc_child, flags); |
rv = config_detach(sc->sc_child, flags); |
|
|
if (rv != 0) |
if (rv != 0) |
return (rv); |
return rv; |
|
|
callout_halt(&sc->sc_tmo_intrlist, NULL); |
callout_halt(&sc->sc_tmo_intrlist, NULL); |
callout_destroy(&sc->sc_tmo_intrlist); |
callout_destroy(&sc->sc_tmo_intrlist); |
Line 1181 ehci_detach(struct ehci_softc *sc, int f |
|
Line 1337 ehci_detach(struct ehci_softc *sc, int f |
|
kmem_free(sc->sc_softitds, |
kmem_free(sc->sc_softitds, |
sc->sc_flsize * sizeof(ehci_soft_itd_t *)); |
sc->sc_flsize * sizeof(ehci_soft_itd_t *)); |
cv_destroy(&sc->sc_doorbell); |
cv_destroy(&sc->sc_doorbell); |
cv_destroy(&sc->sc_softwake_cv); |
|
|
|
#if 0 |
#if 0 |
/* XXX destroyed in ehci_pci.c as it controls ehci_intr access */ |
/* XXX destroyed in ehci_pci.c as it controls ehci_intr access */ |
Line 1197 ehci_detach(struct ehci_softc *sc, int f |
|
Line 1352 ehci_detach(struct ehci_softc *sc, int f |
|
|
|
EOWRITE4(sc, EHCI_CONFIGFLAG, 0); |
EOWRITE4(sc, EHCI_CONFIGFLAG, 0); |
|
|
return (rv); |
return rv; |
} |
} |
|
|
|
|
Line 1232 ehci_suspend(device_t dv, const pmf_qual |
|
Line 1387 ehci_suspend(device_t dv, const pmf_qual |
|
int i; |
int i; |
uint32_t cmd, hcr; |
uint32_t cmd, hcr; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
mutex_spin_enter(&sc->sc_intr_lock); |
mutex_spin_enter(&sc->sc_intr_lock); |
sc->sc_bus.use_polling++; |
sc->sc_bus.ub_usepolling++; |
mutex_spin_exit(&sc->sc_intr_lock); |
mutex_spin_exit(&sc->sc_intr_lock); |
|
|
for (i = 1; i <= sc->sc_noport; i++) { |
for (i = 1; i <= sc->sc_noport; i++) { |
Line 1271 ehci_suspend(device_t dv, const pmf_qual |
|
Line 1428 ehci_suspend(device_t dv, const pmf_qual |
|
printf("%s: config timeout\n", device_xname(dv)); |
printf("%s: config timeout\n", device_xname(dv)); |
|
|
mutex_spin_enter(&sc->sc_intr_lock); |
mutex_spin_enter(&sc->sc_intr_lock); |
sc->sc_bus.use_polling--; |
sc->sc_bus.ub_usepolling--; |
mutex_spin_exit(&sc->sc_intr_lock); |
mutex_spin_exit(&sc->sc_intr_lock); |
|
|
return true; |
return true; |
Line 1284 ehci_resume(device_t dv, const pmf_qual_ |
|
Line 1441 ehci_resume(device_t dv, const pmf_qual_ |
|
int i; |
int i; |
uint32_t cmd, hcr; |
uint32_t cmd, hcr; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
/* restore things in case the bios sucks */ |
/* restore things in case the bios sucks */ |
EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); |
EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); |
EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); |
EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0)); |
Line 1340 ehci_shutdown(device_t self, int flags) |
|
Line 1499 ehci_shutdown(device_t self, int flags) |
|
{ |
{ |
ehci_softc_t *sc = device_private(self); |
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, 0); /* Halt controller */ |
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); |
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); |
return true; |
return true; |
} |
} |
|
|
Static usbd_status |
Static struct usbd_xfer * |
ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) |
ehci_allocx(struct usbd_bus *bus, unsigned int nframes) |
{ |
|
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) |
|
{ |
{ |
struct ehci_softc *sc = bus->hci_private; |
struct ehci_softc *sc = EHCI_BUS2SC(bus); |
usbd_xfer_handle xfer; |
struct usbd_xfer *xfer; |
|
|
xfer = pool_cache_get(sc->sc_xferpool, PR_NOWAIT); |
xfer = pool_cache_get(sc->sc_xferpool, PR_WAITOK); |
if (xfer != NULL) { |
if (xfer != NULL) { |
memset(xfer, 0, sizeof(struct ehci_xfer)); |
memset(xfer, 0, sizeof(struct ehci_xfer)); |
|
|
|
/* Initialise this always so we can call remove on it. */ |
|
usb_init_task(&xfer->ux_aborttask, ehci_timeout_task, xfer, |
|
USB_TASKQ_MPSAFE); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
EXFER(xfer)->isdone = 1; |
struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); |
xfer->busy_free = XFER_BUSY; |
ex->ex_isdone = true; |
|
xfer->ux_state = XFER_BUSY; |
#endif |
#endif |
} |
} |
return (xfer); |
return xfer; |
} |
} |
|
|
Static void |
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 |
#ifdef DIAGNOSTIC |
if (xfer->busy_free != XFER_BUSY) { |
xfer->ux_state = XFER_FREE; |
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"); |
|
} |
|
#endif |
#endif |
|
|
pool_cache_put(sc->sc_xferpool, xfer); |
pool_cache_put(sc->sc_xferpool, xfer); |
} |
} |
|
|
Static void |
Static void |
ehci_get_lock(struct usbd_bus *bus, kmutex_t **lock) |
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; |
*lock = &sc->sc_lock; |
} |
} |
|
|
Static void |
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); |
|
|
DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n", |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
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 |
#ifdef EHCI_DEBUG |
if (ehcidebug) |
if (ehcidebug) |
usbd_dump_pipe(pipe); |
usbd_dump_pipe(pipe); |
Line 1439 ehci_device_clear_toggle(usbd_pipe_handl |
|
Line 1570 ehci_device_clear_toggle(usbd_pipe_handl |
|
} |
} |
|
|
Static void |
Static void |
ehci_noop(usbd_pipe_handle pipe) |
ehci_noop(struct usbd_pipe *pipe) |
{ |
{ |
} |
} |
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
Static void |
/* |
ehci_dump_regs(ehci_softc_t *sc) |
* Unused function - this is meant to be called from a kernel |
|
* debugger. |
|
*/ |
|
void |
|
ehci_dump(void) |
{ |
{ |
|
ehci_softc_t *sc = theehci; |
int i; |
int i; |
printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n", |
printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n", |
EOREAD4(sc, EHCI_USBCMD), |
EOREAD4(sc, EHCI_USBCMD), |
EOREAD4(sc, EHCI_USBSTS), |
EOREAD4(sc, EHCI_USBSTS), |
EOREAD4(sc, EHCI_USBINTR)); |
EOREAD4(sc, EHCI_USBINTR)); |
printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", |
printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", |
EOREAD4(sc, EHCI_FRINDEX), |
EOREAD4(sc, EHCI_FRINDEX), |
EOREAD4(sc, EHCI_CTRLDSSEGMENT), |
EOREAD4(sc, EHCI_CTRLDSSEGMENT), |
EOREAD4(sc, EHCI_PERIODICLISTBASE), |
EOREAD4(sc, EHCI_PERIODICLISTBASE), |
EOREAD4(sc, EHCI_ASYNCLISTADDR)); |
EOREAD4(sc, EHCI_ASYNCLISTADDR)); |
for (i = 1; i <= sc->sc_noport; i++) |
for (i = 1; i <= sc->sc_noport; i++) |
printf("port %d status=0x%08x\n", i, |
printf("port %d status=0x%08x\n", i, |
EOREAD4(sc, EHCI_PORTSC(i))); |
EOREAD4(sc, EHCI_PORTSC(i))); |
} |
|
|
|
/* |
|
* Unused function - this is meant to be called from a kernel |
|
* debugger. |
|
*/ |
|
void |
|
ehci_dump(void) |
|
{ |
|
ehci_dump_regs(theehci); |
|
} |
} |
|
|
Static void |
Static void |
ehci_dump_link(ehci_link_t link, int type) |
ehci_dump_regs(ehci_softc_t *sc) |
{ |
{ |
link = le32toh(link); |
int i; |
printf("0x%08x", link); |
|
if (link & EHCI_LINK_TERMINATE) |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
printf("<T>"); |
|
else { |
DPRINTF("cmd = 0x%08x sts = 0x%08x ien = 0x%08x", |
printf("<"); |
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), |
if (type) { |
EOREAD4(sc, EHCI_USBINTR), 0); |
switch (EHCI_LINK_TYPE(link)) { |
DPRINTF("frindex = 0x%08x ctrdsegm = 0x%08x periodic = 0x%08x " |
case EHCI_LINK_ITD: printf("ITD"); break; |
"async = 0x%08x", |
case EHCI_LINK_QH: printf("QH"); break; |
EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), |
case EHCI_LINK_SITD: printf("SITD"); break; |
EOREAD4(sc, EHCI_PERIODICLISTBASE), |
case EHCI_LINK_FSTN: printf("FSTN"); break; |
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))); |
} |
} |
printf(">"); |
|
} |
} |
} |
} |
|
|
|
#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 |
Static void |
ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
{ |
{ |
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
int i; |
int i; |
u_int32_t stop; |
uint32_t stop = 0; |
|
|
stop = 0; |
|
for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { |
for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { |
ehci_dump_sqtd(sqtd); |
ehci_dump_sqtd(sqtd); |
usb_syncmem(&sqtd->dma, |
usb_syncmem(&sqtd->dma, |
Line 1511 ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
|
Line 1658 ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) |
|
sqtd->offs + offsetof(ehci_qtd_t, qtd_next), |
sqtd->offs + offsetof(ehci_qtd_t, qtd_next), |
sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); |
sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); |
} |
} |
if (sqtd) |
if (!stop) |
printf("dump aborted, too many TDs\n"); |
DPRINTF("dump aborted, too many TDs", 0, 0, 0, 0); |
} |
} |
|
|
Static void |
Static void |
ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) |
ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) |
{ |
{ |
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
usb_syncmem(&sqtd->dma, sqtd->offs, |
usb_syncmem(&sqtd->dma, sqtd->offs, |
sizeof(sqtd->qtd), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
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); |
ehci_dump_qtd(&sqtd->qtd); |
|
|
usb_syncmem(&sqtd->dma, sqtd->offs, |
usb_syncmem(&sqtd->dma, sqtd->offs, |
sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); |
sizeof(sqtd->qtd), BUS_DMASYNC_PREREAD); |
} |
} |
Line 1529 ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) |
|
Line 1680 ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) |
|
Static void |
Static void |
ehci_dump_qtd(ehci_qtd_t *qtd) |
ehci_dump_qtd(ehci_qtd_t *qtd) |
{ |
{ |
u_int32_t s; |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
char sbuf[128]; |
uint32_t s = le32toh(qtd->qtd_status); |
|
|
printf(" next="); ehci_dump_link(qtd->qtd_next, 0); |
DPRINTFN(10, |
printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0); |
" next = 0x%08x altnext = 0x%08x status = 0x%08x", |
printf("\n"); |
qtd->qtd_next, qtd->qtd_altnext, s, 0); |
s = le32toh(qtd->qtd_status); |
DPRINTFN(10, |
snprintb(sbuf, sizeof(sbuf), |
" toggle = %d ioc = %d bytes = %#x " |
"\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR" |
"c_page = %#x", EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_IOC(s), |
"\3MISSED\2SPLIT\1PING", EHCI_QTD_GET_STATUS(s)); |
EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_C_PAGE(s)); |
printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", |
DPRINTFN(10, |
s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), |
" cerr = %d pid = %d stat = %x", |
EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); |
EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), EHCI_QTD_GET_STATUS(s), |
printf(" cerr=%d pid=%d stat=%s\n", EHCI_QTD_GET_CERR(s), |
0); |
EHCI_QTD_GET_PID(s), sbuf); |
DPRINTFN(10, |
for (s = 0; s < 5; s++) |
"active =%d halted=%d buferr=%d babble=%d", |
printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s])); |
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 |
Static void |
ehci_dump_sqh(ehci_soft_qh_t *sqh) |
ehci_dump_sqh(ehci_soft_qh_t *sqh) |
{ |
{ |
ehci_qh_t *qh = &sqh->qh; |
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, |
usb_syncmem(&sqh->dma, sqh->offs, |
sizeof(sqh->qh), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
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); |
endp = le32toh(qh->qh_endp); |
printf(" endp=0x%08x\n", endp); |
DPRINTFN(10, " endp = %#x", endp, 0, 0, 0); |
printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", |
DPRINTFN(10, " addr = 0x%02x inact = %d endpt = %d eps = %d", |
EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), |
EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), |
EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), |
EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp)); |
EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); |
DPRINTFN(10, " dtc = %d hrecl = %d", |
printf(" mpl=0x%x ctl=%d nrl=%d\n", |
EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp), 0, 0); |
EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), |
DPRINTFN(10, " ctl = %d nrl = %d mpl = %#x(%d)", |
EHCI_QH_GET_NRL(endp)); |
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); |
endphub = le32toh(qh->qh_endphub); |
printf(" endphub=0x%08x\n", endphub); |
DPRINTFN(10, " endphub = %#x", endphub, 0, 0, 0); |
printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", |
DPRINTFN(10, " smask = 0x%02x cmask = 0x%02x", |
EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), |
EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), 1, 0); |
EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), |
DPRINTFN(10, " huba = 0x%02x port = %d mult = %d", |
EHCI_QH_GET_MULT(endphub)); |
EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), |
printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n"); |
EHCI_QH_GET_MULT(endphub), 0); |
printf("Overlay qTD:\n"); |
|
|
link = le32toh(qh->qh_curqtd); |
|
ehci_dump_link(link, false); |
|
DPRINTFN(10, "Overlay qTD:", 0, 0, 0, 0); |
ehci_dump_qtd(&qh->qh_qtd); |
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 |
Static void |
ehci_dump_itd(struct ehci_soft_itd *itd) |
ehci_dump_itd(struct ehci_soft_itd *itd) |
{ |
{ |
Line 1588 ehci_dump_itd(struct ehci_soft_itd *itd) |
|
Line 1787 ehci_dump_itd(struct ehci_soft_itd *itd) |
|
ehci_isoc_bufr_ptr_t b, b2, b3; |
ehci_isoc_bufr_ptr_t b, b2, b3; |
int i; |
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++) { |
for (i = 0; i < EHCI_ITD_NUFRAMES; i++) { |
t = le32toh(itd->itd.itd_ctl[i]); |
t = le32toh(itd->itd.itd_ctl[i]); |
printf("ITDctl %d: stat=%X len=%X ioc=%X pg=%X offs=%X\n", i, |
DPRINTF("ITDctl %d: stat = %x len = %x", |
EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t), |
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_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++) |
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]); |
b = le32toh(itd->itd.itd_bufr[0]); |
b2 = le32toh(itd->itd.itd_bufr[1]); |
b2 = le32toh(itd->itd.itd_bufr[1]); |
b3 = le32toh(itd->itd.itd_bufr[2]); |
b3 = le32toh(itd->itd.itd_bufr[2]); |
printf("\nep=%X daddr=%X dir=%d maxpkt=%X multi=%X\n", |
DPRINTF(" ep = %x daddr = %x dir = %d", |
EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2), |
EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2), 0); |
EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3)); |
DPRINTF(" maxpkt = %x multi = %x", |
|
EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3), 0, 0); |
} |
} |
|
|
Static void |
Static void |
ehci_dump_sitd(struct ehci_soft_itd *itd) |
ehci_dump_sitd(struct ehci_soft_itd *itd) |
{ |
{ |
printf("SITD %p next=%p prev=%p xfernext=%p physaddr=%X slot=%d\n", |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
itd, itd->u.frame_list.next, itd->u.frame_list.prev, |
|
itd->xfer_next, itd->physaddr, itd->slot); |
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 |
Static void |
ehci_dump_exfer(struct ehci_xfer *ex) |
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 |
#endif |
|
|
|
Static usbd_status |
Static usbd_status |
ehci_open(usbd_pipe_handle pipe) |
ehci_open(struct usbd_pipe *pipe) |
{ |
{ |
usbd_device_handle dev = pipe->device; |
struct usbd_device *dev = pipe->up_dev; |
ehci_softc_t *sc = dev->bus->hci_private; |
ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; |
usb_endpoint_descriptor_t *ed = pipe->up_endpoint->ue_edesc; |
u_int8_t addr = dev->address; |
uint8_t rhaddr = dev->ud_bus->ub_rhaddr; |
u_int8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes); |
uint8_t addr = dev->ud_addr; |
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; |
uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes); |
|
struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); |
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
usbd_status err; |
usbd_status err; |
int ival, speed, naks; |
int ival, speed, naks; |
int hshubaddr, hshubport; |
int hshubaddr, hshubport; |
|
|
DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
pipe, addr, ed->bEndpointAddress, sc->sc_addr)); |
|
|
DPRINTF("pipe=%p, addr=%d, endpt=%d (%d)", pipe, addr, |
|
ed->bEndpointAddress, rhaddr); |
|
|
if (dev->myhsport) { |
if (dev->ud_myhsport) { |
/* |
/* |
* When directly attached FS/LS device while doing embedded |
* When directly attached FS/LS device while doing embedded |
* transaction translations and we are the hub, set the hub |
* transaction translations and we are the hub, set the hub |
* address to 0 (us). |
* address to 0 (us). |
*/ |
*/ |
if (!(sc->sc_flags & EHCIF_ETTF) |
if (!(sc->sc_flags & EHCIF_ETTF) |
|| (dev->myhsport->parent->address != sc->sc_addr)) { |
|| (dev->ud_myhsport->up_parent->ud_addr != rhaddr)) { |
hshubaddr = dev->myhsport->parent->address; |
hshubaddr = dev->ud_myhsport->up_parent->ud_addr; |
} else { |
} else { |
hshubaddr = 0; |
hshubaddr = 0; |
} |
} |
hshubport = dev->myhsport->portno; |
hshubport = dev->ud_myhsport->up_portno; |
} else { |
} else { |
hshubaddr = 0; |
hshubaddr = 0; |
hshubport = 0; |
hshubport = 0; |
} |
} |
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
return (USBD_IOERROR); |
return USBD_IOERROR; |
|
|
/* toggle state needed for bulk endpoints */ |
/* 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) { |
switch (ed->bEndpointAddress) { |
case USB_CONTROL_ENDPOINT: |
case USB_CONTROL_ENDPOINT: |
pipe->methods = &ehci_root_ctrl_methods; |
pipe->up_methods = &roothub_ctrl_methods; |
break; |
break; |
case UE_DIR_IN | EHCI_INTR_ENDPT: |
case UE_DIR_IN | USBROOTHUB_INTR_ENDPT: |
pipe->methods = &ehci_root_intr_methods; |
pipe->up_methods = &ehci_root_intr_methods; |
break; |
break; |
default: |
default: |
DPRINTF(("ehci_open: bad bEndpointAddress 0x%02x\n", |
DPRINTF("bad bEndpointAddress 0x%02x", |
ed->bEndpointAddress)); |
ed->bEndpointAddress, 0, 0, 0); |
return (USBD_INVAL); |
return USBD_INVAL; |
} |
} |
return (USBD_NORMAL_COMPLETION); |
return USBD_NORMAL_COMPLETION; |
} |
} |
|
|
/* XXX All this stuff is only valid for async. */ |
/* 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_LOW: speed = EHCI_QH_SPEED_LOW; break; |
case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break; |
case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break; |
case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; 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) { |
if (speed == EHCI_QH_SPEED_LOW && xfertype == UE_ISOCHRONOUS) { |
aprint_error_dev(sc->sc_dev, "error opening low/full speed " |
DPRINTF("hshubaddr=%d hshubport=%d", hshubaddr, hshubport, 0, |
"isoc endpoint.\n"); |
0); |
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)); |
|
return USBD_INVAL; |
return USBD_INVAL; |
} |
} |
|
|
Line 1716 ehci_open(usbd_pipe_handle pipe) |
|
Line 1941 ehci_open(usbd_pipe_handle pipe) |
|
if (xfertype != UE_ISOCHRONOUS) { |
if (xfertype != UE_ISOCHRONOUS) { |
sqh = ehci_alloc_sqh(sc); |
sqh = ehci_alloc_sqh(sc); |
if (sqh == NULL) |
if (sqh == NULL) |
return (USBD_NOMEM); |
return USBD_NOMEM; |
/* qh_link filled when the QH is added */ |
/* qh_link filled when the QH is added */ |
sqh->qh.qh_endp = htole32( |
sqh->qh.qh_endp = htole32( |
EHCI_QH_SET_ADDR(addr) | |
EHCI_QH_SET_ADDR(addr) | |
Line 1736 ehci_open(usbd_pipe_handle pipe) |
|
Line 1961 ehci_open(usbd_pipe_handle pipe) |
|
sqh->qh.qh_endphub |= htole32( |
sqh->qh.qh_endphub |= htole32( |
EHCI_QH_SET_PORT(hshubport) | |
EHCI_QH_SET_PORT(hshubport) | |
EHCI_QH_SET_HUBA(hshubaddr) | |
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; |
sqh->qh.qh_curqtd = EHCI_NULL; |
/* Fill the overlay qTD */ |
/* Fill the overlay qTD */ |
Line 1754 ehci_open(usbd_pipe_handle pipe) |
|
Line 1980 ehci_open(usbd_pipe_handle pipe) |
|
switch (xfertype) { |
switch (xfertype) { |
case UE_CONTROL: |
case UE_CONTROL: |
err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), |
err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), |
0, &epipe->u.ctl.reqdma); |
0, &epipe->ctrl.reqdma); |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (err) |
if (err) |
printf("ehci_open: usb_allocmem()=%d\n", err); |
printf("ehci_open: usb_allocmem()=%d\n", err); |
#endif |
#endif |
if (err) |
if (err) |
goto bad; |
goto bad; |
pipe->methods = &ehci_device_ctrl_methods; |
pipe->up_methods = &ehci_device_ctrl_methods; |
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
ehci_add_qh(sc, sqh, sc->sc_async_head); |
ehci_add_qh(sc, sqh, sc->sc_async_head); |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
break; |
break; |
case UE_BULK: |
case UE_BULK: |
pipe->methods = &ehci_device_bulk_methods; |
pipe->up_methods = &ehci_device_bulk_methods; |
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
ehci_add_qh(sc, sqh, sc->sc_async_head); |
ehci_add_qh(sc, sqh, sc->sc_async_head); |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
break; |
break; |
case UE_INTERRUPT: |
case UE_INTERRUPT: |
pipe->methods = &ehci_device_intr_methods; |
pipe->up_methods = &ehci_device_intr_methods; |
ival = pipe->interval; |
ival = pipe->up_interval; |
if (ival == USBD_DEFAULT_INTERVAL) { |
if (ival == USBD_DEFAULT_INTERVAL) { |
if (speed == EHCI_QH_SPEED_HIGH) { |
if (speed == EHCI_QH_SPEED_HIGH) { |
if (ed->bInterval > 16) { |
if (ed->bInterval > 16) { |
Line 1794 ehci_open(usbd_pipe_handle pipe) |
|
Line 2020 ehci_open(usbd_pipe_handle pipe) |
|
goto bad; |
goto bad; |
break; |
break; |
case UE_ISOCHRONOUS: |
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) { |
if (ed->bInterval == 0 || ed->bInterval > 16) { |
printf("ehci: opening pipe with invalid bInterval\n"); |
printf("ehci: opening pipe with invalid bInterval\n"); |
err = USBD_INVAL; |
err = USBD_INVAL; |
Line 1805 ehci_open(usbd_pipe_handle pipe) |
|
Line 2035 ehci_open(usbd_pipe_handle pipe) |
|
err = USBD_INVAL; |
err = USBD_INVAL; |
goto bad; |
goto bad; |
} |
} |
epipe->u.isoc.next_frame = 0; |
epipe->isoc.next_frame = 0; |
epipe->u.isoc.cur_xfers = 0; |
epipe->isoc.cur_xfers = 0; |
break; |
break; |
default: |
default: |
DPRINTF(("ehci: bad xfer type %d\n", xfertype)); |
DPRINTF("bad xfer type %d", xfertype, 0, 0, 0); |
err = USBD_INVAL; |
err = USBD_INVAL; |
goto bad; |
goto bad; |
} |
} |
return (USBD_NORMAL_COMPLETION); |
return USBD_NORMAL_COMPLETION; |
|
|
bad: |
bad: |
if (sqh != NULL) |
if (sqh != NULL) { |
|
mutex_enter(&sc->sc_lock); |
ehci_free_sqh(sc, sqh); |
ehci_free_sqh(sc, sqh); |
return (err); |
mutex_exit(&sc->sc_lock); |
|
} |
|
return err; |
} |
} |
|
|
/* |
/* |
Line 1830 ehci_add_qh(ehci_softc_t *sc, ehci_soft_ |
|
Line 2063 ehci_add_qh(ehci_softc_t *sc, ehci_soft_ |
|
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), |
usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), |
sizeof(head->qh.qh_link), BUS_DMASYNC_POSTWRITE); |
sizeof(head->qh.qh_link), BUS_DMASYNC_POSTWRITE); |
|
|
sqh->next = head->next; |
sqh->next = head->next; |
sqh->qh.qh_link = head->qh.qh_link; |
sqh->qh.qh_link = head->qh.qh_link; |
|
|
usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), |
usb_syncmem(&sqh->dma, sqh->offs + offsetof(ehci_qh_t, qh_link), |
sizeof(sqh->qh.qh_link), BUS_DMASYNC_PREWRITE); |
sizeof(sqh->qh.qh_link), BUS_DMASYNC_PREWRITE); |
|
|
head->next = sqh; |
head->next = sqh; |
head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); |
head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); |
|
|
usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), |
usb_syncmem(&head->dma, head->offs + offsetof(ehci_qh_t, qh_link), |
sizeof(head->qh.qh_link), BUS_DMASYNC_PREWRITE); |
sizeof(head->qh.qh_link), BUS_DMASYNC_PREWRITE); |
|
|
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug > 5) { |
DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
printf("ehci_add_qh:\n"); |
ehci_dump_sqh(sqh); |
ehci_dump_sqh(sqh); |
DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
} |
|
#endif |
#endif |
} |
} |
|
|
|
|
ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) |
ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) |
{ |
{ |
int i; |
int i; |
u_int32_t status; |
uint32_t status; |
|
|
/* Save toggle bit and ping status. */ |
/* Save toggle bit and ping status. */ |
usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
usb_syncmem(&sqh->dma, sqh->offs, sizeof(sqh->qh), |
Line 1922 ehci_sync_hc(ehci_softc_t *sc) |
|
Line 2160 ehci_sync_hc(ehci_softc_t *sc) |
|
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
if (sc->sc_dying) { |
if (sc->sc_dying) { |
DPRINTFN(2,("ehci_sync_hc: dying\n")); |
DPRINTF("dying", 0, 0, 0, 0); |
return; |
return; |
} |
} |
DPRINTFN(2,("ehci_sync_hc: enter\n")); |
|
/* ask for doorbell */ |
/* ask for doorbell */ |
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); |
EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); |
DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", |
DPRINTF("cmd = 0x%08x sts = 0x%08x", |
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); |
EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); |
|
|
error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); /* bell wait */ |
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 |
#ifdef DIAGNOSTIC |
if (error) |
if (error == EWOULDBLOCK) { |
printf("ehci_sync_hc: cv_timedwait() = %d\n", error); |
printf("ehci_sync_hc: timed out\n"); |
|
} else if (error) { |
|
printf("ehci_sync_hc: cv_timedwait: error %d\n", error); |
|
} |
#endif |
#endif |
DPRINTFN(2,("ehci_sync_hc: exit\n")); |
|
} |
} |
|
|
Static void |
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) |
for (; itd != NULL; itd = itd->xfer_next) { |
panic("ehci isoc xfer being freed, but with no itd chain\n"); |
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 */ |
/* Unlink itd from hardware chain, or frame array */ |
if (prev == NULL) { /* We're at the table head */ |
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; |
sc->sc_flist[itd->slot] = itd->itd.itd_next; |
usb_syncmem(&sc->sc_fldma, |
usb_syncmem(&sc->sc_fldma, |
sizeof(ehci_link_t) * itd->slot, |
sizeof(ehci_link_t) * itd->slot, |
sizeof(ehci_link_t), |
sizeof(ehci_link_t), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
|
|
if (itd->u.frame_list.next != NULL) |
if (itd->frame_list.next != NULL) |
itd->u.frame_list.next->u.frame_list.prev = NULL; |
itd->frame_list.next->frame_list.prev = NULL; |
} else { |
} else { |
/* XXX this part is untested... */ |
/* XXX this part is untested... */ |
prev->itd.itd_next = itd->itd.itd_next; |
prev->itd.itd_next = itd->itd.itd_next; |
usb_syncmem(&itd->dma, |
usb_syncmem(&itd->dma, |
itd->offs + offsetof(ehci_itd_t, itd_next), |
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; |
prev->frame_list.next = itd->frame_list.next; |
if (itd->u.frame_list.next != NULL) |
if (itd->frame_list.next != NULL) |
itd->u.frame_list.next->u.frame_list.prev = prev; |
itd->frame_list.next->frame_list.prev = prev; |
} |
} |
} |
} |
|
} |
|
|
prev = NULL; |
Static void |
for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) { |
ehci_free_itd_chain(ehci_softc_t *sc, struct ehci_soft_itd *itd) |
if (prev != NULL) |
{ |
ehci_free_itd(sc, prev); |
struct ehci_soft_itd *next; |
prev = itd; |
|
|
mutex_enter(&sc->sc_lock); |
|
next = NULL; |
|
for (; itd != NULL; itd = next) { |
|
next = itd->xfer_next; |
|
ehci_free_itd_locked(sc, itd); |
} |
} |
if (prev) |
mutex_exit(&sc->sc_lock); |
ehci_free_itd(sc, prev); |
|
exfer->itdstart = NULL; |
|
exfer->itdend = NULL; |
|
} |
} |
|
|
/***********/ |
Static void |
|
ehci_remove_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) |
/* |
{ |
* 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 const usb_device_qualifier_t ehci_odevd = { |
KASSERT(mutex_owned(&sc->sc_lock)); |
USB_DEVICE_DESCRIPTOR_SIZE, |
|
UDESC_DEVICE_QUALIFIER, /* type */ |
|
{0x00, 0x02}, /* USB version */ |
|
UDCLASS_HUB, /* class */ |
|
UDSUBCLASS_HUB, /* subclass */ |
|
UDPROTO_FSHUB, /* protocol */ |
|
64, /* max packet */ |
|
1, /* # of configurations */ |
|
0 |
|
}; |
|
|
|
Static const usb_config_descriptor_t ehci_confd = { |
for (; sitd != NULL; sitd = sitd->xfer_next) { |
USB_CONFIG_DESCRIPTOR_SIZE, |
struct ehci_soft_sitd *prev = sitd->frame_list.prev; |
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 */ |
|
}; |
|
|
|
Static const usb_interface_descriptor_t ehci_ifcd = { |
/* Unlink sitd from hardware chain, or frame array */ |
USB_INTERFACE_DESCRIPTOR_SIZE, |
if (prev == NULL) { /* We're at the table head */ |
UDESC_INTERFACE, |
sc->sc_softsitds[sitd->slot] = sitd->frame_list.next; |
0, |
sc->sc_flist[sitd->slot] = sitd->sitd.sitd_next; |
0, |
usb_syncmem(&sc->sc_fldma, |
1, |
sizeof(ehci_link_t) * sitd->slot, |
UICLASS_HUB, |
sizeof(ehci_link_t), |
UISUBCLASS_HUB, |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
UIPROTO_HSHUBSTT, |
|
0 |
|
}; |
|
|
|
Static const usb_endpoint_descriptor_t ehci_endpd = { |
if (sitd->frame_list.next != NULL) |
USB_ENDPOINT_DESCRIPTOR_SIZE, |
sitd->frame_list.next->frame_list.prev = NULL; |
UDESC_ENDPOINT, |
} else { |
UE_DIR_IN | EHCI_INTR_ENDPT, |
/* XXX this part is untested... */ |
UE_INTERRUPT, |
prev->sitd.sitd_next = sitd->sitd.sitd_next; |
{8, 0}, /* max packet */ |
usb_syncmem(&sitd->dma, |
12 |
sitd->offs + offsetof(ehci_sitd_t, sitd_next), |
}; |
sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE); |
|
|
Static const usb_hub_descriptor_t ehci_hubd = { |
prev->frame_list.next = sitd->frame_list.next; |
USB_HUB_DESCRIPTOR_SIZE, |
if (sitd->frame_list.next != NULL) |
UDESC_HUB, |
sitd->frame_list.next->frame_list.prev = prev; |
0, |
} |
{0,0}, |
} |
0, |
} |
0, |
|
{""}, |
|
{""}, |
|
}; |
|
|
|
/* |
Static void |
* Simulate a hardware hub by handling all the necessary requests. |
ehci_free_sitd_chain(ehci_softc_t *sc, struct ehci_soft_sitd *sitd) |
*/ |
|
Static usbd_status |
|
ehci_root_ctrl_transfer(usbd_xfer_handle xfer) |
|
{ |
{ |
ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; |
|
usbd_status err; |
|
|
|
/* Insert last in queue. */ |
|
mutex_enter(&sc->sc_lock); |
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); |
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; |
ehci_softc_t *sc = EHCI_BUS2SC(bus); |
usb_device_request_t *req; |
|
void *buf = NULL; |
|
int port, i; |
|
int len, value, index, l, totlen = 0; |
|
usb_port_status_t ps; |
|
usb_hub_descriptor_t hubd; |
usb_hub_descriptor_t hubd; |
usbd_status err; |
usb_port_status_t ps; |
u_int32_t v; |
uint16_t len, value, index; |
|
int l, totlen = 0; |
|
int port, i; |
|
uint32_t v; |
|
|
if (sc->sc_dying) |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
return (USBD_IOERROR); |
|
|
|
#ifdef DIAGNOSTIC |
if (sc->sc_dying) |
if (!(xfer->rqflags & URQ_REQUEST)) |
return -1; |
/* XXX panic */ |
|
return (USBD_INVAL); |
|
#endif |
|
req = &xfer->request; |
|
|
|
DPRINTFN(4,("ehci_root_ctrl_start: type=0x%02x request=%02x\n", |
DPRINTF("type=0x%02x request=%02x", req->bmRequestType, req->bRequest, |
req->bmRequestType, req->bRequest)); |
0, 0); |
|
|
len = UGETW(req->wLength); |
len = UGETW(req->wLength); |
value = UGETW(req->wValue); |
value = UGETW(req->wValue); |
index = UGETW(req->wIndex); |
index = UGETW(req->wIndex); |
|
|
if (len != 0) |
|
buf = KERNADDR(&xfer->dmabuf, 0); |
|
|
|
#define C(x,y) ((x) | ((y) << 8)) |
#define C(x,y) ((x) | ((y) << 8)) |
switch(C(req->bRequest, req->bmRequestType)) { |
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; |
|
case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): |
case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): |
DPRINTFN(8,("ehci_root_ctrl_start: wValue=0x%04x\n", value)); |
|
if (len == 0) |
if (len == 0) |
break; |
break; |
switch(value >> 8) { |
switch (value) { |
case UDESC_DEVICE: |
case C(0, UDESC_DEVICE): { |
if ((value & 0xff) != 0) { |
usb_device_descriptor_t devd; |
err = USBD_IOERROR; |
totlen = min(buflen, sizeof(devd)); |
goto ret; |
memcpy(&devd, buf, totlen); |
} |
USETW(devd.idVendor, sc->sc_id_vendor); |
totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); |
memcpy(buf, &devd, totlen); |
USETW(ehci_devd.idVendor, sc->sc_id_vendor); |
|
memcpy(buf, &ehci_devd, l); |
|
break; |
break; |
/* |
|
* We can't really operate at another speed, but the spec says |
} |
* we need this descriptor. |
#define sd ((usb_string_descriptor_t *)buf) |
*/ |
case C(1, UDESC_STRING): |
case UDESC_DEVICE_QUALIFIER: |
/* Vendor */ |
if ((value & 0xff) != 0) { |
totlen = usb_makestrdesc(sd, len, sc->sc_vendor); |
err = USBD_IOERROR; |
|
goto ret; |
|
} |
|
totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); |
|
memcpy(buf, &ehci_odevd, l); |
|
break; |
break; |
/* |
case C(2, UDESC_STRING): |
* We can't really operate at another speed, but the spec says |
/* Product */ |
* we need this descriptor. |
totlen = usb_makestrdesc(sd, len, "EHCI root hub"); |
*/ |
|
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); |
|
break; |
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 |
#undef sd |
break; |
|
default: |
default: |
err = USBD_IOERROR; |
/* default from usbroothub */ |
goto ret; |
return buflen; |
} |
|
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; |
|
} |
} |
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; |
break; |
|
|
/* Hub requests */ |
/* Hub requests */ |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): |
break; |
break; |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): |
case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): |
DPRINTFN(4, ("ehci_root_ctrl_start: UR_CLEAR_PORT_FEATURE " |
DPRINTF("UR_CLEAR_PORT_FEATURE port=%d feature=%d", index, |
"port=%d feature=%d\n", |
value, 0, 0); |
index, value)); |
|
if (index < 1 || index > sc->sc_noport) { |
if (index < 1 || index > sc->sc_noport) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
port = EHCI_PORTSC(index); |
port = EHCI_PORTSC(index); |
v = EOREAD4(sc, port); |
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; |
v &= ~EHCI_PS_CLEAR; |
switch(value) { |
switch (value) { |
case UHF_PORT_ENABLE: |
case UHF_PORT_ENABLE: |
EOWRITE4(sc, port, v &~ EHCI_PS_PE); |
EOWRITE4(sc, port, v &~ EHCI_PS_PE); |
break; |
break; |
Line 2289 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2374 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
EOWRITE4(sc, port, v &~ EHCI_PS_PP); |
EOWRITE4(sc, port, v &~ EHCI_PS_PP); |
break; |
break; |
case UHF_PORT_TEST: |
case UHF_PORT_TEST: |
DPRINTFN(2,("ehci_root_ctrl_start: clear port test " |
DPRINTF("clear port test %d", index, 0, 0, 0); |
"%d\n", index)); |
|
break; |
break; |
case UHF_PORT_INDICATOR: |
case UHF_PORT_INDICATOR: |
DPRINTFN(2,("ehci_root_ctrl_start: clear port ind " |
DPRINTF("clear port ind %d", index, 0, 0, 0); |
"%d\n", index)); |
|
EOWRITE4(sc, port, v &~ EHCI_PS_PIC); |
EOWRITE4(sc, port, v &~ EHCI_PS_PIC); |
break; |
break; |
case UHF_C_PORT_CONNECTION: |
case UHF_C_PORT_CONNECTION: |
Line 2313 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2396 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
sc->sc_isreset[index] = 0; |
sc->sc_isreset[index] = 0; |
break; |
break; |
default: |
default: |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
#if 0 |
#if 0 |
switch(value) { |
switch(value) { |
Line 2332 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2414 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
if (len == 0) |
if (len == 0) |
break; |
break; |
if ((value & 0xff) != 0) { |
if ((value & 0xff) != 0) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
hubd = ehci_hubd; |
totlen = min(buflen, sizeof(hubd)); |
|
memcpy(&hubd, buf, totlen); |
hubd.bNbrPorts = sc->sc_noport; |
hubd.bNbrPorts = sc->sc_noport; |
v = EOREAD4(sc, EHCI_HCSPARAMS); |
v = EOREAD4(sc, EHCI_HCSPARAMS); |
USETW(hubd.wHubCharacteristics, |
USETW(hubd.wHubCharacteristics, |
Line 2346 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2428 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) |
for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) |
hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ |
hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ |
hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; |
hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; |
l = min(len, hubd.bDescLength); |
totlen = min(totlen, hubd.bDescLength); |
totlen = l; |
memcpy(buf, &hubd, totlen); |
memcpy(buf, &hubd, l); |
|
break; |
break; |
case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): |
case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): |
if (len != 4) { |
if (len != 4) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
memset(buf, 0, len); /* ? XXX */ |
memset(buf, 0, len); /* ? XXX */ |
totlen = len; |
totlen = len; |
break; |
break; |
case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): |
case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): |
DPRINTFN(8,("ehci_root_ctrl_start: get port status i=%d\n", |
DPRINTF("get port status i=%d", index, 0, 0, 0); |
index)); |
|
if (index < 1 || index > sc->sc_noport) { |
if (index < 1 || index > sc->sc_noport) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
if (len != 4) { |
if (len != 4) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
v = EOREAD4(sc, EHCI_PORTSC(index)); |
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; |
i = UPS_HIGH_SPEED; |
if (sc->sc_flags & EHCIF_ETTF) { |
if (sc->sc_flags & EHCIF_ETTF) { |
Line 2397 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2474 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; |
if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; |
if (sc->sc_isreset[index]) i |= UPS_C_PORT_RESET; |
if (sc->sc_isreset[index]) i |= UPS_C_PORT_RESET; |
USETW(ps.wPortChange, i); |
USETW(ps.wPortChange, i); |
l = min(len, sizeof ps); |
totlen = min(len, sizeof(ps)); |
memcpy(buf, &ps, l); |
memcpy(buf, &ps, totlen); |
totlen = l; |
|
break; |
break; |
case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): |
case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): |
case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): |
break; |
break; |
case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): |
case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): |
if (index < 1 || index > sc->sc_noport) { |
if (index < 1 || index > sc->sc_noport) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
port = EHCI_PORTSC(index); |
port = EHCI_PORTSC(index); |
v = EOREAD4(sc, port); |
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; |
v &= ~EHCI_PS_CLEAR; |
switch(value) { |
switch(value) { |
case UHF_PORT_ENABLE: |
case UHF_PORT_ENABLE: |
Line 2423 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2497 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
EOWRITE4(sc, port, v | EHCI_PS_SUSP); |
EOWRITE4(sc, port, v | EHCI_PS_SUSP); |
break; |
break; |
case UHF_PORT_RESET: |
case UHF_PORT_RESET: |
DPRINTFN(5,("ehci_root_ctrl_start: reset port %d\n", |
DPRINTF("reset port %d", index, 0, 0, 0); |
index)); |
|
if (EHCI_PS_IS_LOWSPEED(v) |
if (EHCI_PS_IS_LOWSPEED(v) |
&& sc->sc_ncomp > 0 |
&& sc->sc_ncomp > 0 |
&& !(sc->sc_flags & EHCIF_ETTF)) { |
&& !(sc->sc_flags & EHCIF_ETTF)) { |
Line 2441 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2514 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
/* Wait for reset to complete. */ |
/* Wait for reset to complete. */ |
usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); |
usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); |
if (sc->sc_dying) { |
if (sc->sc_dying) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
/* |
/* |
* An embedded transaction translator will automatically |
* An embedded transaction translator will automatically |
Line 2457 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2529 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
usb_delay_ms(&sc->sc_bus, |
usb_delay_ms(&sc->sc_bus, |
EHCI_PORT_RESET_COMPLETE); |
EHCI_PORT_RESET_COMPLETE); |
if (sc->sc_dying) { |
if (sc->sc_dying) { |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
} |
} |
|
|
v = EOREAD4(sc, port); |
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) { |
if (v & EHCI_PS_PR) { |
printf("%s: port reset timeout\n", |
printf("%s: port reset timeout\n", |
device_xname(sc->sc_dev)); |
device_xname(sc->sc_dev)); |
return (USBD_TIMEOUT); |
return USBD_TIMEOUT; |
} |
} |
if (!(v & EHCI_PS_PE)) { |
if (!(v & EHCI_PS_PE)) { |
/* Not a high speed device, give up ownership.*/ |
/* Not a high speed device, give up ownership.*/ |
Line 2475 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2546 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
break; |
break; |
} |
} |
sc->sc_isreset[index] = 1; |
sc->sc_isreset[index] = 1; |
DPRINTF(("ehci port %d reset, status = 0x%08x\n", |
DPRINTF("ehci port %d reset, status = 0x%08x", index, |
index, v)); |
v, 0, 0); |
break; |
break; |
case UHF_PORT_POWER: |
case UHF_PORT_POWER: |
DPRINTFN(2,("ehci_root_ctrl_start: set port power " |
DPRINTF("set port power %d (has PPC = %d)", index, |
"%d (has PPC = %d)\n", index, |
sc->sc_hasppc, 0, 0); |
sc->sc_hasppc)); |
|
if (sc->sc_hasppc) |
if (sc->sc_hasppc) |
EOWRITE4(sc, port, v | EHCI_PS_PP); |
EOWRITE4(sc, port, v | EHCI_PS_PP); |
break; |
break; |
case UHF_PORT_TEST: |
case UHF_PORT_TEST: |
DPRINTFN(2,("ehci_root_ctrl_start: set port test " |
DPRINTF("set port test %d", index, 0, 0, 0); |
"%d\n", index)); |
|
break; |
break; |
case UHF_PORT_INDICATOR: |
case UHF_PORT_INDICATOR: |
DPRINTFN(2,("ehci_root_ctrl_start: set port ind " |
DPRINTF("set port ind %d", index, 0, 0, 0); |
"%d\n", index)); |
|
EOWRITE4(sc, port, v | EHCI_PS_PIC); |
EOWRITE4(sc, port, v | EHCI_PS_PIC); |
break; |
break; |
default: |
default: |
err = USBD_IOERROR; |
return -1; |
goto ret; |
|
} |
} |
break; |
break; |
case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): |
case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): |
Line 2505 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
Line 2572 ehci_root_ctrl_start(usbd_xfer_handle xf |
|
case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): |
case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): |
break; |
break; |
default: |
default: |
err = USBD_IOERROR; |
/* default from usbroothub */ |
goto ret; |
DPRINTF("returning %d (usbroothub default)", buflen, 0, 0, 0); |
|
|
|
return buflen; |
} |
} |
xfer->actlen = totlen; |
|
err = USBD_NORMAL_COMPLETION; |
DPRINTF("returning %d", totlen, 0, 0, 0); |
ret: |
|
mutex_enter(&sc->sc_lock); |
return totlen; |
xfer->status = err; |
|
usb_transfer_complete(xfer); |
|
mutex_exit(&sc->sc_lock); |
|
return (USBD_IN_PROGRESS); |
|
} |
} |
|
|
Static void |
Static void |
ehci_disown(ehci_softc_t *sc, int index, int lowspeed) |
ehci_disown(ehci_softc_t *sc, int index, int lowspeed) |
{ |
{ |
int port; |
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 |
#ifdef DIAGNOSTIC |
if (sc->sc_npcomp != 0) { |
if (sc->sc_npcomp != 0) { |
int i = (index-1) / sc->sc_npcomp; |
int i = (index-1) / sc->sc_npcomp; |
Line 2546 ehci_disown(ehci_softc_t *sc, int index, |
|
Line 2613 ehci_disown(ehci_softc_t *sc, int index, |
|
EOWRITE4(sc, port, v | EHCI_PS_PO); |
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 |
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; |
usbd_status err; |
|
|
/* Insert last in queue. */ |
/* Insert last in queue. */ |
Line 2578 ehci_root_intr_transfer(usbd_xfer_handle |
|
Line 2624 ehci_root_intr_transfer(usbd_xfer_handle |
|
err = usb_insert_transfer(xfer); |
err = usb_insert_transfer(xfer); |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
if (err) |
if (err) |
return (err); |
return err; |
|
|
/* Pipe isn't running, start first */ |
/* 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 |
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 = EHCI_XFER2SC(xfer); |
ehci_softc_t *sc = pipe->device->bus->hci_private; |
|
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
return (USBD_IOERROR); |
return USBD_IOERROR; |
|
|
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
sc->sc_intrxfer = xfer; |
sc->sc_intrxfer = xfer; |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
|
|
return (USBD_IN_PROGRESS); |
return USBD_IN_PROGRESS; |
} |
} |
|
|
/* Abort a root interrupt request. */ |
/* Abort a root interrupt request. */ |
Static void |
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(mutex_owned(&sc->sc_lock)); |
KASSERT(xfer->pipe->intrxfer == xfer); |
KASSERT(xfer->ux_pipe->up_intrxfer == xfer); |
|
|
sc->sc_intrxfer = NULL; |
sc->sc_intrxfer = NULL; |
|
|
xfer->status = USBD_CANCELLED; |
xfer->ux_status = USBD_CANCELLED; |
usb_transfer_complete(xfer); |
usb_transfer_complete(xfer); |
} |
} |
|
|
/* Close the root pipe. */ |
/* Close the root pipe. */ |
Static void |
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; |
sc->sc_intrxfer = NULL; |
} |
} |
|
|
Static void |
Static void |
ehci_root_intr_done(usbd_xfer_handle xfer) |
ehci_root_intr_done(struct usbd_xfer *xfer) |
{ |
{ |
xfer->hcpriv = NULL; |
|
} |
} |
|
|
/************************/ |
/************************/ |
Line 2644 ehci_alloc_sqh(ehci_softc_t *sc) |
|
Line 2688 ehci_alloc_sqh(ehci_softc_t *sc) |
|
int i, offs; |
int i, offs; |
usb_dma_t dma; |
usb_dma_t dma; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
|
mutex_enter(&sc->sc_lock); |
if (sc->sc_freeqhs == NULL) { |
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, |
err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK, |
EHCI_PAGE_SIZE, &dma); |
EHCI_PAGE_SIZE, &dma); |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
Line 2653 ehci_alloc_sqh(ehci_softc_t *sc) |
|
Line 2702 ehci_alloc_sqh(ehci_softc_t *sc) |
|
printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); |
printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); |
#endif |
#endif |
if (err) |
if (err) |
return (NULL); |
return NULL; |
for(i = 0; i < EHCI_SQH_CHUNK; i++) { |
|
|
mutex_enter(&sc->sc_lock); |
|
for (i = 0; i < EHCI_SQH_CHUNK; i++) { |
offs = i * EHCI_SQH_SIZE; |
offs = i * EHCI_SQH_SIZE; |
sqh = KERNADDR(&dma, offs); |
sqh = KERNADDR(&dma, offs); |
sqh->physaddr = DMAADDR(&dma, offs); |
sqh->physaddr = DMAADDR(&dma, offs); |
Line 2666 ehci_alloc_sqh(ehci_softc_t *sc) |
|
Line 2717 ehci_alloc_sqh(ehci_softc_t *sc) |
|
} |
} |
sqh = sc->sc_freeqhs; |
sqh = sc->sc_freeqhs; |
sc->sc_freeqhs = sqh->next; |
sc->sc_freeqhs = sqh->next; |
|
mutex_exit(&sc->sc_lock); |
|
|
memset(&sqh->qh, 0, sizeof(ehci_qh_t)); |
memset(&sqh->qh, 0, sizeof(ehci_qh_t)); |
sqh->next = NULL; |
sqh->next = NULL; |
return (sqh); |
return sqh; |
} |
} |
|
|
Static void |
Static void |
ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) |
ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) |
{ |
{ |
|
KASSERT(mutex_owned(&sc->sc_lock)); |
|
|
sqh->next = sc->sc_freeqhs; |
sqh->next = sc->sc_freeqhs; |
sc->sc_freeqhs = sqh; |
sc->sc_freeqhs = sqh; |
} |
} |
Line 2686 ehci_alloc_sqtd(ehci_softc_t *sc) |
|
Line 2741 ehci_alloc_sqtd(ehci_softc_t *sc) |
|
int i, offs; |
int i, offs; |
usb_dma_t dma; |
usb_dma_t dma; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
|
mutex_enter(&sc->sc_lock); |
if (sc->sc_freeqtds == NULL) { |
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, |
err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK, |
EHCI_PAGE_SIZE, &dma); |
EHCI_PAGE_SIZE, &dma); |
Line 2698 ehci_alloc_sqtd(ehci_softc_t *sc) |
|
Line 2757 ehci_alloc_sqtd(ehci_softc_t *sc) |
|
if (err) |
if (err) |
goto done; |
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; |
offs = i * EHCI_SQTD_SIZE; |
sqtd = KERNADDR(&dma, offs); |
sqtd = KERNADDR(&dma, offs); |
sqtd->physaddr = DMAADDR(&dma, offs); |
sqtd->physaddr = DMAADDR(&dma, offs); |
Line 2712 ehci_alloc_sqtd(ehci_softc_t *sc) |
|
Line 2772 ehci_alloc_sqtd(ehci_softc_t *sc) |
|
|
|
sqtd = sc->sc_freeqtds; |
sqtd = sc->sc_freeqtds; |
sc->sc_freeqtds = sqtd->nextqtd; |
sc->sc_freeqtds = sqtd->nextqtd; |
|
mutex_exit(&sc->sc_lock); |
|
|
memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); |
memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); |
sqtd->nextqtd = NULL; |
sqtd->nextqtd = NULL; |
sqtd->xfer = NULL; |
sqtd->xfer = NULL; |
|
|
done: |
done: |
return (sqtd); |
return sqtd; |
} |
} |
|
|
Static void |
Static void |
ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd) |
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; |
sqtd->nextqtd = sc->sc_freeqtds; |
sc->sc_freeqtds = sqtd; |
sc->sc_freeqtds = sqtd; |
|
mutex_exit(&sc->sc_lock); |
} |
} |
|
|
Static usbd_status |
Static int |
ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, |
ehci_alloc_sqtd_chain(ehci_softc_t *sc, struct usbd_xfer *xfer, |
int alen, int rd, usbd_xfer_handle xfer, |
int alen, int rd, ehci_soft_qtd_t **sp) |
ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep) |
{ |
{ |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
ehci_soft_qtd_t *next, *cur; |
uint16_t flags = xfer->ux_flags; |
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; |
|
} |
|
|
|
/* Find number of pages we'll be using, insert dma addresses */ |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
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); |
|
} |
|
|
|
/* First buffer pointer requires a page offset to start at */ |
ASSERT_SLEEPABLE(); |
va = (vaddr_t)KERNADDR(dma, curoffs); |
KASSERT(sp); |
cur->qtd.qtd_buffer[0] |= htole32(EHCI_PAGE_OFFSET(va)); |
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->xfer = xfer; |
cur->len = curlen; |
cur->len = 0; |
|
|
DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08zx end=0x%08zx\n", |
|
(size_t)curoffs, (size_t)(curoffs + curlen))); |
|
|
|
/* 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 = exfer->ex_sqtds[0]; |
*sp, *ep)); |
DPRINTF("return sqtd=%p", *sp, 0, 0, 0); |
|
|
return (USBD_NORMAL_COMPLETION); |
return 0; |
|
|
nomem: |
nomem: |
/* XXX free chain */ |
ehci_free_sqtds(sc, exfer); |
DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n")); |
kmem_free(exfer->ex_sqtds, sizeof(ehci_soft_qtd_t *) * nsqtd); |
return (USBD_NOMEM); |
DPRINTF("no memory", 0, 0, 0, 0); |
|
return ENOMEM; |
} |
} |
|
|
Static void |
Static void |
ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd, |
ehci_free_sqtds(ehci_softc_t *sc, struct ehci_xfer *exfer) |
ehci_soft_qtd_t *sqtdend) |
|
{ |
{ |
ehci_soft_qtd_t *p; |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
int i; |
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", |
if (sqtd == NULL) |
sqtd, sqtdend)); |
break; |
|
|
for (i = 0; sqtd != sqtdend; sqtd = p, i++) { |
sqtd->nextqtd = sc->sc_freeqtds; |
p = sqtd->nextqtd; |
sc->sc_freeqtds = sqtd; |
ehci_free_sqtd(sc, sqtd); |
|
} |
} |
|
mutex_exit(&sc->sc_lock); |
} |
} |
|
|
Static ehci_soft_itd_t * |
Static void |
ehci_alloc_itd(ehci_softc_t *sc) |
ehci_append_sqtd(ehci_soft_qtd_t *sqtd, ehci_soft_qtd_t *prev) |
{ |
{ |
struct ehci_soft_itd *itd, *freeitd; |
if (prev) { |
usbd_status err; |
prev->nextqtd = sqtd; |
int i, offs, frindex, previndex; |
prev->qtd.qtd_next = htole32(sqtd->physaddr); |
usb_dma_t dma; |
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 |
KASSERT(length != 0 || (!isread && (flags & USBD_FORCE_SHORT_XFER))); |
* 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; |
|
} |
|
} |
|
|
|
if (freeitd == NULL) { |
const uint32_t qtdstatus = EHCI_QTD_ACTIVE | |
DPRINTFN(2, ("ehci_alloc_itd allocating chunk\n")); |
EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | |
err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK, |
EHCI_QTD_SET_CERR(3) |
EHCI_PAGE_SIZE, &dma); |
; |
|
|
|
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); |
|
|
|
/* |
|
* 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) { |
if (err) { |
DPRINTF(("ehci_alloc_itd, alloc returned %d\n", err)); |
DPRINTF("alloc returned %d", err, 0, 0, 0); |
mutex_exit(&sc->sc_lock); |
|
return NULL; |
return NULL; |
} |
} |
|
mutex_enter(&sc->sc_lock); |
|
|
for (i = 0; i < EHCI_ITD_CHUNK; i++) { |
for (int i = 0; i < EHCI_ITD_CHUNK; i++) { |
offs = i * EHCI_ITD_SIZE; |
int offs = i * EHCI_ITD_SIZE; |
itd = KERNADDR(&dma, offs); |
itd = KERNADDR(&dma, offs); |
itd->physaddr = DMAADDR(&dma, offs); |
itd->physaddr = DMAADDR(&dma, offs); |
itd->dma = dma; |
itd->dma = dma; |
itd->offs = offs; |
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); |
freeitd = LIST_FIRST(&sc->sc_freeitds); |
} |
} |
|
|
itd = freeitd; |
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)); |
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->frame_list.next = NULL; |
itd->u.frame_list.prev = NULL; |
itd->frame_list.prev = NULL; |
itd->xfer_next = NULL; |
itd->xfer_next = NULL; |
itd->slot = 0; |
itd->slot = 0; |
|
|
mutex_exit(&sc->sc_lock); |
|
|
|
return itd; |
return itd; |
} |
} |
|
|
Static void |
Static ehci_soft_sitd_t * |
ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd) |
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; |
} |
} |
|
|
/****************/ |
/****************/ |
Line 2964 ehci_free_itd(ehci_softc_t *sc, ehci_sof |
|
Line 3091 ehci_free_itd(ehci_softc_t *sc, ehci_sof |
|
* Assumes that there are no pending transactions. |
* Assumes that there are no pending transactions. |
*/ |
*/ |
Static void |
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; |
struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); |
ehci_softc_t *sc = pipe->device->bus->hci_private; |
ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
ehci_soft_qh_t *sqh = epipe->sqh; |
ehci_soft_qh_t *sqh = epipe->sqh; |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
Line 2977 ehci_close_pipe(usbd_pipe_handle pipe, e |
|
Line 3104 ehci_close_pipe(usbd_pipe_handle pipe, e |
|
} |
} |
|
|
/* |
/* |
* Abort a device request. |
* Cancel or timeout a device request. We have two cases to deal with |
* If this routine is called at splusb() it guarantees that the request |
* |
* will be removed from the hardware scheduling and that the callback |
* 1) A driver wants to stop scheduled or inflight transfers |
* for it will be called with USBD_CANCELLED status. |
* 2) A transfer has timed out |
* It's impossible to guarantee that the requested transfer will not |
* |
* have happened since the hardware runs concurrently. |
* have (partially) happened since the hardware runs concurrently. |
* If the transaction has already happened we rely on the ordinary |
* |
* interrupt processing to process it. |
* Transfer state is protected by the bus lock and we set the transfer status |
* XXX This is most probably wrong. |
* as soon as either of the above happens (with bus lock held). |
* XXXMRG this doesn't make sense anymore. |
* |
|
* Then we arrange for the hardware to tells us that it is not still |
|
* processing the TDs by setting the QH halted bit and wait for the ehci |
|
* door bell |
*/ |
*/ |
Static void |
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) |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
ehci_softc_t *sc = epipe->pipe.device->bus->hci_private; |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
|
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
ehci_soft_qh_t *sqh = epipe->sqh; |
ehci_soft_qh_t *sqh = epipe->sqh; |
ehci_soft_qtd_t *sqtd; |
ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; |
ehci_physaddr_t cur; |
ehci_physaddr_t cur; |
u_int32_t qhstatus; |
uint32_t qhstatus; |
int hit; |
int hit; |
int wake; |
|
|
|
DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe)); |
KASSERTMSG((status == USBD_CANCELLED || status == USBD_TIMEOUT), |
|
"invalid status for abort: %d", (int)status); |
|
|
|
DPRINTF("xfer=%p pipe=%p", xfer, epipe, 0, 0); |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
|
ASSERT_SLEEPABLE(); |
|
|
if (sc->sc_dying) { |
if (status == USBD_CANCELLED) { |
/* If we're dying, just do the software part. */ |
/* |
xfer->status = status; /* make software ignore it */ |
* We are synchronously aborting. Try to stop the |
callout_stop(&xfer->timeout_handle); |
* callout and task, but if we can't, wait for them to |
usb_transfer_complete(xfer); |
* complete. |
return; |
*/ |
|
callout_halt(&xfer->ux_callout, &sc->sc_lock); |
|
usb_rem_task_wait(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, |
|
USB_TASKQ_HC, &sc->sc_lock); |
|
} else { |
|
/* Otherwise, we are timing out. */ |
|
KASSERT(status == USBD_TIMEOUT); |
} |
} |
|
|
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 |
* The xfer cannot have been cancelled already. It is the |
* complete and return. |
* responsibility of the caller of usbd_abort_pipe not to try |
|
* to abort a pipe multiple times, whether concurrently or |
|
* sequentially. |
*/ |
*/ |
if (xfer->hcflags & UXFER_ABORTING) { |
|
DPRINTFN(2, ("ehci_abort_xfer: already aborting\n")); |
KASSERT(xfer->ux_status != USBD_CANCELLED); |
#ifdef DIAGNOSTIC |
|
if (status == USBD_TIMEOUT) |
/* Only the timeout, which runs only once, can time it out. */ |
printf("ehci_abort_xfer: TIMEOUT while aborting\n"); |
KASSERT(xfer->ux_status != USBD_TIMEOUT); |
#endif |
|
/* Override the status which might be USBD_TIMEOUT. */ |
/* If anyone else beat us, we're done. */ |
xfer->status = status; |
if (xfer->ux_status != USBD_IN_PROGRESS) |
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); |
|
return; |
return; |
|
|
|
/* We beat everyone else. Claim the status. */ |
|
xfer->ux_status = status; |
|
|
|
/* |
|
* If we're dying, skip the hardware action and just notify the |
|
* software that we're done. |
|
*/ |
|
if (sc->sc_dying) { |
|
goto dying; |
} |
} |
xfer->hcflags |= UXFER_ABORTING; |
|
|
|
/* |
/* |
* Step 1: Make interrupt routine and hardware ignore xfer. |
* HC Step 1: Make interrupt routine and hardware ignore xfer. |
*/ |
*/ |
xfer->status = status; /* make software ignore it */ |
ehci_del_intr_list(sc, exfer); |
callout_stop(&xfer->timeout_handle); |
|
|
|
usb_syncmem(&sqh->dma, |
usb_syncmem(&sqh->dma, |
sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
Line 3052 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 3195 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), |
sizeof(sqh->qh.qh_qtd.qtd_status), |
sizeof(sqh->qh.qh_qtd.qtd_status), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
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, |
usb_syncmem(&sqtd->dma, |
sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sizeof(sqtd->qtd.qtd_status), |
sizeof(sqtd->qtd.qtd_status), |
Line 3062 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 3213 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sqtd->offs + offsetof(ehci_qtd_t, qtd_status), |
sizeof(sqtd->qtd.qtd_status), |
sizeof(sqtd->qtd.qtd_status), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
if (sqtd == exfer->sqtdend) |
if (sqtd == lsqtd) |
break; |
break; |
} |
} |
|
|
/* |
/* |
* Step 2: Wait until we know hardware has finished any possible |
* HC Step 2: Wait until we know hardware has finished any possible |
* use of the xfer. Also make sure the soft interrupt routine |
* use of the xfer. |
* has run. |
|
*/ |
*/ |
ehci_sync_hc(sc); |
ehci_sync_hc(sc); |
sc->sc_softwake = 1; |
|
usb_schedsoftintr(&sc->sc_bus); |
|
cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); |
|
|
|
/* |
/* |
* Step 3: Remove any vestiges of the xfer from the hardware. |
* HC Step 3: Remove any vestiges of the xfer from the hardware. |
* The complication here is that the hardware may have executed |
* The complication here is that the hardware may have executed |
* beyond the xfer we're trying to abort. So as we're scanning |
* beyond the xfer we're trying to abort. So as we're scanning |
* the TDs of this xfer we check if the hardware points to |
* the TDs of this xfer we check if the hardware points to |
Line 3090 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 3237 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); |
cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); |
hit = 0; |
hit = 0; |
for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { |
for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { |
hit |= cur == sqtd->physaddr; |
hit |= cur == sqtd->physaddr; |
if (sqtd == exfer->sqtdend) |
if (sqtd == lsqtd) |
break; |
break; |
} |
} |
sqtd = sqtd->nextqtd; |
sqtd = sqtd->nextqtd; |
/* Zap curqtd register if hardware pointed inside the xfer. */ |
/* Zap curqtd register if hardware pointed inside the xfer. */ |
if (hit && sqtd != NULL) { |
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 */ |
sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */ |
usb_syncmem(&sqh->dma, |
usb_syncmem(&sqh->dma, |
sqh->offs + offsetof(ehci_qh_t, qh_curqtd), |
sqh->offs + offsetof(ehci_qh_t, qh_curqtd), |
Line 3110 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
Line 3257 ehci_abort_xfer(usbd_xfer_handle xfer, u |
|
sizeof(sqh->qh.qh_qtd.qtd_status), |
sizeof(sqh->qh.qh_qtd.qtd_status), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
} else { |
} 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. |
* Final step: Notify completion to waiting xfers. |
*/ |
*/ |
|
dying: |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
exfer->isdone = 1; |
exfer->ex_isdone = true; |
#endif |
#endif |
wake = xfer->hcflags & UXFER_ABORTWAIT; |
|
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); |
|
usb_transfer_complete(xfer); |
usb_transfer_complete(xfer); |
if (wake) { |
DPRINTFN(14, "end", 0, 0, 0, 0); |
cv_broadcast(&xfer->hccv); |
|
} |
|
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
#undef exfer |
|
} |
} |
|
|
Static void |
Static void |
ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status) |
ehci_abort_isoc_xfer(struct usbd_xfer *xfer, usbd_status status) |
{ |
{ |
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
ehci_isoc_trans_t trans_status; |
ehci_isoc_trans_t trans_status; |
struct ehci_pipe *epipe; |
|
struct ehci_xfer *exfer; |
struct ehci_xfer *exfer; |
ehci_softc_t *sc; |
ehci_softc_t *sc; |
struct ehci_soft_itd *itd; |
struct ehci_soft_itd *itd; |
int i, wake; |
struct ehci_soft_sitd *sitd; |
|
int i; |
|
|
|
KASSERTMSG(status == USBD_CANCELLED, |
|
"invalid status for abort: %d", (int)status); |
|
|
epipe = (struct ehci_pipe *) xfer->pipe; |
exfer = EHCI_XFER2EXFER(xfer); |
exfer = EXFER(xfer); |
sc = EHCI_XFER2SC(xfer); |
sc = epipe->pipe.device->bus->hci_private; |
|
|
|
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)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
|
ASSERT_SLEEPABLE(); |
|
|
if (sc->sc_dying) { |
/* No timeout or task here. */ |
xfer->status = status; |
|
callout_stop(&xfer->timeout_handle); |
|
usb_transfer_complete(xfer); |
|
return; |
|
} |
|
|
|
if (xfer->hcflags & UXFER_ABORTING) { |
/* |
DPRINTFN(2, ("ehci_abort_isoc_xfer: already aborting\n")); |
* The xfer cannot have been cancelled already. It is the |
|
* responsibility of the caller of usbd_abort_pipe not to try |
|
* to abort a pipe multiple times, whether concurrently or |
|
* sequentially. |
|
*/ |
|
KASSERT(xfer->ux_status != USBD_CANCELLED); |
|
|
#ifdef DIAGNOSTIC |
/* If anyone else beat us, we're done. */ |
if (status == USBD_TIMEOUT) |
if (xfer->ux_status != USBD_IN_PROGRESS) |
printf("ehci_abort_isoc_xfer: TIMEOUT while aborting\n"); |
return; |
#endif |
|
|
|
xfer->status = status; |
/* We beat everyone else. Claim the status. */ |
DPRINTFN(2, ("ehci_abort_isoc_xfer: waiting for abort to finish\n")); |
xfer->ux_status = status; |
xfer->hcflags |= UXFER_ABORTWAIT; |
|
while (xfer->hcflags & UXFER_ABORTING) |
/* |
cv_wait(&xfer->hccv, &sc->sc_lock); |
* If we're dying, skip the hardware action and just notify the |
goto done; |
* software that we're done. |
|
*/ |
|
if (sc->sc_dying) { |
|
goto dying; |
} |
} |
xfer->hcflags |= UXFER_ABORTING; |
|
|
|
xfer->status = status; |
/* |
callout_stop(&xfer->timeout_handle); |
* HC Step 1: Make interrupt routine and hardware ignore xfer. |
|
*/ |
|
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) { |
for (i = 0; i < 8; i++) { |
usb_syncmem(&itd->dma, |
trans_status = le32toh(itd->itd.itd_ctl[i]); |
itd->offs + offsetof(ehci_itd_t, itd_ctl), |
trans_status &= ~EHCI_ITD_ACTIVE; |
sizeof(itd->itd.itd_ctl), |
itd->itd.itd_ctl[i] = htole32(trans_status); |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
} |
|
|
for (i = 0; i < 8; i++) { |
usb_syncmem(&itd->dma, |
trans_status = le32toh(itd->itd.itd_ctl[i]); |
itd->offs + offsetof(ehci_itd_t, itd_ctl), |
trans_status &= ~EHCI_ITD_ACTIVE; |
sizeof(itd->itd.itd_ctl), |
itd->itd.itd_ctl[i] = htole32(trans_status); |
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, |
trans_status = le32toh(sitd->sitd.sitd_trans); |
itd->offs + offsetof(ehci_itd_t, itd_ctl), |
trans_status &= ~EHCI_SITD_ACTIVE; |
sizeof(itd->itd.itd_ctl), |
sitd->sitd.sitd_trans = htole32(trans_status); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
|
|
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; |
dying: |
usb_schedsoftintr(&sc->sc_bus); |
|
cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); |
|
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
exfer->isdone = 1; |
exfer->ex_isdone = true; |
#endif |
#endif |
wake = xfer->hcflags & UXFER_ABORTWAIT; |
|
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); |
|
usb_transfer_complete(xfer); |
usb_transfer_complete(xfer); |
if (wake) { |
DPRINTFN(14, "end", 0, 0, 0, 0); |
cv_broadcast(&xfer->hccv); |
|
} |
|
|
|
done: |
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
return; |
|
} |
} |
|
|
Static void |
Static void |
ehci_timeout(void *addr) |
ehci_timeout(void *addr) |
{ |
{ |
struct ehci_xfer *exfer = addr; |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
struct ehci_pipe *epipe = (struct ehci_pipe *)exfer->xfer.pipe; |
struct usbd_xfer *xfer = addr; |
ehci_softc_t *sc = epipe->pipe.device->bus->hci_private; |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
|
struct usbd_device *dev = xfer->ux_pipe->up_dev; |
|
|
DPRINTF(("ehci_timeout: exfer=%p\n", exfer)); |
DPRINTF("xfer %p", xfer, 0, 0, 0); |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug > 1) |
if (ehcidebug >= 2) { |
usbd_dump_pipe(exfer->xfer.pipe); |
struct usbd_pipe *pipe = xfer->ux_pipe; |
#endif |
usbd_dump_pipe(pipe); |
|
|
if (sc->sc_dying) { |
|
mutex_enter(&sc->sc_lock); |
|
ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT); |
|
mutex_exit(&sc->sc_lock); |
|
return; |
|
} |
} |
|
#endif |
|
|
/* Execute the abort in a process context. */ |
mutex_enter(&sc->sc_lock); |
usb_init_task(&exfer->abort_task, ehci_timeout_task, addr, |
if (!sc->sc_dying && xfer->ux_status == USBD_IN_PROGRESS) |
USB_TASKQ_MPSAFE); |
usb_add_task(dev, &xfer->ux_aborttask, USB_TASKQ_HC); |
usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task, |
mutex_exit(&sc->sc_lock); |
USB_TASKQ_HC); |
|
} |
} |
|
|
Static void |
Static void |
ehci_timeout_task(void *addr) |
ehci_timeout_task(void *addr) |
{ |
{ |
usbd_xfer_handle xfer = addr; |
struct usbd_xfer *xfer = addr; |
ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; |
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); |
mutex_enter(&sc->sc_lock); |
ehci_abort_xfer(xfer, USBD_TIMEOUT); |
ehci_abort_xfer(xfer, USBD_TIMEOUT); |
Line 3254 ehci_timeout_task(void *addr) |
|
Line 3416 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 |
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; |
usbd_status err; |
|
|
/* Insert last in queue. */ |
/* Insert last in queue. */ |
Line 3265 ehci_device_ctrl_transfer(usbd_xfer_hand |
|
Line 3515 ehci_device_ctrl_transfer(usbd_xfer_hand |
|
err = usb_insert_transfer(xfer); |
err = usb_insert_transfer(xfer); |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
if (err) |
if (err) |
return (err); |
return err; |
|
|
/* Pipe isn't running, start first */ |
/* 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 |
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; |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
usbd_status err; |
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) |
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 |
#ifdef DIAGNOSTIC |
if (!(xfer->rqflags & URQ_REQUEST)) { |
exfer->ex_isdone = false; |
/* XXX panic */ |
#endif |
printf("ehci_device_ctrl_transfer: not a request\n"); |
|
return (USBD_INVAL); |
/* 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 |
#endif |
|
|
err = ehci_device_request(xfer); |
mutex_enter(&sc->sc_lock); |
if (err) { |
|
return (err); |
/* 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) |
#if 0 |
ehci_waitintr(sc, xfer); |
#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 |
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 __diagused = EHCI_XFER2SC(xfer); |
ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
usb_device_request_t *req = &xfer->ux_request; |
usb_device_request_t *req = &xfer->request; |
|
int len = UGETW(req->wLength); |
int len = UGETW(req->wLength); |
int rd = req->bmRequestType & UT_READ; |
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)); |
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); |
|
KASSERT(xfer->ux_rqflags & URQ_REQUEST); |
#ifdef DIAGNOSTIC |
|
if (!(xfer->rqflags & URQ_REQUEST)) { |
|
panic("ehci_ctrl_done: not a request"); |
|
} |
|
#endif |
|
|
|
if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { |
usb_syncmem(&epipe->ctrl.reqdma, 0, sizeof(*req), |
ehci_del_intr_list(sc, ex); /* remove from active list */ |
BUS_DMASYNC_POSTWRITE); |
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); |
if (len) |
usb_syncmem(&epipe->u.ctl.reqdma, 0, sizeof *req, |
usb_syncmem(&xfer->ux_dmabuf, 0, len, |
BUS_DMASYNC_POSTWRITE); |
rd ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); |
if (len) |
|
usb_syncmem(&xfer->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. */ |
/* Abort a device control request. */ |
Static void |
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); |
ehci_abort_xfer(xfer, USBD_CANCELLED); |
} |
} |
|
|
/* Close a device control pipe. */ |
/* Close a device control pipe. */ |
Static void |
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; |
ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
/*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/ |
/*struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe);*/ |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
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); |
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 |
#define exfer EXFER(xfer) |
* machine is too fast, we we can miss transaction completion - when we scan |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
* the active list the transaction still seems to be active. This generally |
usb_device_request_t *req = &xfer->request; |
* exhibits itself as a umass stall that never recovers. |
usbd_device_handle dev = epipe->pipe.device; |
* |
ehci_softc_t *sc = dev->bus->hci_private; |
* We work around this behaviour by setting up this callback after any softintr |
ehci_soft_qtd_t *setup, *stat, *next; |
* that completes with transactions still pending, giving us another chance to |
ehci_soft_qh_t *sqh; |
* check for completion after the writeback has taken place. |
int isread; |
*/ |
int len; |
Static void |
usbd_status err; |
ehci_intrlist_timeout(void *arg) |
|
{ |
|
ehci_softc_t *sc = arg; |
|
|
isread = req->bmRequestType & UT_READ; |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
len = UGETW(req->wLength); |
|
|
|
DPRINTFN(3,("ehci_device_request: type=0x%02x, request=0x%02x, " |
usb_schedsoftintr(&sc->sc_bus); |
"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)); |
|
|
Static int |
setup = ehci_alloc_sqtd(sc); |
ehci_device_bulk_init(struct usbd_xfer *xfer) |
if (setup == NULL) { |
{ |
err = USBD_NOMEM; |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
goto bad1; |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
} |
usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc; |
stat = ehci_alloc_sqtd(sc); |
int endpt = ed->bEndpointAddress; |
if (stat == NULL) { |
int isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
err = USBD_NOMEM; |
int len = xfer->ux_bufsize; |
goto bad2; |
int err = 0; |
} |
|
|
exfer->ex_type = EX_BULK; |
|
exfer->ex_nsqtd = 0; |
|
err = ehci_alloc_sqtd_chain(sc, xfer, len, isread, |
|
&exfer->ex_sqtdstart); |
|
|
|
return err; |
|
} |
|
|
|
Static void |
|
ehci_device_bulk_fini(struct usbd_xfer *xfer) |
|
{ |
|
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
|
struct ehci_xfer *ex = EHCI_XFER2EXFER(xfer); |
|
|
|
KASSERT(ex->ex_type == EX_BULK); |
|
|
|
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_bulk_transfer(struct usbd_xfer *xfer) |
|
{ |
|
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
|
usbd_status err; |
|
|
|
/* Insert last in queue. */ |
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
|
err = usb_insert_transfer(xfer); |
|
mutex_exit(&sc->sc_lock); |
|
if (err) |
|
return err; |
|
|
sqh = epipe->sqh; |
/* Pipe isn't running, start first */ |
|
return ehci_device_bulk_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); |
|
} |
|
|
KASSERTMSG(EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)) == dev->address, |
Static usbd_status |
"address QH %d pipe %d\n", |
ehci_device_bulk_start(struct usbd_xfer *xfer) |
EHCI_QH_GET_ADDR(le32toh(sqh->qh.qh_endp)), dev->address); |
{ |
KASSERTMSG(EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)) == |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize), |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
"MPS QH %d pipe %d\n", |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
EHCI_QH_GET_MPL(le32toh(sqh->qh.qh_endp)), |
ehci_soft_qh_t *sqh; |
UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)); |
ehci_soft_qtd_t *end; |
|
int len, isread, endpt; |
|
|
/* Set up data transaction */ |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
if (len != 0) { |
|
ehci_soft_qtd_t *end; |
|
|
|
/* Start toggle at 1. */ |
DPRINTF("xfer=%p len=%d flags=%d", xfer, xfer->ux_length, |
epipe->nexttoggle = 1; |
xfer->ux_flags, 0); |
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; |
|
} |
|
|
|
memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); |
if (sc->sc_dying) |
usb_syncmem(&epipe->u.ctl.reqdma, 0, sizeof *req, BUS_DMASYNC_PREWRITE); |
return USBD_IOERROR; |
|
|
/* Clear toggle */ |
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); |
setup->qtd.qtd_status = htole32( |
KASSERT(xfer->ux_length <= xfer->ux_bufsize); |
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( |
len = xfer->ux_length; |
EHCI_QTD_ACTIVE | |
endpt = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; |
EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | |
isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
EHCI_QTD_SET_CERR(3) | |
sqh = epipe->sqh; |
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 |
KASSERT(exfer->ex_isdone); |
if (ehcidebug > 5) { |
#ifdef DIAGNOSTIC |
DPRINTF(("ehci_device_request:\n")); |
exfer->ex_isdone = false; |
ehci_dump_sqh(sqh); |
|
ehci_dump_sqtds(setup); |
|
} |
|
#endif |
#endif |
|
|
exfer->sqtdstart = setup; |
/* Take lock here to protect nexttoggle */ |
exfer->sqtdend = stat; |
mutex_enter(&sc->sc_lock); |
#ifdef DIAGNOSTIC |
|
if (!exfer->isdone) { |
ehci_reset_sqtd_chain(sc, xfer, len, isread, &epipe->nexttoggle, &end); |
printf("ehci_device_request: not done, exfer=%p\n", exfer); |
|
} |
exfer->ex_sqtdend = end; |
exfer->isdone = 0; |
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 |
|
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 |
#endif |
|
|
/* Insert qTD in QH list. */ |
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, |
ehci_set_qh_qtd(sqh, setup); /* also does usb_syncmem(sqh) */ |
isread ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); |
if (xfer->timeout && !sc->sc_bus.use_polling) { |
|
callout_reset(&xfer->timeout_handle, mstohz(xfer->timeout), |
/* 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_timeout, xfer); |
} |
} |
ehci_add_intr_list(sc, exfer); |
ehci_add_intr_list(sc, exfer); |
xfer->status = USBD_IN_PROGRESS; |
xfer->ux_status = USBD_IN_PROGRESS; |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
|
|
|
#if 0 |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug > 10) { |
DPRINTFN(5, "data(2)", 0, 0, 0, 0); |
DPRINTF(("ehci_device_request: status=%x\n", |
// delay(10000); |
EOREAD4(sc, EHCI_USBSTS))); |
DPRINTFN(5, "data(3)", 0, 0, 0, 0); |
delay(10000); |
ehci_dump_regs(sc); |
ehci_dump_regs(sc); |
#if 0 |
ehci_dump_sqh(sc->sc_async_head); |
printf("async_head:\n"); |
ehci_dump_sqh(sqh); |
ehci_dump_sqh(sc->sc_async_head); |
ehci_dump_sqtds(setup); |
#endif |
} |
DPRINTF("sqh:", 0, 0, 0, 0); |
|
ehci_dump_sqh(sqh); |
|
ehci_dump_sqtds(exfer->ex_sqtdstart); |
|
#endif |
#endif |
#endif |
|
|
return (USBD_NORMAL_COMPLETION); |
return USBD_IN_PROGRESS; |
|
} |
|
|
bad3: |
Static void |
mutex_exit(&sc->sc_lock); |
ehci_device_bulk_abort(struct usbd_xfer *xfer) |
ehci_free_sqtd(sc, stat); |
{ |
bad2: |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
ehci_free_sqtd(sc, setup); |
|
bad1: |
DPRINTF("xfer %p", xfer, 0, 0, 0); |
DPRINTFN(-1,("ehci_device_request: no memory\n")); |
ehci_abort_xfer(xfer, USBD_CANCELLED); |
mutex_enter(&sc->sc_lock); |
|
xfer->status = err; |
|
usb_transfer_complete(xfer); |
|
mutex_exit(&sc->sc_lock); |
|
return (err); |
|
#undef exfer |
|
} |
} |
|
|
/* |
/* |
* Some EHCI chips from VIA seem to trigger interrupts before writing back the |
* Close a device bulk pipe. |
* 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 |
Static void |
ehci_intrlist_timeout(void *arg) |
ehci_device_bulk_close(struct usbd_pipe *pipe) |
{ |
{ |
ehci_softc_t *sc = arg; |
ehci_softc_t *sc = EHCI_PIPE2SC(pipe); |
|
struct ehci_pipe *epipe = EHCI_PIPE2EPIPE(pipe); |
|
|
DPRINTF(("ehci_intrlist_timeout\n")); |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
usb_schedsoftintr(&sc->sc_bus); |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
|
|
|
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(struct usbd_xfer *xfer) |
|
{ |
|
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; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
|
DPRINTF("xfer=%p, actlen=%d", xfer, xfer->ux_actlen, 0, 0); |
|
|
|
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); |
|
|
|
DPRINTF("length=%d", xfer->ux_actlen, 0, 0, 0); |
} |
} |
|
|
/************************/ |
/************************/ |
|
|
Static usbd_status |
Static usbd_status |
ehci_device_bulk_transfer(usbd_xfer_handle xfer) |
ehci_device_setintr(ehci_softc_t *sc, ehci_soft_qh_t *sqh, int ival) |
|
{ |
|
struct ehci_soft_islot *isp; |
|
int islot, lev; |
|
|
|
/* Find a poll rate that is large enough. */ |
|
for (lev = EHCI_IPOLLRATES - 1; lev > 0; lev--) |
|
if (EHCI_ILEV_IVAL(lev) <= ival) |
|
break; |
|
|
|
/* Pick an interrupt slot at the right level. */ |
|
/* XXX could do better than picking at random */ |
|
sc->sc_rand = (sc->sc_rand + 191) % sc->sc_flsize; |
|
islot = EHCI_IQHIDX(lev, sc->sc_rand); |
|
|
|
sqh->islot = islot; |
|
isp = &sc->sc_islots[islot]; |
|
mutex_enter(&sc->sc_lock); |
|
ehci_add_qh(sc, sqh, isp->sqh); |
|
mutex_exit(&sc->sc_lock); |
|
|
|
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(struct usbd_xfer *xfer) |
{ |
{ |
ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
usbd_status err; |
usbd_status err; |
|
|
/* Insert last in queue. */ |
/* Insert last in queue. */ |
Line 3546 ehci_device_bulk_transfer(usbd_xfer_hand |
|
Line 4001 ehci_device_bulk_transfer(usbd_xfer_hand |
|
err = usb_insert_transfer(xfer); |
err = usb_insert_transfer(xfer); |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
if (err) |
if (err) |
return (err); |
return err; |
|
|
/* Pipe isn't running, start first */ |
/* |
return (ehci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); |
* Pipe isn't running (otherwise err would be USBD_INPROG), |
|
* so start it first. |
|
*/ |
|
return ehci_device_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); |
} |
} |
|
|
Static usbd_status |
Static usbd_status |
ehci_device_bulk_start(usbd_xfer_handle xfer) |
ehci_device_intr_start(struct usbd_xfer *xfer) |
{ |
{ |
#define exfer EXFER(xfer) |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
usbd_device_handle dev = epipe->pipe.device; |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
ehci_softc_t *sc = dev->bus->hci_private; |
ehci_soft_qtd_t *end; |
ehci_soft_qtd_t *data, *dataend; |
|
ehci_soft_qh_t *sqh; |
ehci_soft_qh_t *sqh; |
usbd_status err; |
|
int len, isread, endpt; |
int len, isread, endpt; |
|
|
DPRINTFN(2, ("ehci_device_bulk_start: xfer=%p len=%d flags=%d\n", |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
xfer, xfer->length, xfer->flags)); |
|
|
DPRINTF("xfer=%p len=%d flags=%d", xfer, xfer->ux_length, |
|
xfer->ux_flags, 0); |
|
|
if (sc->sc_dying) |
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 |
#ifdef DIAGNOSTIC |
if (xfer->rqflags & URQ_REQUEST) |
exfer->ex_isdone = false; |
panic("ehci_device_bulk_start: a request"); |
|
#endif |
#endif |
|
|
|
/* Take lock to protect nexttoggle */ |
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
|
|
len = xfer->length; |
ehci_reset_sqtd_chain(sc, xfer, len, isread, &epipe->nexttoggle, &end); |
endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; |
|
isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
|
sqh = epipe->sqh; |
|
|
|
epipe->u.bulk.length = len; |
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; |
|
|
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, |
#ifdef EHCI_DEBUG |
&dataend); |
DPRINTFN(5, "--- dump start ---", 0, 0, 0, 0); |
if (err) { |
ehci_dump_sqh(sqh); |
DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n")); |
ehci_dump_sqtds(exfer->ex_sqtdstart); |
xfer->status = err; |
DPRINTFN(5, "--- dump end ---", 0, 0, 0, 0); |
usb_transfer_complete(xfer); |
#endif |
mutex_exit(&sc->sc_lock); |
|
return (err); |
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->ux_status = USBD_IN_PROGRESS; |
|
mutex_exit(&sc->sc_lock); |
|
|
|
#if 0 |
#ifdef EHCI_DEBUG |
#ifdef EHCI_DEBUG |
if (ehcidebug > 5) { |
DPRINTFN(5, "data(2)", 0, 0, 0, 0); |
DPRINTF(("ehci_device_bulk_start: data(1)\n")); |
// delay(10000); |
ehci_dump_sqh(sqh); |
DPRINTFN(5, "data(3)", 0, 0, 0, 0); |
ehci_dump_sqtds(data); |
ehci_dump_regs(sc); |
|
DPRINTFN(5, "sqh:", 0, 0, 0, 0); |
|
ehci_dump_sqh(sqh); |
|
ehci_dump_sqtds(exfer->ex_sqtdstart); |
|
#endif |
|
#endif |
|
|
|
return USBD_IN_PROGRESS; |
|
} |
|
|
|
Static void |
|
ehci_device_intr_abort(struct usbd_xfer *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 |
|
* async doorbell. That's dependent on the async list, wheras |
|
* intr xfers are periodic, should not use this? |
|
*/ |
|
ehci_abort_xfer(xfer, USBD_CANCELLED); |
|
} |
|
|
|
Static void |
|
ehci_device_intr_close(struct usbd_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)); |
|
|
|
isp = &sc->sc_islots[epipe->sqh->islot]; |
|
ehci_close_pipe(pipe, isp->sqh); |
|
} |
|
|
|
Static void |
|
ehci_device_intr_done(struct usbd_xfer *xfer) |
|
{ |
|
ehci_softc_t *sc __diagused = EHCI_XFER2SC(xfer); |
|
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
|
int isread, endpt; |
|
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
|
DPRINTF("xfer=%p, actlen=%d", xfer, xfer->ux_actlen, |
|
0, 0); |
|
|
|
KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); |
|
|
|
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); |
|
} |
|
|
|
/************************/ |
|
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; |
|
} |
|
|
|
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; |
} |
} |
#endif |
|
|
|
/* Set up interrupt info. */ |
KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths); |
exfer->sqtdstart = data; |
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); |
exfer->sqtdend = dataend; |
KASSERT(exfer->ex_isdone); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (!exfer->isdone) { |
exfer->ex_isdone = false; |
printf("ehci_device_bulk_start: not done, ex=%p\n", exfer); |
|
} |
|
exfer->isdone = 0; |
|
#endif |
#endif |
|
|
ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */ |
/* |
if (xfer->timeout && !sc->sc_bus.use_polling) { |
* Step 1: Initialize sitds. |
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_bulk_start: data(2)\n")); |
|
delay(10000); |
|
DPRINTF(("ehci_device_bulk_start: data(3)\n")); |
|
ehci_dump_regs(sc); |
|
#if 0 |
|
printf("async_head:\n"); |
|
ehci_dump_sqh(sc->sc_async_head); |
|
#endif |
|
printf("sqh:\n"); |
|
ehci_dump_sqh(sqh); |
|
ehci_dump_sqtds(data); |
|
} |
|
#endif |
|
|
|
if (sc->sc_bus.use_polling) |
frames = xfer->ux_nframes; |
ehci_waitintr(sc, xfer); |
dma_buf = &xfer->ux_dmabuf; |
|
offs = 0; |
|
|
return (USBD_IN_PROGRESS); |
for (sitd = exfer->ex_sitdstart, i = 0; i < frames; |
#undef exfer |
i++, sitd = sitd->xfer_next) { |
} |
KASSERT(sitd != NULL); |
|
KASSERT(xfer->ux_frlengths[i] <= 0x3ff); |
|
|
Static void |
sitd->sitd.sitd_trans = htole32(EHCI_SITD_ACTIVE | |
ehci_device_bulk_abort(usbd_xfer_handle xfer) |
EHCI_SITD_SET_LEN(xfer->ux_frlengths[i])); |
{ |
|
DPRINTF(("ehci_device_bulk_abort: xfer=%p\n", xfer)); |
|
ehci_abort_xfer(xfer, USBD_CANCELLED); |
|
} |
|
|
|
/* |
/* Set page0 index and offset - TP and T-offset are set below */ |
* Close a device bulk pipe. |
sitd->sitd.sitd_buffer[0] = htole32(DMAADDR(dma_buf, offs)); |
*/ |
|
Static void |
|
ehci_device_bulk_close(usbd_pipe_handle pipe) |
|
{ |
|
ehci_softc_t *sc = pipe->device->bus->hci_private; |
|
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; |
|
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
total_length += xfer->ux_frlengths[i]; |
|
offs += xfer->ux_frlengths[i]; |
|
|
DPRINTF(("ehci_device_bulk_close: pipe=%p\n", pipe)); |
sitd->sitd.sitd_buffer[1] = |
pipe->endpoint->datatoggle = epipe->nexttoggle; |
htole32(EHCI_SITD_SET_BPTR(DMAADDR(dma_buf, offs - 1))); |
ehci_close_pipe(pipe, sc->sc_async_head); |
|
} |
|
|
|
Static void |
u_int huba __diagused = dev->ud_myhsport->up_parent->ud_addr; |
ehci_device_bulk_done(usbd_xfer_handle 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; |
|
int rd = UE_GET_DIR(endpt) == UE_DIR_IN; |
|
|
|
DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n", |
#if 0 |
xfer, xfer->actlen)); |
if (sc->sc_flags & EHCIF_FREESCALE) { |
|
// Set hub address to 0 if embedded TT is used. |
|
if (huba == sc->sc_addr) |
|
huba = 0; |
|
} |
|
#endif |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
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); |
|
|
if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { |
tlen += sa; |
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); |
|
} |
|
|
|
DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen)); |
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; |
|
} |
|
|
Static usbd_status |
sitd->sitd.sitd_sched = htole32( |
ehci_device_setintr(ehci_softc_t *sc, ehci_soft_qh_t *sqh, int ival) |
EHCI_SITD_SET_SMASK(sa) | |
{ |
EHCI_SITD_SET_CMASK(sb) |
struct ehci_soft_islot *isp; |
); |
int islot, lev; |
|
|
|
/* Find a poll rate that is large enough. */ |
usb_syncmem(&sitd->dma, sitd->offs, sizeof(ehci_sitd_t), |
for (lev = EHCI_IPOLLRATES - 1; lev > 0; lev--) |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
if (EHCI_ILEV_IVAL(lev) <= ival) |
} /* End of frame */ |
break; |
|
|
|
/* Pick an interrupt slot at the right level. */ |
sitd = exfer->ex_sitdend; |
/* XXX could do better than picking at random */ |
sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC); |
sc->sc_rand = (sc->sc_rand + 191) % sc->sc_flsize; |
|
islot = EHCI_IQHIDX(lev, sc->sc_rand); |
|
|
|
sqh->islot = islot; |
usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_trans), |
isp = &sc->sc_islots[islot]; |
sizeof(sitd->sitd.sitd_trans), |
mutex_enter(&sc->sc_lock); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
ehci_add_qh(sc, sqh, isp->sqh); |
|
mutex_exit(&sc->sc_lock); |
|
|
|
return (USBD_NORMAL_COMPLETION); |
usb_syncmem(&exfer->ex_xfer.ux_dmabuf, 0, total_length, |
} |
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
|
|
Static usbd_status |
/* |
ehci_device_intr_transfer(usbd_xfer_handle xfer) |
* Part 2: Transfer descriptors have now been set up, now they must |
{ |
* be scheduled into the periodic frame list. Erk. Not wanting to |
ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; |
* complicate matters, transfer is denied if the transfer spans |
usbd_status err; |
* more than the period frame list. |
|
*/ |
|
|
/* Insert last in queue. */ |
|
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
err = usb_insert_transfer(xfer); |
|
mutex_exit(&sc->sc_lock); |
|
if (err) |
|
return (err); |
|
|
|
/* |
/* Start inserting frames */ |
* Pipe isn't running (otherwise err would be USBD_INPROG), |
if (epipe->isoc.cur_xfers > 0) { |
* so start it first. |
frindex = epipe->isoc.next_frame; |
*/ |
} else { |
return (ehci_device_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); |
frindex = EOREAD4(sc, EHCI_FRINDEX); |
} |
frindex = frindex >> 3; /* Erase microframe index */ |
|
frindex += 2; |
|
} |
|
|
Static usbd_status |
if (frindex >= sc->sc_flsize) |
ehci_device_intr_start(usbd_xfer_handle xfer) |
frindex &= (sc->sc_flsize - 1); |
{ |
|
#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; |
|
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", |
/* Whats the frame interval? */ |
xfer, xfer->length, xfer->flags)); |
i = epipe->pipe.up_endpoint->ue_edesc->bInterval; |
|
|
if (sc->sc_dying) |
for (sitd = exfer->ex_sitdstart, j = 0; j < frames; |
return (USBD_IOERROR); |
j++, sitd = sitd->xfer_next) { |
|
KASSERT(sitd); |
|
|
#ifdef DIAGNOSTIC |
usb_syncmem(&sc->sc_fldma, |
if (xfer->rqflags & URQ_REQUEST) |
sizeof(ehci_link_t) * frindex, |
panic("ehci_device_intr_start: a request"); |
sizeof(ehci_link_t), |
#endif |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
|
|
mutex_enter(&sc->sc_lock); |
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; |
|
|
len = xfer->length; |
usb_syncmem(&sitd->dma, |
endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; |
sitd->offs + offsetof(ehci_sitd_t, sitd_next), |
isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
sizeof(ehci_sitd_t), |
sqh = epipe->sqh; |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
|
|
epipe->u.intr.length = len; |
sc->sc_flist[frindex] = |
|
htole32(EHCI_LINK_SITD | sitd->physaddr); |
|
|
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, |
usb_syncmem(&sc->sc_fldma, |
&dataend); |
sizeof(ehci_link_t) * frindex, |
if (err) { |
sizeof(ehci_link_t), |
DPRINTFN(-1, ("ehci_device_intr_start: no memory\n")); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
xfer->status = err; |
|
usb_transfer_complete(xfer); |
|
mutex_exit(&sc->sc_lock); |
|
return (err); |
|
} |
|
|
|
#ifdef EHCI_DEBUG |
sitd->frame_list.next = sc->sc_softsitds[frindex]; |
if (ehcidebug > 5) { |
sc->sc_softsitds[frindex] = sitd; |
DPRINTF(("ehci_device_intr_start: data(1)\n")); |
if (sitd->frame_list.next != NULL) |
ehci_dump_sqh(sqh); |
sitd->frame_list.next->frame_list.prev = sitd; |
ehci_dump_sqtds(data); |
sitd->slot = frindex; |
} |
sitd->frame_list.prev = NULL; |
#endif |
|
|
|
/* Set up interrupt info. */ |
frindex += i; |
exfer->sqtdstart = data; |
if (frindex >= sc->sc_flsize) |
exfer->sqtdend = dataend; |
frindex -= sc->sc_flsize; |
#ifdef DIAGNOSTIC |
|
if (!exfer->isdone) { |
|
printf("ehci_device_intr_start: not done, ex=%p\n", exfer); |
|
} |
} |
exfer->isdone = 0; |
|
#endif |
|
|
|
ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */ |
epipe->isoc.cur_xfers++; |
if (xfer->timeout && !sc->sc_bus.use_polling) { |
epipe->isoc.next_frame = frindex; |
callout_reset(&xfer->timeout_handle, mstohz(xfer->timeout), |
|
ehci_timeout, xfer); |
|
} |
|
ehci_add_intr_list(sc, exfer); |
ehci_add_intr_list(sc, exfer); |
xfer->status = USBD_IN_PROGRESS; |
xfer->ux_status = USBD_IN_PROGRESS; |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
|
|
#ifdef EHCI_DEBUG |
return USBD_IN_PROGRESS; |
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); |
|
} |
|
#endif |
|
|
|
if (sc->sc_bus.use_polling) |
Static void |
ehci_waitintr(sc, xfer); |
ehci_device_fs_isoc_abort(struct usbd_xfer *xfer) |
|
{ |
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
return (USBD_IN_PROGRESS); |
DPRINTF("xfer = %p", xfer, 0, 0, 0); |
#undef exfer |
ehci_abort_isoc_xfer(xfer, USBD_CANCELLED); |
} |
} |
|
|
Static void |
Static void |
ehci_device_intr_abort(usbd_xfer_handle xfer) |
ehci_device_fs_isoc_close(struct usbd_pipe *pipe) |
{ |
{ |
DPRINTFN(1, ("ehci_device_intr_abort: xfer=%p\n", xfer)); |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
KASSERT(xfer->pipe->intrxfer == xfer); |
|
|
|
/* |
DPRINTF("nothing in the pipe to free?", 0, 0, 0, 0); |
* XXX - abort_xfer uses ehci_sync_hc, which syncs via the advance |
|
* async doorbell. That's dependent on the async list, wheras |
|
* intr xfers are periodic, should not use this? |
|
*/ |
|
ehci_abort_xfer(xfer, USBD_CANCELLED); |
|
} |
} |
|
|
Static void |
Static void |
ehci_device_intr_close(usbd_pipe_handle pipe) |
ehci_device_fs_isoc_done(struct usbd_xfer *xfer) |
{ |
{ |
ehci_softc_t *sc = pipe->device->bus->hci_private; |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
struct ehci_soft_islot *isp; |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
|
|
isp = &sc->sc_islots[epipe->sqh->islot]; |
epipe->isoc.cur_xfers--; |
ehci_close_pipe(pipe, isp->sqh); |
ehci_remove_sitd_chain(sc, exfer->ex_itdstart); |
|
|
|
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, |
|
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
} |
} |
|
|
Static void |
|
ehci_device_intr_done(usbd_xfer_handle xfer) |
/************************/ |
|
|
|
|
|
Static int |
|
ehci_device_isoc_init(struct usbd_xfer *xfer) |
{ |
{ |
#define exfer EXFER(xfer) |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
struct ehci_xfer *ex = EXFER(xfer); |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
ehci_softc_t *sc = xfer->pipe->device->bus->hci_private; |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; |
ehci_soft_itd_t *itd, *prev, *start, *stop; |
ehci_soft_qtd_t *data, *dataend; |
int i, j, k; |
ehci_soft_qh_t *sqh; |
int frames, ufrperframe; |
usbd_status err; |
int err; |
int len, isread, endpt; |
|
|
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
|
start = NULL; |
|
prev = NULL; |
|
itd = NULL; |
|
|
DPRINTFN(10, ("ehci_device_intr_done: xfer=%p, actlen=%d\n", |
KASSERT(xfer->ux_nframes != 0); |
xfer, xfer->actlen)); |
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); |
|
KASSERT(exfer->ex_isdone); |
|
|
KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock)); |
exfer->ex_type = EX_ISOC; |
|
|
if (xfer->pipe->repeat) { |
/* |
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); |
* 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; |
|
} |
|
|
len = epipe->u.intr.length; |
ufrperframe = max(1, USB_UFRAMES_PER_FRAME / (1 << (i - 1))); |
xfer->length = len; |
frames = (xfer->ux_nframes + (ufrperframe - 1)) / ufrperframe; |
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; |
|
|
|
err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, |
for (i = 0, prev = NULL; i < frames; i++, prev = itd) { |
&data, &dataend); |
itd = ehci_alloc_itd(sc); |
if (err) { |
if (itd == NULL) { |
DPRINTFN(-1, ("ehci_device_intr_done: no memory\n")); |
err = ENOMEM; |
xfer->status = err; |
goto fail; |
return; |
|
} |
} |
|
|
/* Set up interrupt info. */ |
if (prev != NULL) { |
exfer->sqtdstart = data; |
/* Maybe not as it's updated by the scheduling? */ |
exfer->sqtdend = dataend; |
prev->itd.itd_next = |
#ifdef DIAGNOSTIC |
htole32(itd->physaddr | EHCI_LINK_ITD); |
if (!exfer->isdone) { |
|
printf("ehci_device_intr_done: not done, ex=%p\n", |
|
exfer); |
|
} |
|
exfer->isdone = 0; |
|
#endif |
|
|
|
ehci_set_qh_qtd(sqh, data); /* also does usb_syncmem(sqh) */ |
prev->xfer_next = itd; |
if (xfer->timeout && !sc->sc_bus.use_polling) { |
} else { |
callout_reset(&xfer->timeout_handle, |
start = itd; |
mstohz(xfer->timeout), ehci_timeout, xfer); |
|
} |
} |
|
|
xfer->status = USBD_IN_PROGRESS; |
/* |
} else if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { |
* Other special values |
ehci_del_intr_list(sc, ex); /* remove from active list */ |
*/ |
ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); |
k = epipe->pipe.up_endpoint->ue_edesc->bEndpointAddress; |
endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; |
itd->itd.itd_bufr[0] = htole32( |
isread = UE_GET_DIR(endpt) == UE_DIR_IN; |
EHCI_ITD_SET_EP(UE_GET_ADDR(k)) | |
usb_syncmem(&xfer->dmabuf, 0, xfer->length, |
EHCI_ITD_SET_DADDR(epipe->pipe.up_dev->ud_addr)); |
isread ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); |
|
|
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); |
|
ehci_soft_itd_t *next; |
|
for (itd = start; itd; itd = next) { |
|
next = itd->xfer_next; |
|
ehci_free_itd_locked(sc, itd); |
} |
} |
#undef exfer |
mutex_exit(&sc->sc_lock); |
|
|
|
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 |
Static usbd_status |
ehci_device_isoc_transfer(usbd_xfer_handle xfer) |
ehci_device_isoc_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; |
usbd_status __diagused err; |
|
|
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
err = usb_insert_transfer(xfer); |
err = usb_insert_transfer(xfer); |
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
if (err && err != USBD_IN_PROGRESS) |
|
return err; |
|
|
|
return ehci_device_isoc_start(xfer); |
KASSERT(err == USBD_NORMAL_COMPLETION); |
} |
|
|
|
Static usbd_status |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
ehci_device_isoc_start(usbd_xfer_handle xfer) |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
{ |
ehci_soft_itd_t *itd, *prev; |
struct ehci_pipe *epipe; |
|
ehci_softc_t *sc; |
|
struct ehci_xfer *exfer; |
|
ehci_soft_itd_t *itd, *prev, *start, *stop; |
|
usb_dma_t *dma_buf; |
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 trans_count, offs, total_length; |
int frindex; |
int frindex; |
|
|
start = NULL; |
EHCIHIST_FUNC(); EHCIHIST_CALLED(); |
|
|
prev = NULL; |
prev = NULL; |
itd = NULL; |
itd = NULL; |
trans_count = 0; |
trans_count = 0; |
total_length = 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", |
DPRINTF("xfer %p flags %d", xfer, xfer->ux_flags, 0, 0); |
xfer, xfer->length, xfer->flags)); |
|
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
return USBD_IOERROR; |
return USBD_IOERROR; |
Line 3977 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4649 ehci_device_isoc_start(usbd_xfer_handle |
|
* the entire frame table. To within 4 frames, to allow some leeway |
* the entire frame table. To within 4 frames, to allow some leeway |
* on either side of where the hc currently is. |
* on either side of where the hc currently is. |
*/ |
*/ |
if ((1 << (epipe->pipe.endpoint->edesc->bInterval)) * |
if ((1 << (epipe->pipe.up_endpoint->ue_edesc->bInterval)) * |
xfer->nframes >= (sc->sc_flsize - 4) * 8) { |
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"); |
printf("ehci: isoc descriptor requested that spans the entire frametable, too many frames\n"); |
return USBD_INVAL; |
return USBD_INVAL; |
} |
} |
|
|
|
KASSERT(xfer->ux_nframes != 0 && xfer->ux_frlengths); |
|
KASSERT(!(xfer->ux_rqflags & URQ_REQUEST)); |
|
KASSERT(exfer->ex_isdone); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if (xfer->rqflags & URQ_REQUEST) |
exfer->ex_isdone = false; |
panic("ehci_device_isoc_start: request\n"); |
|
|
|
if (!exfer->isdone) |
|
printf("ehci_device_isoc_start: not done, ex = %p\n", exfer); |
|
exfer->isdone = 0; |
|
#endif |
#endif |
|
|
/* |
/* |
* Step 1: Allocate and initialize itds, how many do we need? |
* Step 1: Re-Initialize itds |
* One per transfer if interval >= 8 microframes, fewer if we use |
|
* multiple microframes per frame. |
|
*/ |
*/ |
|
|
i = epipe->pipe.endpoint->edesc->bInterval; |
i = epipe->pipe.up_endpoint->ue_edesc->bInterval; |
if (i > 16 || i == 0) { |
if (i > 16 || i == 0) { |
/* Spec page 271 says intervals > 16 are invalid */ |
/* 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; |
return USBD_INVAL; |
} |
} |
|
|
ufrperframe = max(1, USB_UFRAMES_PER_FRAME / (1 << (i - 1))); |
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; |
uframes = USB_UFRAMES_PER_FRAME / ufrperframe; |
|
|
if (frames == 0) { |
if (frames == 0) { |
DPRINTF(("ehci_device_isoc_start: frames == 0\n")); |
DPRINTF("frames == 0", 0, 0, 0, 0); |
return USBD_INVAL; |
return USBD_INVAL; |
} |
} |
|
|
dma_buf = &xfer->dmabuf; |
dma_buf = &xfer->ux_dmabuf; |
offs = 0; |
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; |
int froffs = offs; |
itd = ehci_alloc_itd(sc); |
|
|
|
if (prev != NULL) { |
if (prev != NULL) { |
prev->itd.itd_next = |
prev->itd.itd_next = |
htole32(itd->physaddr | EHCI_LINK_ITD); |
htole32(itd->physaddr | EHCI_LINK_ITD); |
usb_syncmem(&itd->dma, |
usb_syncmem(&prev->dma, |
itd->offs + offsetof(ehci_itd_t, itd_next), |
prev->offs + offsetof(ehci_itd_t, itd_next), |
sizeof(itd->itd.itd_next), BUS_DMASYNC_POSTWRITE); |
sizeof(prev->itd.itd_next), BUS_DMASYNC_POSTWRITE); |
|
|
prev->xfer_next = itd; |
prev->xfer_next = itd; |
} else { |
|
start = itd; |
|
} |
} |
|
|
/* |
/* |
Line 4044 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4711 ehci_device_isoc_start(usbd_xfer_handle |
|
addr = EHCI_PAGE(addr); |
addr = EHCI_PAGE(addr); |
addr /= EHCI_PAGE_SIZE; |
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 |
* looks how far further along the current uframe |
* offset is. Works out how many pages that is. |
* offset is. Works out how many pages that is. |
*/ |
*/ |
|
|
itd->itd.itd_ctl[j] = htole32 ( EHCI_ITD_ACTIVE | |
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_PG(addr) | |
EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,offs)))); |
EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,offs)))); |
|
|
total_length += xfer->frlengths[trans_count]; |
total_length += xfer->ux_frlengths[trans_count]; |
offs += xfer->frlengths[trans_count]; |
offs += xfer->ux_frlengths[trans_count]; |
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); |
itd->itd.itd_ctl[j] |= htole32(EHCI_ITD_IOC); |
break; |
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 |
* pointers are filled out for the next 7 hardware pages in |
* the dma block, so no need to worry what pages to cover |
* the dma block, so no need to worry what pages to cover |
* and what to not. |
* and what to not. |
Line 4076 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4745 ehci_device_isoc_start(usbd_xfer_handle |
|
* of buffer |
* of buffer |
*/ |
*/ |
int page_offs = EHCI_PAGE(froffs + (EHCI_PAGE_SIZE * j)); |
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; |
break; |
|
|
unsigned long long page = DMAADDR(dma_buf, page_offs); |
uint64_t page = DMAADDR(dma_buf, page_offs); |
page = EHCI_PAGE(page); |
page = EHCI_PAGE(page); |
itd->itd.itd_bufr[j] = |
itd->itd.itd_bufr[j] = htole32(EHCI_ITD_SET_BPTR(page)); |
htole32(EHCI_ITD_SET_BPTR(page)); |
itd->itd.itd_bufr_hi[j] = htole32(page >> 32); |
itd->itd.itd_bufr_hi[j] = |
|
htole32(page >> 32); |
|
} |
} |
|
|
/* |
/* |
* Other special values |
* 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)) | |
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; |
? 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) | |
itd->itd.itd_bufr[1] |= htole32(EHCI_ITD_SET_DIR(k) | |
EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j))); |
EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j))); |
|
|
Line 4105 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4771 ehci_device_isoc_start(usbd_xfer_handle |
|
itd->itd.itd_bufr[2] |= |
itd->itd.itd_bufr[2] |= |
htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1)); |
htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1)); |
|
|
usb_syncmem(&itd->dma, |
usb_syncmem(&itd->dma, itd->offs, sizeof(ehci_itd_t), |
itd->offs + offsetof(ehci_itd_t, itd_next), |
|
sizeof(ehci_itd_t), |
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
|
|
prev = itd; |
prev = itd; |
} /* End of frame */ |
} /* End of frame */ |
|
|
stop = itd; |
usb_syncmem(&exfer->ex_xfer.ux_dmabuf, 0, total_length, |
stop->xfer_next = NULL; |
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
exfer->isoc_len = total_length; |
|
|
|
usb_syncmem(&exfer->xfer.dmabuf, 0, total_length, |
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
|
|
|
/* |
/* |
* Part 2: Transfer descriptors have now been set up, now they must |
* Part 2: Transfer descriptors have now been set up, now they must |
Line 4130 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4790 ehci_device_isoc_start(usbd_xfer_handle |
|
mutex_enter(&sc->sc_lock); |
mutex_enter(&sc->sc_lock); |
|
|
/* Start inserting frames */ |
/* Start inserting frames */ |
if (epipe->u.isoc.cur_xfers > 0) { |
if (epipe->isoc.cur_xfers > 0) { |
frindex = epipe->u.isoc.next_frame; |
frindex = epipe->isoc.next_frame; |
} else { |
} else { |
frindex = EOREAD4(sc, EHCI_FRINDEX); |
frindex = EOREAD4(sc, EHCI_FRINDEX); |
frindex = frindex >> 3; /* Erase microframe index */ |
frindex = frindex >> 3; /* Erase microframe index */ |
Line 4142 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4802 ehci_device_isoc_start(usbd_xfer_handle |
|
frindex &= (sc->sc_flsize - 1); |
frindex &= (sc->sc_flsize - 1); |
|
|
/* What's the frame interval? */ |
/* 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) |
if (i / USB_UFRAMES_PER_FRAME == 0) |
i = 1; |
i = 1; |
else |
else |
i /= USB_UFRAMES_PER_FRAME; |
i /= USB_UFRAMES_PER_FRAME; |
|
|
itd = start; |
itd = exfer->ex_itdstart; |
for (j = 0; j < frames; j++) { |
for (j = 0; j < frames; j++) { |
if (itd == NULL) |
KASSERTMSG(itd != NULL, "frame %d\n", j); |
panic("ehci: unexpectedly ran out of isoc itds, isoc_start\n"); |
|
|
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]; |
itd->itd.itd_next = sc->sc_flist[frindex]; |
if (itd->itd.itd_next == 0) |
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; |
itd->itd.itd_next = EHCI_NULL; |
|
|
usb_syncmem(&itd->dma, |
usb_syncmem(&itd->dma, |
itd->offs + offsetof(ehci_itd_t, itd_next), |
itd->offs + offsetof(ehci_itd_t, itd_next), |
sizeof(itd->itd.itd_next), |
sizeof(itd->itd.itd_next), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
|
|
sc->sc_flist[frindex] = htole32(EHCI_LINK_ITD | itd->physaddr); |
sc->sc_flist[frindex] = htole32(EHCI_LINK_ITD | itd->physaddr); |
|
|
usb_syncmem(&sc->sc_fldma, |
usb_syncmem(&sc->sc_fldma, |
sizeof(ehci_link_t) * frindex, |
sizeof(ehci_link_t) * frindex, |
sizeof(ehci_link_t), |
sizeof(ehci_link_t), |
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); |
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; |
sc->sc_softitds[frindex] = itd; |
if (itd->u.frame_list.next != NULL) |
if (itd->frame_list.next != NULL) |
itd->u.frame_list.next->u.frame_list.prev = itd; |
itd->frame_list.next->frame_list.prev = itd; |
itd->slot = frindex; |
itd->slot = frindex; |
itd->u.frame_list.prev = NULL; |
itd->frame_list.prev = NULL; |
|
|
frindex += i; |
frindex += i; |
if (frindex >= sc->sc_flsize) |
if (frindex >= sc->sc_flsize) |
Line 4185 ehci_device_isoc_start(usbd_xfer_handle |
|
Line 4851 ehci_device_isoc_start(usbd_xfer_handle |
|
itd = itd->xfer_next; |
itd = itd->xfer_next; |
} |
} |
|
|
epipe->u.isoc.cur_xfers++; |
epipe->isoc.cur_xfers++; |
epipe->u.isoc.next_frame = frindex; |
epipe->isoc.next_frame = frindex; |
|
|
exfer->itdstart = start; |
|
exfer->itdend = stop; |
|
exfer->sqtdstart = NULL; |
|
exfer->sqtdend = NULL; |
|
|
|
ehci_add_intr_list(sc, exfer); |
ehci_add_intr_list(sc, exfer); |
xfer->status = USBD_IN_PROGRESS; |
xfer->ux_status = USBD_IN_PROGRESS; |
xfer->done = 0; |
|
mutex_exit(&sc->sc_lock); |
mutex_exit(&sc->sc_lock); |
|
|
if (sc->sc_bus.use_polling) { |
|
printf("Starting ehci isoc xfer with polling. Bad idea?\n"); |
|
ehci_waitintr(sc, xfer); |
|
} |
|
|
|
return USBD_IN_PROGRESS; |
return USBD_IN_PROGRESS; |
} |
} |
|
|
Static void |
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); |
ehci_abort_isoc_xfer(xfer, USBD_CANCELLED); |
} |
} |
|
|
Static void |
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 |
Static void |
ehci_device_isoc_done(usbd_xfer_handle xfer) |
ehci_device_isoc_done(struct usbd_xfer *xfer) |
{ |
{ |
struct ehci_xfer *exfer; |
struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); |
ehci_softc_t *sc; |
ehci_softc_t *sc = EHCI_XFER2SC(xfer); |
struct ehci_pipe *epipe; |
struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); |
|
|
exfer = EXFER(xfer); |
|
sc = xfer->pipe->device->bus->hci_private; |
|
epipe = (struct ehci_pipe *) xfer->pipe; |
|
|
|
KASSERT(mutex_owned(&sc->sc_lock)); |
KASSERT(mutex_owned(&sc->sc_lock)); |
|
|
epipe->u.isoc.cur_xfers--; |
epipe->isoc.cur_xfers--; |
if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) { |
ehci_remove_itd_chain(sc, exfer->ex_sitdstart); |
ehci_del_intr_list(sc, exfer); |
usb_syncmem(&xfer->ux_dmabuf, 0, xfer->ux_length, |
ehci_rem_free_itd_chain(sc, exfer); |
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); |
} |
|
|
|
usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_POSTWRITE | |
|
BUS_DMASYNC_POSTREAD); |
|
|
|
} |
} |