aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/dwc2
diff options
context:
space:
mode:
authorMatthijs Kooijman <matthijs@stdin.nl>2013-03-25 12:00:25 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-25 16:32:46 -0700
commit8509f2f43a2b245ade076d1568daa2cf15446732 (patch)
treedd9c82aaaf62afb143758b7eb2c6dbc8b70bffbd /drivers/staging/dwc2
parent257f65801520a5c888930d410f781d408a405990 (diff)
staging: dwc2: always release host channel after dequeueing
Previously, when an active urb was dequeued, its host channel would not always be released. There is some special handling for this in dwc2_hc_chhltd_intr_dma, but when it was the last urb/qtd in its qh, a safeguard in dwc2_hc_n_intr would short-circuit and prevent the regular interrupt handlers from running, without releasing the channel. This is easily triggered when using a 3G modem using the option driver. Opening and closing any ttyUSBx device will eat up a host channel that is forever unusable from that point on. Signed-off-by: Matthijs Kooijman <matthijs@stdin.nl> [paulz@synopsys.com: fixed comment style and added a couple of NULL checks] Signed-off-by: Paul Zimmerman <paulz@synopsys.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/dwc2')
-rw-r--r--drivers/staging/dwc2/hcd_intr.c33
1 files changed, 29 insertions, 4 deletions
diff --git a/drivers/staging/dwc2/hcd_intr.c b/drivers/staging/dwc2/hcd_intr.c
index 4b007ab3649..8b68df89e33 100644
--- a/drivers/staging/dwc2/hcd_intr.c
+++ b/drivers/staging/dwc2/hcd_intr.c
@@ -708,7 +708,7 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
free_qtd = 1;
break;
case DWC2_HC_XFER_XACT_ERR:
- if (qtd->error_count >= 3) {
+ if (qtd && qtd->error_count >= 3) {
dev_vdbg(hsotg->dev,
" Complete URB with transaction error\n");
free_qtd = 1;
@@ -729,7 +729,7 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
case DWC2_HC_XFER_PERIODIC_INCOMPLETE:
dev_vdbg(hsotg->dev, " Complete URB with I/O error\n");
free_qtd = 1;
- if (qtd->urb) {
+ if (qtd && qtd->urb) {
qtd->urb->status = -EIO;
dwc2_host_complete(hsotg, qtd->urb->priv, qtd->urb,
-EIO);
@@ -1708,8 +1708,9 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev,
"hcint 0x%08x, hcintmsk 0x%08x, hcsplt 0x%08x,\n",
chan->hcint, hcintmsk, hcsplt);
- dev_dbg(hsotg->dev, "qtd->complete_split %d\n",
- qtd->complete_split);
+ if (qtd)
+ dev_dbg(hsotg->dev, "qtd->complete_split %d\n",
+ qtd->complete_split);
dev_warn(hsotg->dev,
"%s: no halt status, channel %d, ignoring interrupt\n",
__func__, chnum);
@@ -1937,7 +1938,31 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
chan->hcint = hcint;
hcint &= hcintmsk;
+ /*
+ * If the channel was halted due to a dequeue, the qtd list might
+ * be empty or at least the first entry will not be the active qtd.
+ * In this case, take a shortcut and just release the channel.
+ */
+ if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) {
+ /*
+ * If the channel was halted, this should be the only
+ * interrupt unmasked
+ */
+ WARN_ON(hcint != HCINTMSK_CHHLTD);
+ if (hsotg->core_params->dma_desc_enable > 0)
+ dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
+ chan->halt_status);
+ else
+ dwc2_release_channel(hsotg, chan, NULL,
+ chan->halt_status);
+ return;
+ }
+
if (list_empty(&chan->qh->qtd_list)) {
+ /*
+ * TODO: Will this ever happen with the
+ * DWC2_HC_XFER_URB_DEQUEUE handling above?
+ */
dev_dbg(hsotg->dev, "## no QTD queued for channel %d ##\n",
chnum);
dev_dbg(hsotg->dev,