Module Name: src
Committed By: skrll
Date: Mon Feb 4 21:24:07 UTC 2013
Modified Files:
src/sys/dev/usb: dwc_otg.c dwc_otgvar.h
Log Message:
Fix transfer aborts and bump the workqueue to IPL_BIO.
To generate a diff of this commit:
cvs rdiff -u -r1.42 -r1.43 src/sys/dev/usb/dwc_otg.c
cvs rdiff -u -r1.9 -r1.10 src/sys/dev/usb/dwc_otgvar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/usb/dwc_otg.c
diff -u src/sys/dev/usb/dwc_otg.c:1.42 src/sys/dev/usb/dwc_otg.c:1.43
--- src/sys/dev/usb/dwc_otg.c:1.42 Sun Feb 3 22:25:39 2013
+++ src/sys/dev/usb/dwc_otg.c Mon Feb 4 21:24:07 2013
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_otg.c,v 1.42 2013/02/03 22:25:39 skrll Exp $ */
+/* $NetBSD: dwc_otg.c,v 1.43 2013/02/04 21:24:07 skrll Exp $ */
/*-
* Copyright (c) 2012 Hans Petter Selasky. All rights reserved.
@@ -60,7 +60,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc_otg.c,v 1.42 2013/02/03 22:25:39 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc_otg.c,v 1.43 2013/02/04 21:24:07 skrll Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -190,9 +190,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 +245,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,6 +424,12 @@ 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);
@@ -577,21 +582,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 +620,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 +631,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);
- }
- }
+ if (dxfer->active) {
+ TAILQ_REMOVE(&sc->sc_active, dxfer, xnext);
+ dxfer->active = false;
}
-
- mutex_spin_enter(&sc->sc_intr_lock);
- dxfer->queued = false;
- 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) {
+ cv_wait(&xfer->hccv, &sc->sc_lock);
+ }
+
/*
- * Step 4: Execute callback.
+ * Step 2: Execute callback.
*/
wake = xfer->hcflags & UXFER_ABORTWAIT;
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
@@ -1136,9 +1137,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 +1307,10 @@ dwc_otg_device_ctrl_close(usbd_pipe_hand
dpipe = dpipe;
sc = sc;
+
DPRINTF("\n");
+
+ dwc_otg_close_pipe(pipe);
}
Static void
@@ -1390,6 +1393,8 @@ dwc_otg_device_bulk_close(usbd_pipe_hand
dpipe = dpipe;
sc = sc;
+
+ dwc_otg_close_pipe(pipe);
}
Static void
@@ -1475,6 +1480,8 @@ dwc_otg_device_intr_close(usbd_pipe_hand
dpipe = dpipe;
sc = sc;
DPRINTF("\n");
+
+ dwc_otg_close_pipe(pipe);
}
Static void
@@ -1583,6 +1590,8 @@ dwc_otg_device_isoc_close(usbd_pipe_hand
dpipe = dpipe;
sc = sc;
DPRINTF("\n");
+
+ dwc_otg_close_pipe(pipe);
}
void
@@ -1653,6 +1662,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 +1671,13 @@ 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 +2327,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 +3348,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 +3853,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 +3967,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 +4059,12 @@ 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;
+ }
callout_stop(&xfer->timeout_handle);
- dxfer->queued = false;
TAILQ_INSERT_TAIL(&sc->sc_complete, dxfer, xnext);
usb_schedsoftintr(&sc->sc_bus);
@@ -4097,7 +4114,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 +4406,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: src/sys/dev/usb/dwc_otgvar.h
diff -u src/sys/dev/usb/dwc_otgvar.h:1.9 src/sys/dev/usb/dwc_otgvar.h:1.10
--- src/sys/dev/usb/dwc_otgvar.h:1.9 Thu Jan 31 12:41:41 2013
+++ src/sys/dev/usb/dwc_otgvar.h Mon Feb 4 21:24:07 2013
@@ -1,4 +1,4 @@
-/* $NetBSD: dwc_otgvar.h,v 1.9 2013/01/31 12:41:41 skrll Exp $ */
+/* $NetBSD: dwc_otgvar.h,v 1.10 2013/02/04 21:24:07 skrll Exp $ */
/* $FreeBSD: src/sys/dev/usb/controller/dwc_otg.h,v 1.12 2012/09/27 15:23:38 hselasky Exp $ */
/*-
@@ -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 *);