Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / usb / class / cdc-acm.c
index b72fa49..5e1a253 100644 (file)
@@ -170,6 +170,7 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb)
 {
        wb->use = 0;
        acm->transmitting--;
+       usb_autopm_put_interface_async(acm->control);
 }
 
 /*
@@ -211,9 +212,12 @@ static int acm_write_start(struct acm *acm, int wbn)
        }
 
        dbg("%s susp_count: %d", __func__, acm->susp_count);
+       usb_autopm_get_interface_async(acm->control);
        if (acm->susp_count) {
-               acm->delayed_wb = wb;
-               schedule_work(&acm->waker);
+               if (!acm->delayed_wb)
+                       acm->delayed_wb = wb;
+               else
+                       usb_autopm_put_interface_async(acm->control);
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return 0;       /* A white lie */
        }
@@ -424,7 +428,6 @@ next_buffer:
                throttled = acm->throttle;
                spin_unlock_irqrestore(&acm->throttle_lock, flags);
                if (!throttled) {
-                       tty_buffer_request_room(tty, buf->size);
                        tty_insert_flip_string(tty, buf->base, buf->size);
                        tty_flip_buffer_push(tty);
                } else {
@@ -534,23 +537,6 @@ static void acm_softint(struct work_struct *work)
        tty_kref_put(tty);
 }
 
-static void acm_waker(struct work_struct *waker)
-{
-       struct acm *acm = container_of(waker, struct acm, waker);
-       int rv;
-
-       rv = usb_autopm_get_interface(acm->control);
-       if (rv < 0) {
-               dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
-               return;
-       }
-       if (acm->delayed_wb) {
-               acm_start_wb(acm, acm->delayed_wb);
-               acm->delayed_wb = NULL;
-       }
-       usb_autopm_put_interface(acm->control);
-}
-
 /*
  * TTY handlers
  */
@@ -566,7 +552,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
        acm = acm_table[tty->index];
        if (!acm || !acm->dev)
-               goto err_out;
+               goto out;
        else
                rv = 0;
 
@@ -582,8 +568,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
        mutex_lock(&acm->mutex);
        if (acm->port.count++) {
+               mutex_unlock(&acm->mutex);
                usb_autopm_put_interface(acm->control);
-               goto done;
+               goto out;
        }
 
        acm->ctrlurb->dev = acm->dev;
@@ -612,18 +599,18 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
        set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
        rv = tty_port_block_til_ready(&acm->port, tty, filp);
        tasklet_schedule(&acm->urb_task);
-done:
+
        mutex_unlock(&acm->mutex);
-err_out:
+out:
        mutex_unlock(&open_mutex);
        return rv;
 
 full_bailout:
        usb_kill_urb(acm->ctrlurb);
 bail_out:
-       usb_autopm_put_interface(acm->control);
        acm->port.count--;
        mutex_unlock(&acm->mutex);
+       usb_autopm_put_interface(acm->control);
 early_bail:
        mutex_unlock(&open_mutex);
        tty_port_tty_set(&acm->port, NULL);
@@ -686,15 +673,21 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
 
        /* Perform the closing process and see if we need to do the hardware
           shutdown */
-       if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0)
+       if (!acm)
+               return;
+       if (tty_port_close_start(&acm->port, tty, filp) == 0) {
+               mutex_lock(&open_mutex);
+               if (!acm->dev) {
+                       tty_port_tty_set(&acm->port, NULL);
+                       acm_tty_unregister(acm);
+                       tty->driver_data = NULL;
+               }
+               mutex_unlock(&open_mutex);
                return;
+       }
        acm_port_down(acm, 0);
        tty_port_close_end(&acm->port, tty);
-       mutex_lock(&open_mutex);
        tty_port_tty_set(&acm->port, NULL);
