revert android-tegra-2.6.36-honeycomb-mr1-9001adc to v2.6.36
[firefly-linux-kernel-4.4.55.git] / drivers / isdn / gigaset / bas-gigaset.c
index 131976d880d08b2a37a3e5f157d1918fbf3ba6b2..707d9c94cf9e0ed475e108d9d3037402322c34cb 100644 (file)
@@ -438,27 +438,23 @@ static void cmd_in_timeout(unsigned long data)
                return;
        }
 
-       if (ucs->retry_cmd_in++ >= BAS_RETRY) {
+       if (ucs->retry_cmd_in++ < BAS_RETRY) {
+               dev_notice(cs->dev, "control read: timeout, retry %d\n",
+                          ucs->retry_cmd_in);
+               rc = atread_submit(cs, BAS_TIMEOUT);
+               if (rc >= 0 || rc == -ENODEV)
+                       /* resubmitted or disconnected */
+                       /* - bypass regular exit block */
+                       return;
+       } else {
                dev_err(cs->dev,
                        "control read: timeout, giving up after %d tries\n",
                        ucs->retry_cmd_in);
-               kfree(ucs->rcvbuf);
-               ucs->rcvbuf = NULL;
-               ucs->rcvbuf_size = 0;
-               error_reset(cs);
-               return;
-       }
-
-       gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d",
-               __func__, ucs->retry_cmd_in);
-       rc = atread_submit(cs, BAS_TIMEOUT);
-       if (rc < 0) {
-               kfree(ucs->rcvbuf);
-               ucs->rcvbuf = NULL;
-               ucs->rcvbuf_size = 0;
-               if (rc != -ENODEV)
-                       error_reset(cs);
        }
+       kfree(ucs->rcvbuf);
+       ucs->rcvbuf = NULL;
+       ucs->rcvbuf_size = 0;
+       error_reset(cs);
 }
 
 /* read_ctrl_callback
@@ -474,11 +470,18 @@ static void read_ctrl_callback(struct urb *urb)
        struct cardstate *cs = inbuf->cs;
        struct bas_cardstate *ucs = cs->hw.bas;
        int status = urb->status;
+       int have_data = 0;
        unsigned numbytes;
        int rc;
 
        update_basstate(ucs, 0, BS_ATRDPEND);
        wake_up(&ucs->waitqueue);
+
+       if (!ucs->rcvbuf_size) {
+               dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
+               return;
+       }
+
        del_timer(&ucs->timer_cmd_in);
 
        switch (status) {
@@ -492,10 +495,19 @@ static void read_ctrl_callback(struct urb *urb)
                                numbytes = ucs->rcvbuf_size;
                }
 
-               /* copy received bytes to inbuf, notify event layer */
-               if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) {
-                       gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
-                       gigaset_schedule_event(cs);
+               /* copy received bytes to inbuf */
+               have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes);
+
+               if (unlikely(numbytes < ucs->rcvbuf_size)) {
+                       /* incomplete - resubmit for remaining bytes */
+                       ucs->rcvbuf_size -= numbytes;
+                       ucs->retry_cmd_in = 0;
+                       rc = atread_submit(cs, BAS_TIMEOUT);
+                       if (rc >= 0 || rc == -ENODEV)
+                               /* resubmitted or disconnected */
+                               /* - bypass regular exit block */
+                               return;
+                       error_reset(cs);
                }
                break;
 
@@ -504,32 +516,37 @@ static void read_ctrl_callback(struct urb *urb)
        case -EINPROGRESS:              /* pending */
        case -ENODEV:                   /* device removed */
        case -ESHUTDOWN:                /* device shut down */
-               /* no further action necessary */
+               /* no action necessary */
                gig_dbg(DEBUG_USBREQ, "%s: %s",
                        __func__, get_usb_statmsg(status));
                break;
 
-       default:                        /* other errors: retry */
+       default:                        /* severe trouble */
+               dev_warn(cs->dev, "control read: %s\n",
+                        get_usb_statmsg(status));
                if (ucs->retry_cmd_in++ < BAS_RETRY) {
-                       gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__,
-                               get_usb_statmsg(status), ucs->retry_cmd_in);
+                       dev_notice(cs->dev, "control read: retry %d\n",
+                                  ucs->retry_cmd_in);
                        rc = atread_submit(cs, BAS_TIMEOUT);
-                       if (rc >= 0)
-                               /* successfully resubmitted, skip freeing */
+                       if (rc >= 0 || rc == -ENODEV)
+                               /* resubmitted or disconnected */
+                               /* - bypass regular exit block */
                                return;
-                       if (rc == -ENODEV)
-                               /* disconnect, no further action necessary */
-                               break;
+               } else {
+                       dev_err(cs->dev,
+                               "control read: giving up after %d tries\n",
+                               ucs->retry_cmd_in);
                }
-               dev_err(cs->dev, "control read: %s, giving up after %d tries\n",
-                       get_usb_statmsg(status), ucs->retry_cmd_in);
                error_reset(cs);
        }
 
-       /* read finished, free buffer */
        kfree(ucs->rcvbuf);
        ucs->rcvbuf = NULL;
        ucs->rcvbuf_size = 0;
+       if (have_data) {
+               gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+               gigaset_schedule_event(cs);
+       }
 }
 
 /* atread_submit
@@ -1581,13 +1598,13 @@ static int gigaset_init_bchannel(struct bc_state *bcs)
 
        ret = starturbs(bcs);
        if (ret < 0) {
-               spin_unlock_irqrestore(&cs->lock, flags);
                dev_err(cs->dev,
                        "could not start isochronous I/O for channel B%d: %s\n",
                        bcs->channel + 1,
                        ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret));
                if (ret != -ENODEV)
                        error_hangup(bcs);
+               spin_unlock_irqrestore(&cs->lock, flags);
                return ret;
        }
 
@@ -1597,11 +1614,11 @@ static int gigaset_init_bchannel(struct bc_state *bcs)
                dev_err(cs->dev, "could not open channel B%d\n",
                        bcs->channel + 1);
                stopurbs(bcs->hw.bas);
+               if (ret != -ENODEV)
+                       error_hangup(bcs);
        }
 
        spin_unlock_irqrestore(&cs->lock, flags);
-       if (ret < 0 && ret != -ENODEV)
-               error_hangup(bcs);
        return ret;
 }