Index: sys/dev/usb/dwc_otg.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/dwc_otg.c,v retrieving revision 1.42 diff -u -p -u -r1.42 dwc_otg.c --- sys/dev/usb/dwc_otg.c 3 Feb 2013 22:25:39 -0000 1.42 +++ sys/dev/usb/dwc_otg.c 4 Feb 2013 17:14:31 -0000 @@ -190,9 +192,7 @@ Static void dwc_otg_device_isoc_abort(u Static void dwc_otg_device_isoc_close(usbd_pipe_handle); Static void dwc_otg_device_isoc_done(usbd_xfer_handle); -#if 0 -Static void dwc_otg_close_pipe(usbd_pipe_handle, dwc_otg_soft_ed_t *); -#endif +Static void dwc_otg_close_pipe(usbd_pipe_handle); Static void dwc_otg_abort_xfer(usbd_xfer_handle, usbd_status); Static void dwc_otg_device_clear_toggle(usbd_pipe_handle pipe); @@ -247,6 +248,7 @@ Static void dwc_otg_vbus_interrupt(stru Static void dwc_otg_standard_done(usbd_xfer_handle); Static void dwc_otg_setup_standard_chain(usbd_xfer_handle); Static void dwc_otg_start_standard_chain(usbd_xfer_handle); +Static void dwc_otg_host_channel_free(struct dwc_otg_td *); Static void dwc_otg_core_reset(struct dwc_otg_softc *sc); @@ -425,11 +427,18 @@ dwc_otg_softintr(void *v) mutex_spin_enter(&sc->sc_intr_lock); while ((dxfer = TAILQ_FIRST(&sc->sc_complete)) != NULL) { TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext); + + if (dxfer->xfer.hcflags & UXFER_ABORTING) { + cv_broadcast(&dxfer->xfer.hccv); + continue; + } + mutex_spin_exit(&sc->sc_intr_lock); usb_transfer_complete(&dxfer->xfer); mutex_spin_enter(&sc->sc_intr_lock); } mutex_spin_exit(&sc->sc_intr_lock); + } Static void @@ -577,21 +586,20 @@ dwc_otg_poll(struct usbd_bus *bus) mutex_spin_exit(&sc->sc_intr_lock); } -#if 0 /* * Close a reqular pipe. * Assumes that there are no pending transactions. */ Static void -dwc_otg_close_pipe(usbd_pipe_handle pipe, dwc_otg_soft_ed_t *head) +dwc_otg_close_pipe(usbd_pipe_handle pipe) { struct dwc_otg_pipe *dpipe = (struct dwc_otg_pipe *)pipe; struct dwc_otg_softc *sc = pipe->device->bus->hci_private; dpipe = dpipe; - usb_delay_ms(&sc->sc_bus, 1); + + KASSERT(mutex_owned(&sc->sc_lock)); } -#endif /* * Abort a device request. @@ -616,6 +624,10 @@ dwc_otg_abort_xfer(usbd_xfer_handle xfer return; } + /* + * If an abort is already in progress then just wait for it to + * complete and return. + */ if (xfer->hcflags & UXFER_ABORTING) { xfer->status = status; xfer->hcflags |= UXFER_ABORTWAIT; @@ -623,39 +635,32 @@ dwc_otg_abort_xfer(usbd_xfer_handle xfer cv_wait(&xfer->hccv, &sc->sc_lock); return; } - xfer->hcflags |= UXFER_ABORTING; /* * Step 1: Make interrupt routine and hardware ignore xfer. */ + mutex_spin_enter(&sc->sc_intr_lock); + xfer->hcflags |= UXFER_ABORTING; + xfer->status = status; /* make software ignore it */ callout_stop(&xfer->timeout_handle); - if (dxfer->td_transfer_cache) { - int ch = dxfer->td_transfer_cache->channel; - if (ch < DWC_OTG_MAX_CHANNELS) { - - DPRINTF("Disabling channel %d\n", ch); - DWC_OTG_WRITE_4(sc, DOTG_HCINTMSK(ch), - HCINTMSK_CHHLTDMSK); - DWC_OTG_WRITE_4(sc, DOTG_HCINT(ch), - ~HCINTMSK_CHHLTDMSK); - - if ((DWC_OTG_READ_4(sc, DOTG_HCCHAR(ch)) & - HCCHAR_CHENA) != 0) { - DWC_OTG_MODIFY_4(sc, DOTG_HCCHAR(ch), - HCCHAR_CHENA, HCCHAR_CHDIS); - } - } - } - - mutex_spin_enter(&sc->sc_intr_lock); - dxfer->queued = false; - TAILQ_REMOVE(&sc->sc_active, dxfer, xnext); + if (dxfer->active) + TAILQ_REMOVE(&sc->sc_active, dxfer, xnext); mutex_spin_exit(&sc->sc_intr_lock); + if (dxfer->td_transfer_cache != NULL) + dwc_otg_host_channel_free(dxfer->td_transfer_cache); + + while (dxfer->queued) { + printf("%s: waiting for workq to see dxfer = %p...\n", __func__, dxfer); + cv_wait(&xfer->hccv, &sc->sc_lock); + printf("%s: wakeup!\n", __func__); + } + printf("%s: done!\n", __func__); + /* - * Step 4: Execute callback. + * Step 2: Execute callback. */ wake = xfer->hcflags & UXFER_ABORTWAIT; xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT); @@ -1136,9 +1141,8 @@ dwc_otg_root_ctrl_start(usbd_xfer_handle err = USBD_NORMAL_COMPLETION; fail: - xfer->status = err; - mutex_enter(&sc->sc_lock); + xfer->status = err; usb_transfer_complete(xfer); mutex_exit(&sc->sc_lock); @@ -1307,7 +1311,10 @@ dwc_otg_device_ctrl_close(usbd_pipe_hand dpipe = dpipe; sc = sc; + DPRINTF("\n"); + + dwc_otg_close_pipe(pipe); } Static void @@ -1390,6 +1397,8 @@ dwc_otg_device_bulk_close(usbd_pipe_hand dpipe = dpipe; sc = sc; + + dwc_otg_close_pipe(pipe); } Static void @@ -1475,6 +1484,8 @@ dwc_otg_device_intr_close(usbd_pipe_hand dpipe = dpipe; sc = sc; DPRINTF("\n"); + + dwc_otg_close_pipe(pipe); } Static void @@ -1583,6 +1594,8 @@ dwc_otg_device_isoc_close(usbd_pipe_hand dpipe = dpipe; sc = sc; DPRINTF("\n"); + + dwc_otg_close_pipe(pipe); } void @@ -1653,6 +1666,7 @@ dwc_otg_worker(struct work *wk, void *pr struct dwc_otg_work *dwork = (struct dwc_otg_work *)wk; struct dwc_otg_softc *sc = priv; usbd_xfer_handle xfer = dwork->xfer; + struct dwc_otg_xfer *dxfer = DWC_OTG_XFER2DXFER(xfer); DOTG_EVCNT_INCR(sc->sc_ev_work); @@ -1661,7 +1675,12 @@ dwc_otg_worker(struct work *wk, void *pr dwc_otg_timer(sc); } else { KASSERT(dwork->xfer != NULL); - dwc_otg_start_standard_chain(xfer); + KASSERT(dxfer->queued == true); + + if (!(xfer->hcflags & UXFER_ABORTING)) + dwc_otg_start_standard_chain(xfer); + dxfer->queued = false; + cv_broadcast(&xfer->hccv); } mutex_exit(&sc->sc_lock); } @@ -2311,7 +2357,7 @@ dwc_otg_host_channel_disable(struct dwc_ } } -static void +Static void dwc_otg_host_channel_free(struct dwc_otg_td *td) { struct dwc_otg_softc *sc; @@ -3332,6 +3378,9 @@ repeat: } TAILQ_FOREACH(dxfer, &sc->sc_active, xnext) { + if (dxfer->xfer.hcflags & UXFER_ABORTING) + continue; + if (!dwc_otg_xfer_do_fifo(&dxfer->xfer)) { /* queue has been modified */ goto repeat; @@ -3834,6 +3883,8 @@ dwc_otg_setup_standard_chain(usbd_xfer_h // DPRINTF(("%s: xfer->length %d\n", __func__, xfer->length)); + dxfer->queued = false; + /* get first again */ td = dxfer->td_transfer_first; td->toggle = dpipe->pipe.endpoint->datatoggle; @@ -3946,13 +3997,8 @@ dwc_otg_start_standard_chain(usbd_xfer_h KASSERT(mutex_owned(&sc->sc_lock)); /* put transfer on interrupt queue */ - if (!dxfer->queued) { - dxfer->queued = true; - TAILQ_INSERT_TAIL(&sc->sc_active, dxfer, xnext); - } else { - printf("%s: xfer %p already queued\n", __func__, - xfer); - } + dxfer->active = true; + TAILQ_INSERT_TAIL(&sc->sc_active, dxfer, xnext); /* start timeout, if any */ if (xfer->timeout != 0) { @@ -4043,11 +4089,14 @@ dwc_otg_standard_done(usbd_xfer_handle x dwc_otg_host_channel_free(td); xfer->status = err; - TAILQ_REMOVE(&sc->sc_active, dxfer, xnext); - + if (dxfer->active) { + TAILQ_REMOVE(&sc->sc_active, dxfer, xnext); + dxfer->active = false; + } else { + printf("%s: n.m. aq\n", __func__); + } callout_stop(&xfer->timeout_handle); - dxfer->queued = false; TAILQ_INSERT_TAIL(&sc->sc_complete, dxfer, xnext); usb_schedsoftintr(&sc->sc_bus); @@ -4097,7 +4150,7 @@ dwc_otg_init(struct dwc_otg_softc *sc) sc->sc_rhc_si = softint_establish(SOFTINT_NET | SOFTINT_MPSAFE, dwc_otg_rhc, sc); - workqueue_create(&sc->sc_wq, xname, dwc_otg_worker, sc, PRI_NONE, + workqueue_create(&sc->sc_wq, xname, dwc_otg_worker, sc, PRI_BIO, IPL_USB, WQ_MPSAFE); usb_setup_reserve(sc->sc_dev, &sc->sc_dma_reserve, sc->sc_bus.dmatag, @@ -4389,8 +4442,9 @@ dwc_otg_xfer_start(usbd_xfer_handle xfer if (sc->sc_bus.use_polling) { dwc_otg_start_standard_chain(xfer); } else { - workqueue_enqueue(sc->sc_wq, - (struct work *)&dxfer->work, NULL); + KASSERT(dxfer->queued == false); + dxfer->queued = true; + workqueue_enqueue(sc->sc_wq, (struct work *)&dxfer->work, NULL); } } Index: sys/dev/usb/dwc_otgvar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/dwc_otgvar.h,v retrieving revision 1.9 diff -u -p -u -r1.9 dwc_otgvar.h --- sys/dev/usb/dwc_otgvar.h 31 Jan 2013 12:41:41 -0000 1.9 +++ sys/dev/usb/dwc_otgvar.h 4 Feb 2013 17:14:31 -0000 @@ -158,7 +158,8 @@ struct dwc_otg_xfer { struct usbd_xfer xfer; /* Needs to be first */ struct usb_task abort_task; TAILQ_ENTRY(dwc_otg_xfer) xnext; /* list of active/complete xfers */ - bool queued; + bool queued; /* pending workqueue */ + bool active; /* still active */ void *td_start[1]; dwc_otg_td_t *td_transfer_first; @@ -253,7 +254,6 @@ typedef struct dwc_otg_softc { #define DWC_MODE_HOST 2 /* host only */ struct dwc_otg_flags sc_flags; - } dwc_otg_softc_t; usbd_status dwc_otg_init(dwc_otg_softc_t *);