staging: dwc2: always release host channel after dequeueing
authorMatthijs Kooijman <matthijs@stdin.nl>
Mon, 25 Mar 2013 19:00:25 +0000 (12:00 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 25 Mar 2013 23:32:46 +0000 (16:32 -0700)
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>
drivers/staging/dwc2/hcd_intr.c

index 4b007ab..8b68df8 100644 (file)
@@ -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,