cdc-acm: prevent infinite loop when parsing CDC headers.
[pandora-kernel.git] / drivers / usb / class / cdc-acm.c
index e8c564a..57d6302 100644 (file)
@@ -72,13 +72,23 @@ static const struct tty_port_operations acm_port_ops = {
 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;
 }
 
@@ -181,14 +191,17 @@ static int acm_write_start(struct acm *acm, int wbn)
 
        dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
                                                        acm->susp_count);
-       usb_autopm_get_interface_async(acm->control);
+       rc = usb_autopm_get_interface_async(acm->control);
+       if (rc) {
+               wb->use = 0;
+               spin_unlock_irqrestore(&acm->write_lock, flags);
+               return rc;
+       }
+
        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);
 
@@ -498,6 +511,14 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
        usb_autopm_put_interface(acm->control);
 
+       /*
+        * Unthrottle device in case the TTY was closed while throttled.
+        */
+       spin_lock_irq(&acm->read_lock);
+       acm->throttled = 0;
+       acm->throttle_req = 0;
+       spin_unlock_irq(&acm->read_lock);
+
        if (acm_submit_read_urbs(acm, GFP_KERNEL))
                goto bail_out;
 
@@ -537,11 +558,23 @@ static void acm_tty_unregister(struct acm *acm)
 
 static void acm_port_down(struct acm *acm)
 {
+       struct urb *urb;
+       struct acm_wb *wb;
        int i;
 
        if (acm->dev) {
                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);
@@ -554,10 +587,18 @@ static void acm_port_down(struct acm *acm)
 
 static void acm_tty_hangup(struct tty_struct *tty)
 {
-       struct acm *acm = tty->driver_data;
-       tty_port_hangup(&acm->port);
+       struct acm *acm;
+
        mutex_lock(&open_mutex);
+       acm = tty->driver_data;
+
+       if (!acm)
+               goto out;
+
+       tty_port_hangup(&acm->port);
        acm_port_down(acm);
+
+out:
        mutex_unlock(&open_mutex);
 }
 
@@ -744,10 +785,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)
 {
@@ -764,15 +801,30 @@ 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);
 
-       if (!newline.dwDTERate) {
+       if (C_BAUD(tty) == B0) {
                newline.dwDTERate = acm->line.dwDTERate;
                newctrl &= ~ACM_CTRL_DTR;
-       } else
+       } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
                newctrl |=  ACM_CTRL_DTR;
+       }
 
        if (newctrl != acm->ctrlout)
                acm_set_control(acm, acm->ctrlout = newctrl);
