Merge commit 'v3.4.4' into android-tegra-nv-3.4
[linux-2.6.git] / drivers / usb / class / cdc-acm.c
index 640cf79..5be18b8 100644 (file)
@@ -206,7 +206,6 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
        wb->urb->transfer_dma = wb->dmah;
        wb->urb->transfer_buffer_length = wb->len;
        wb->urb->dev = acm->dev;
-
        rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
        if (rc < 0) {
                dev_err(&acm->data->dev,
@@ -222,6 +221,9 @@ static int acm_write_start(struct acm *acm, int wbn)
        unsigned long flags;
        struct acm_wb *wb = &acm->wb[wbn];
        int rc;
+#ifdef CONFIG_PM
+       struct urb *res;
+#endif
 
        spin_lock_irqsave(&acm->write_lock, flags);
        if (!acm->dev) {
@@ -234,15 +236,35 @@ static int acm_write_start(struct acm *acm, int wbn)
                                                        acm->susp_count);
        usb_autopm_get_interface_async(acm->control);
        if (acm->susp_count) {
+#ifdef CONFIG_PM
+               acm->transmitting++;
+               wb->urb->transfer_buffer = wb->buf;
+               wb->urb->transfer_dma = wb->dmah;
+               wb->urb->transfer_buffer_length = wb->len;
+               wb->urb->dev = acm->dev;
+               usb_anchor_urb(wb->urb, &acm->deferred);
+#else
                if (!acm->delayed_wb)
                        acm->delayed_wb = wb;
                else
                        usb_autopm_put_interface_async(acm->control);
+#endif
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return 0;       /* A white lie */
        }
        usb_mark_last_busy(acm->dev);
-
+#ifdef CONFIG_PM
+       while ((res = usb_get_from_anchor(&acm->deferred))) {
+               /* decrement ref count*/
+               usb_put_urb(res);
+               rc = usb_submit_urb(res, GFP_ATOMIC);
+               if (rc < 0) {
+                       dbg("usb_submit_urb(pending request) failed: %d", rc);
+                       usb_unanchor_urb(res);
+                       acm_write_done(acm, res->context);
+               }
+       }
+#endif
        rc = acm_start_wb(acm, wb);
        spin_unlock_irqrestore(&acm->write_lock, flags);
 
@@ -551,7 +573,10 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
         * memory is really nasty...
         */
        set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
-       acm->control->needs_remote_wakeup = 1;
+       acm->control->needs_remote_wakeup = 0;
+
+       if (acm_submit_read_urbs(acm, GFP_KERNEL))
+               goto error_submit_urb;
 
        acm->ctrlurb->dev = acm->dev;
        if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
@@ -942,8 +967,12 @@ static int acm_probe(struct usb_interface *intf,
        quirks = (unsigned long)id->driver_info;
        num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
 
+       /* not a real CDC ACM device */
+       if (quirks & NOT_REAL_ACM)
+               return -ENODEV;
+
        /* handle quirks deadly to normal probing*/
-       if (quirks == NO_UNION_NORMAL) {
+       if (quirks & NO_UNION_NORMAL) {
                data_interface = usb_ifnum_to_if(usb_dev, 1);
                control_interface = usb_ifnum_to_if(usb_dev, 0);
                goto skip_normal_probe;
@@ -1154,6 +1183,7 @@ made_compressed_probe:
        acm->readsize = readsize;
        acm->rx_buflimit = num_rx_buf;
        INIT_WORK(&acm->work, acm_softint);
+       init_usb_anchor(&acm->deferred);
        spin_lock_init(&acm->write_lock);
        spin_lock_init(&acm->read_lock);
        mutex_init(&acm->mutex);
@@ -1161,6 +1191,8 @@ made_compressed_probe:
        acm->is_int_ep = usb_endpoint_xfer_int(epread);
        if (acm->is_int_ep)
                acm->bInterval = epread->bInterval;
+       if (quirks & NO_HANGUP_IN_RESET_RESUME)
+               acm->no_hangup_in_reset_resume = 1;
        tty_port_init(&acm->port);
        acm->port.ops = &acm_port_ops;
 
@@ -1324,6 +1356,11 @@ static void stop_data_traffic(struct acm *acm)
 {
        int i;
 
+       if (!acm) {
+               pr_err("%s: !acm\n", __func__);
+               return;
+       }
+
        dev_dbg(&acm->control->dev, "%s\n", __func__);
 
        usb_kill_urb(acm->ctrlurb);
@@ -1340,6 +1377,7 @@ static void acm_disconnect(struct usb_interface *intf)
        struct acm *acm = usb_get_intfdata(intf);
        struct usb_device *usb_dev = interface_to_usbdev(intf);
        struct tty_struct *tty;
+       struct urb *res;
        int i;
 
        dev_dbg(&intf->dev, "%s\n", __func__);
@@ -1369,6 +1407,9 @@ static void acm_disconnect(struct usb_interface *intf)
 
        stop_data_traffic(acm);
 
+       /* decrement ref count of anchored urbs */
+       while ((res = usb_get_from_anchor(&acm->deferred)))
+               usb_put_urb(res);
        usb_free_urb(acm->ctrlurb);
        for (i = 0; i < ACM_NW; i++)
                usb_free_urb(acm->wb[i].urb);
@@ -1391,6 +1432,11 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
        struct acm *acm = usb_get_intfdata(intf);
        int cnt;
 
+       if (!acm) {
+               pr_err("%s: !acm\n", __func__);
+               return -ENODEV;
+       }
+
        if (PMSG_IS_AUTO(message)) {
                int b;
 
@@ -1419,13 +1465,27 @@ 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;
        int rv = 0;
        int cnt;
+#ifdef CONFIG_PM
+       struct urb *res;
+#else
+       struct acm_wb *wb;
+#endif
+
+       if (!acm) {
+               pr_err("%s: !acm\n", __func__);
+               return -ENODEV;
+       }
 
        spin_lock_irq(&acm->read_lock);
-       acm->susp_count -= 1;
-       cnt = acm->susp_count;
+       if (acm->susp_count > 0) {
+               acm->susp_count -= 1;
+               cnt = acm->susp_count;
+       } else {
+               spin_unlock_irq(&acm->read_lock);
+               return 0;
+       }
        spin_unlock_irq(&acm->read_lock);
 
        if (cnt)
@@ -1433,8 +1493,21 @@ static int acm_resume(struct usb_interface *intf)
 
        if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
                rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
-
                spin_lock_irq(&acm->write_lock);
+#ifdef CONFIG_PM
+               while ((res = usb_get_from_anchor(&acm->deferred))) {
+                       /* decrement ref count*/
+                       usb_put_urb(res);
+                       rv = usb_submit_urb(res, GFP_ATOMIC);
+                       if (rv < 0) {
+                               dbg("usb_submit_urb(pending request)"
+                                       " failed: %d", rv);
+                               usb_unanchor_urb(res);
+                               acm_write_done(acm, res->context);
+                       }
+               }
+               spin_unlock_irq(&acm->write_lock);
+#else
                if (acm->delayed_wb) {
                        wb = acm->delayed_wb;
                        acm->delayed_wb = NULL;
@@ -1443,6 +1516,7 @@ static int acm_resume(struct usb_interface *intf)
                } else {
                        spin_unlock_irq(&acm->write_lock);
                }
+#endif
 
                /*
                 * delayed error checking because we must
@@ -1463,10 +1537,16 @@ static int acm_reset_resume(struct usb_interface *intf)
        struct acm *acm = usb_get_intfdata(intf);
        struct tty_struct *tty;
 
+       if (!acm) {
+               pr_err("%s: !acm\n", __func__);
+               return -ENODEV;
+       }
+
        if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
                tty = tty_port_tty_get(&acm->port);
                if (tty) {
-                       tty_hangup(tty);
+                       if (!acm->no_hangup_in_reset_resume)
+                               tty_hangup(tty);
                        tty_kref_put(tty);
                }
        }
@@ -1556,6 +1636,9 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
        .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
        },
+       { USB_DEVICE(0x1519, 0x0020),
+       .driver_info = NO_UNION_NORMAL | NO_HANGUP_IN_RESET_RESUME, /* has no union descriptor */
+       },
 
        /* Nokia S60 phones expose two ACM channels. The first is
         * a modem and is picked up by the standard AT-command
@@ -1636,6 +1719,16 @@ static const struct usb_device_id acm_ids[] = {
        .driver_info = NO_DATA_INTERFACE,
        },
 
+       /* Exclude XMM6260 boot rom (not running modem software yet) */
+       { USB_DEVICE(0x058b, 0x0041),
+       .driver_info = NOT_REAL_ACM,
+       },
+
+       /* Icera 450 */
+       { USB_DEVICE(0x1983, 0x0321),
+       .driver_info = NO_HANGUP_IN_RESET_RESUME,
+       },
+
        /* control interfaces without any protocol set */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
                USB_CDC_PROTO_NONE) },