-       if (!acm->dev)
-               acm_tty_unregister(acm);
-       mutex_unlock(&open_mutex);
 }
 
 static int acm_tty_write(struct tty_struct *tty,
@@ -1017,7 +1010,7 @@ static int acm_probe(struct usb_interface *intf,
                case USB_CDC_CALL_MANAGEMENT_TYPE:
                        call_management_function = buffer[3];
                        call_interface_num = buffer[4];
-                       if ((call_management_function & 3) != 3)
+                       if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
                                dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
                        break;
                default:
@@ -1172,7 +1165,6 @@ made_compressed_probe:
        acm->urb_task.func = acm_rx_tasklet;
        acm->urb_task.data = (unsigned long) acm;
        INIT_WORK(&acm->work, acm_softint);
-       INIT_WORK(&acm->waker, acm_waker);
        init_waitqueue_head(&acm->drain_wait);
        spin_lock_init(&acm->throttle_lock);
        spin_lock_init(&acm->write_lock);
@@ -1337,7 +1329,6 @@ static void stop_data_traffic(struct acm *acm)
        tasklet_enable(&acm->urb_task);
 
        cancel_work_sync(&acm->work);
-       cancel_work_sync(&acm->waker);
 }
 
 static void acm_disconnect(struct usb_interface *intf)
@@ -1429,6 +1420,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;
        int rv = 0;
        int cnt;
 
@@ -1443,6 +1435,21 @@ static int acm_resume(struct usb_interface *intf)
        mutex_lock(&acm->mutex);
        if (acm->port.count) {
                rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
+
+               spin_lock_irq(&acm->write_lock);
+               if (acm->delayed_wb) {
+                       wb = acm->delayed_wb;
+                       acm->delayed_wb = NULL;
+                       spin_unlock_irq(&acm->write_lock);
+                       acm_start_wb(acm, wb);
+               } else {
+                       spin_unlock_irq(&acm->write_lock);
+               }
+
+               /*
+                * delayed error checking because we must
+                * do the write path at all cost
+                */
                if (rv < 0)
                        goto err_out;
 
@@ -1454,12 +1461,35 @@ err_out:
        return rv;
 }
 
+static int acm_reset_resume(struct usb_interface *intf)
+{
+       struct acm *acm = usb_get_intfdata(intf);
+       struct tty_struct *tty;
+
+       mutex_lock(&acm->mutex);
+       if (acm->port.count) {
+               tty = tty_port_tty_get(&acm->port);
+               if (tty) {
+                       tty_hangup(tty);
+                       tty_kref_put(tty);
+               }
+       }
+       mutex_unlock(&acm->mutex);
+       return acm_resume(intf);
+}
+
 #endif /* CONFIG_PM */
+
+#define NOKIA_PCSUITE_ACM_INFO(x) \
+               USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
+               USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
+               USB_CDC_ACM_PROTO_VENDOR)
+
 /*
  * USB driver structure.
  */
 
-static struct usb_device_id acm_ids[] = {
+static const struct usb_device_id acm_ids[] = {
        /* quirky and broken devices */
        { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
@@ -1512,6 +1542,65 @@ static struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
        .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
        },
+       { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
+       .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+       },
+
+       /* Nokia S60 phones expose two ACM channels. The first is
+        * a modem and is picked up by the standard AT-command
+        * information below. The second is 'vendor-specific' but
+        * is treated as a serial device at the S60 end, so we want
+        * to expose it on Linux too. */
+       { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
+       { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
+       { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
+       { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
+       { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
+       { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
+       { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
+       { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
+       { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
+       { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
+       { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
+       { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
+       { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
+       { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
+       { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
+       { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
+       { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
+       { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
+       { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
+       { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
+       { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
+       { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
+       { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
+       { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
+       { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
+       { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
+       { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
+       { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
+       { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
+       { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
+       { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
+       { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
+
+       /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
+
+       /* Support Lego NXT using pbLua firmware */
+       { USB_DEVICE(0x0694, 0xff00),
+       .driver_info = NOT_A_MODEM,
+               },
 
        /* control interfaces with various AT-command sets */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
@@ -1527,7 +1616,6 @@ static struct usb_device_id acm_ids[] = {
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
                USB_CDC_ACM_PROTO_AT_CDMA) },
 
-       /* NOTE:  COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
        { }
 };
 
@@ -1540,6 +1628,7 @@ static struct usb_driver acm_driver = {
 #ifdef CONFIG_PM
        .suspend =      acm_suspend,
        .resume =       acm_resume,
+       .reset_resume = acm_reset_resume,
 #endif
        .id_table =     acm_ids,
 #ifdef CONFIG_PM