@@ -859,6 +911,7 @@ static int acm_probe(struct usb_interface *intf,
        unsigned long quirks;
        int num_rx_buf;
        int i;
+       unsigned int elength = 0;
        int combined_interfaces = 0;
 
        /* normal quirks */
@@ -894,6 +947,12 @@ static int acm_probe(struct usb_interface *intf,
        }
 
        while (buflen > 0) {
+               elength = buffer[0];
+               if (!elength) {
+                       dev_err(&intf->dev, "skipping garbage byte\n");
+                       elength = 1;
+                       goto next_desc;
+               }
                if (buffer[1] != USB_DT_CS_INTERFACE) {
                        dev_err(&intf->dev, "skipping garbage\n");
                        goto next_desc;
@@ -901,6 +960,8 @@ static int acm_probe(struct usb_interface *intf,
 
                switch (buffer[2]) {
                case USB_CDC_UNION_TYPE: /* we've found it */
+                       if (elength < sizeof(struct usb_cdc_union_desc))
+                               goto next_desc;
                        if (union_header) {
                                dev_err(&intf->dev, "More than one "
                                        "union descriptor, skipping ...\n");
@@ -909,31 +970,38 @@ static int acm_probe(struct usb_interface *intf,
                        union_header = (struct usb_cdc_union_desc *)buffer;
                        break;
                case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+                       if (elength < sizeof(struct usb_cdc_country_functional_desc))
+                               goto next_desc;
                        cfd = (struct usb_cdc_country_functional_desc *)buffer;
                        break;
                case USB_CDC_HEADER_TYPE: /* maybe check version */
                        break; /* for now we ignore it */
                case USB_CDC_ACM_TYPE:
+                       if (elength < 4)
+                               goto next_desc;
                        ac_management_function = buffer[3];
                        break;
                case USB_CDC_CALL_MANAGEMENT_TYPE:
+                       if (elength < 5)
+                               goto next_desc;
                        call_management_function = buffer[3];
                        call_interface_num = buffer[4];
                        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:
-                       /* there are LOTS more CDC descriptors that
+                       /*
+                        * there are LOTS more CDC descriptors that
                         * could legitimately be found here.
                         */
                        dev_dbg(&intf->dev, "Ignoring descriptor: "
-                                       "type %02x, length %d\n",
-                                       buffer[2], buffer[0]);
+                                       "type %02x, length %ud\n",
+                                       buffer[2], elength);
                        break;
                }
 next_desc:
-               buflen -= buffer[0];
-               buffer += buffer[0];
+               buflen -= elength;
+               buffer += elength;
        }
 
        if (!union_header) {
@@ -959,10 +1027,11 @@ next_desc:
        } else {
                control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
                data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
-               if (!control_interface || !data_interface) {
-                       dev_dbg(&intf->dev, "no interfaces\n");
-                       return -ENODEV;
-               }
+       }
+
+       if (!control_interface || !data_interface) {
+               dev_dbg(&intf->dev, "no interfaces\n");
+               return -ENODEV;
        }
 
        if (data_interface_num != call_interface_num)
@@ -1027,7 +1096,8 @@ skip_normal_probe:
        }
 
 
-       if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
+       if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
+           control_interface->cur_altsetting->desc.bNumEndpoints == 0)
                return -EINVAL;
 
        epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
@@ -1085,6 +1155,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) {
@@ -1155,7 +1226,7 @@ made_compressed_probe:
 
                if (usb_endpoint_xfer_int(epwrite))
                        usb_fill_int_urb(snd->urb, usb_dev,
-                               usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
+                               usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
                                NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
                else
                        usb_fill_bulk_urb(snd->urb, usb_dev,
@@ -1183,6 +1254,8 @@ made_compressed_probe:
                i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
                if (i < 0) {
                        kfree(acm->country_codes);
+                       acm->country_codes = NULL;
+                       acm->country_code_size = 0;
                        goto skip_countries;
                }
 
@@ -1191,6 +1264,8 @@ made_compressed_probe:
                if (i < 0) {
                        device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
                        kfree(acm->country_codes);
+                       acm->country_codes = NULL;
+                       acm->country_code_size = 0;
                        goto skip_countries;
                }
        }
@@ -1307,18 +1382,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);
@@ -1341,30 +1413,25 @@ 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;
-       int cnt;
 
+       mutex_lock(&acm->mutex);
        spin_lock_irq(&acm->read_lock);
-       acm->susp_count -= 1;
-       cnt = acm->susp_count;
-       spin_unlock_irq(&acm->read_lock);
+       spin_lock(&acm->write_lock);
 
-       if (cnt)
-               return 0;
+       if (--acm->susp_count)
+               goto out;
 
-       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);
+               rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
+
+               for (;;) {
+                       urb = usb_get_from_anchor(&acm->delayed);
+                       if (!urb)
+                               break;
+
+                       acm_start_wb(acm, urb->context);
                }
 
                /*
@@ -1372,13 +1439,15 @@ static int acm_resume(struct usb_interface *intf)
                 * do the write path at all cost
                 */
                if (rv < 0)
-                       goto err_out;
+                       goto out;
 
-               rv = acm_submit_read_urbs(acm, GFP_NOIO);
+               rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
        }
-
-err_out:
+out:
+       spin_unlock(&acm->write_lock);
+       spin_unlock_irq(&acm->read_lock);
        mutex_unlock(&acm->mutex);
+
        return rv;
 }
 
@@ -1417,6 +1486,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 */
        },
@@ -1456,8 +1527,33 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
+       { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
        { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
        },
+       /* Motorola H24 HSPA module: */
+       { USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */
+       { 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
                                           data interface instead of
@@ -1465,6 +1561,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 */
        },