USB: cdc-acm: fix write and suspend race
[pandora-kernel.git] / drivers / usb / class / cdc-acm.c
index 7881832..456ec17 100644 (file)
@@ -760,10 +760,6 @@ static const __u32 acm_tty_speed[] = {
        2500000, 3000000, 3500000, 4000000
 };
 
-static const __u8 acm_tty_size[] = {
-       5, 6, 7, 8
-};
-
 static void acm_tty_set_termios(struct tty_struct *tty,
                                                struct ktermios *termios_old)
 {
@@ -780,7 +776,21 @@ static void acm_tty_set_termios(struct tty_struct *tty,
        newline.bParityType = termios->c_cflag & PARENB ?
                                (termios->c_cflag & PARODD ? 1 : 2) +
                                (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
-       newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               newline.bDataBits = 5;
+               break;
+       case CS6:
+               newline.bDataBits = 6;
+               break;
+       case CS7:
+               newline.bDataBits = 7;
+               break;
+       case CS8:
+       default:
+               newline.bDataBits = 8;
+               break;
+       }
        /* FIXME: Needs to clear unsupported bits in the termios */
        acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
@@ -1328,18 +1338,15 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
        struct acm *acm = usb_get_intfdata(intf);
        int cnt;
 
+       spin_lock_irq(&acm->read_lock);
+       spin_lock(&acm->write_lock);
        if (PMSG_IS_AUTO(message)) {
-               int b;
-
-               spin_lock_irq(&acm->write_lock);
-               b = acm->transmitting;
-               spin_unlock_irq(&acm->write_lock);
-               if (b)
+               if (acm->transmitting) {
+                       spin_unlock(&acm->write_lock);
+                       spin_unlock_irq(&acm->read_lock);
                        return -EBUSY;
+               }
        }
-
-       spin_lock_irq(&acm->read_lock);
-       spin_lock(&acm->write_lock);
        cnt = acm->susp_count++;
        spin_unlock(&acm->write_lock);
        spin_unlock_irq(&acm->read_lock);
@@ -1438,6 +1445,8 @@ static int acm_reset_resume(struct usb_interface *intf)
 
 static const struct usb_device_id acm_ids[] = {
        /* quirky and broken devices */
+       { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
+       .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
        { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
@@ -1481,13 +1490,27 @@ static const struct usb_device_id acm_ids[] = {
        },
        /* Motorola H24 HSPA module: */
        { USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */
-       { USB_DEVICE(0x22b8, 0x2d92) }, /* modem           + diagnostics        */
-       { USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port                      */
-       { USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics        */
-       { USB_DEVICE(0x22b8, 0x2d96) }, /* modem                         + NMEA */
-       { USB_DEVICE(0x22b8, 0x2d97) }, /* modem           + diagnostics + NMEA */
-       { USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port               + NMEA */
-       { USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */
+       { USB_DEVICE(0x22b8, 0x2d92),   /* modem           + diagnostics        */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d93),   /* modem + AT port                      */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d95),   /* modem + AT port + diagnostics        */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d96),   /* modem                         + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d97),   /* modem           + diagnostics + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d99),   /* modem + AT port               + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d9a),   /* modem + AT port + diagnostics + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
 
        { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
        .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
@@ -1496,6 +1519,12 @@ static const struct usb_device_id acm_ids[] = {
                                           Maybe we should define a new
                                           quirk for this. */
        },
+       { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
+       .driver_info = NO_UNION_NORMAL,
+       },
+       { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
+       .driver_info = NO_UNION_NORMAL,
+       },
        { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
        .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
        },