Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / usb / class / cdc-acm.c
index 751e6ad153baeecd160fb9ce4dae6302d9bfcc7d..fbf3f11aed2c6c2914e8c7c0ea9fdf2f2e1465a1 100644 (file)
@@ -122,13 +122,23 @@ static void acm_release_minor(struct acm *acm)
 static int acm_ctrl_msg(struct acm *acm, int request, int value,
                                                        void *buf, int len)
 {
-       int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+       int retval;
+
+       retval = usb_autopm_get_interface(acm->control);
+       if (retval)
+               return retval;
+
+       retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
                request, USB_RT_ACM, value,
                acm->control->altsetting[0].desc.bInterfaceNumber,
                buf, len, 5000);
+
        dev_dbg(&acm->control->dev,
                        "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
                        __func__, request, value, len, retval);
+
+       usb_autopm_put_interface(acm->control);
+
        return retval < 0 ? retval : 0;
 }
 
@@ -233,12 +243,9 @@ static int acm_write_start(struct acm *acm, int wbn)
                                                        acm->susp_count);
        usb_autopm_get_interface_async(acm->control);
        if (acm->susp_count) {
-               if (!acm->delayed_wb)
-                       acm->delayed_wb = wb;
-               else
-                       usb_autopm_put_interface_async(acm->control);
+               usb_anchor_urb(wb->urb, &acm->delayed);
                spin_unlock_irqrestore(&acm->write_lock, flags);
-               return 0;       /* A white lie */
+               return 0;
        }
        usb_mark_last_busy(acm->dev);
 
@@ -516,6 +523,7 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
 {
        struct acm *acm = container_of(port, struct acm, port);
        int retval = -ENODEV;
+       int i;
 
        dev_dbg(&acm->control->dev, "%s\n", __func__);
 
@@ -564,6 +572,8 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
        return 0;
 
 error_submit_read_urbs:
+       for (i = 0; i < acm->rx_buflimit; i++)
+               usb_kill_urb(acm->read_urbs[i]);
        acm->ctrlout = 0;
        acm_set_control(acm, acm->ctrlout);
 error_set_control:
@@ -591,21 +601,35 @@ static void acm_port_destruct(struct tty_port *port)
 static void acm_port_shutdown(struct tty_port *port)
 {
        struct acm *acm = container_of(port, struct acm, port);
+       struct urb *urb;
+       struct acm_wb *wb;
        int i;
+       int pm_err;
 
        dev_dbg(&acm->control->dev, "%s\n", __func__);
 
        mutex_lock(&acm->mutex);
        if (!acm->disconnected) {
-               usb_autopm_get_interface(acm->control);
+               pm_err = usb_autopm_get_interface(acm->control);
                acm_set_control(acm, acm->ctrlout = 0);
+
+               for (;;) {
+                       urb = usb_get_from_anchor(&acm->delayed);
+                       if (!urb)
+                               break;
+                       wb = urb->context;
+                       wb->use = 0;
+                       usb_autopm_put_interface_async(acm->control);
+               }
+
                usb_kill_urb(acm->ctrlurb);
                for (i = 0; i < ACM_NW; i++)
                        usb_kill_urb(acm->wb[i].urb);
                for (i = 0; i < acm->rx_buflimit; i++)
                        usb_kill_urb(acm->read_urbs[i]);
                acm->control->needs_remote_wakeup = 0;
-               usb_autopm_put_interface(acm->control);
+               if (!pm_err)
+                       usb_autopm_put_interface(acm->control);
        }
        mutex_unlock(&acm->mutex);
 }
@@ -1190,6 +1214,7 @@ made_compressed_probe:
                acm->bInterval = epread->bInterval;
        tty_port_init(&acm->port);
        acm->port.ops = &acm_port_ops;
+       init_usb_anchor(&acm->delayed);
 
        buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
        if (!buf) {
@@ -1450,8 +1475,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
        if (cnt)
                return 0;
 
-       if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
-               stop_data_traffic(acm);
+       stop_data_traffic(acm);
 
        return 0;
 }
@@ -1459,7 +1483,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 static int acm_resume(struct usb_interface *intf)
 {
        struct acm *acm = usb_get_intfdata(intf);
-       struct acm_wb *wb;
+       struct urb *urb;
        int rv = 0;
 
        spin_lock_irq(&acm->read_lock);
@@ -1471,10 +1495,12 @@ static int acm_resume(struct usb_interface *intf)
        if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
                rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
 
-               if (acm->delayed_wb) {
-                       wb = acm->delayed_wb;
-                       acm->delayed_wb = NULL;
-                       acm_start_wb(acm, wb);
+               for (;;) {
+                       urb = usb_get_from_anchor(&acm->delayed);
+                       if (!urb)
+                               break;
+
+                       acm_start_wb(acm, urb->context);
                }
 
                /*