usb: cdc-acm: fix cooldown mechanism
[platform/kernel/linux-starfive.git] / drivers / usb / class / cdc-acm.c
index 7f6f3ab..1e75688 100644 (file)
@@ -173,7 +173,7 @@ static int acm_wb_alloc(struct acm *acm)
        for (;;) {
                wb = &acm->wb[wbn];
                if (!wb->use) {
-                       wb->use = 1;
+                       wb->use = true;
                        wb->len = 0;
                        return wbn;
                }
@@ -191,7 +191,8 @@ static int acm_wb_is_avail(struct acm *acm)
        n = ACM_NW;
        spin_lock_irqsave(&acm->write_lock, flags);
        for (i = 0; i < ACM_NW; i++)
-               n -= acm->wb[i].use;
+               if(acm->wb[i].use)
+                       n--;
        spin_unlock_irqrestore(&acm->write_lock, flags);
        return n;
 }
@@ -201,7 +202,7 @@ static int acm_wb_is_avail(struct acm *acm)
  */
 static void acm_write_done(struct acm *acm, struct acm_wb *wb)
 {
-       wb->use = 0;
+       wb->use = false;
        acm->transmitting--;
        usb_autopm_put_interface_async(acm->control);
 }
@@ -507,6 +508,7 @@ static void acm_read_bulk_callback(struct urb *urb)
                        "%s - cooling babbling device\n", __func__);
                usb_mark_last_busy(acm->dev);
                set_bit(rb->index, &acm->urbs_in_error_delay);
+               set_bit(ACM_ERROR_DELAY, &acm->flags);
                cooldown = true;
                break;
        default:
@@ -532,7 +534,7 @@ static void acm_read_bulk_callback(struct urb *urb)
 
        if (stopped || stalled || cooldown) {
                if (stalled)
-                       schedule_work(&acm->work);
+                       schedule_delayed_work(&acm->dwork, 0);
                else if (cooldown)
                        schedule_delayed_work(&acm->dwork, HZ / 2);
                return;
@@ -562,13 +564,13 @@ static void acm_write_bulk(struct urb *urb)
        acm_write_done(acm, wb);
        spin_unlock_irqrestore(&acm->write_lock, flags);
        set_bit(EVENT_TTY_WAKEUP, &acm->flags);
-       schedule_work(&acm->work);
+       schedule_delayed_work(&acm->dwork, 0);
 }
 
 static void acm_softint(struct work_struct *work)
 {
        int i;
-       struct acm *acm = container_of(work, struct acm, work);
+       struct acm *acm = container_of(work, struct acm, dwork.work);
 
        if (test_bit(EVENT_RX_STALL, &acm->flags)) {
                smp_mb(); /* against acm_suspend() */
@@ -584,7 +586,7 @@ static void acm_softint(struct work_struct *work)
        if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
                for (i = 0; i < acm->rx_buflimit; i++)
                        if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
-                                       acm_submit_read_urb(acm, i, GFP_NOIO);
+                               acm_submit_read_urb(acm, i, GFP_KERNEL);
        }
 
        if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags))
@@ -741,7 +743,7 @@ static void acm_port_shutdown(struct tty_port *port)
                if (!urb)
                        break;
                wb = urb->context;
-               wb->use = 0;
+               wb->use = false;
                usb_autopm_put_interface_async(acm->control);
        }
 
@@ -792,7 +794,7 @@ static int acm_tty_write(struct tty_struct *tty,
        wb = &acm->wb[wbn];
 
        if (!acm->dev) {
-               wb->use = 0;
+               wb->use = false;
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return -ENODEV;
        }
@@ -804,7 +806,7 @@ static int acm_tty_write(struct tty_struct *tty,
 
        stat = usb_autopm_get_interface_async(acm->control);
        if (stat) {
-               wb->use = 0;
+               wb->use = false;
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return stat;
        }
@@ -1196,9 +1198,6 @@ static int acm_probe(struct usb_interface *intf,
                return -EINVAL;
        }
 
-       if (!intf->cur_altsetting)
-               return -EINVAL;
-
        if (!buflen) {
                if (intf->cur_altsetting->endpoint &&
                                intf->cur_altsetting->endpoint->extralen &&
@@ -1221,39 +1220,42 @@ static int acm_probe(struct usb_interface *intf,
                call_intf_num = cmgmd->bDataInterface;
 
        if (!union_header) {
-               if (call_intf_num > 0) {
+               if (intf->cur_altsetting->desc.bNumEndpoints == 3) {
+                       dev_dbg(&intf->dev, "No union descriptor, assuming single interface\n");
+                       combined_interfaces = 1;
+                       control_interface = data_interface = intf;
+                       goto look_for_collapsed_interface;
+               } else if (call_intf_num > 0) {
                        dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
-                       /* quirks for Droids MuIn LCD */
-                       if (quirks & NO_DATA_INTERFACE) {
-                               data_interface = usb_ifnum_to_if(usb_dev, 0);
-                       } else {
-                               data_intf_num = call_intf_num;
-                               data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
-                       }
+                       data_intf_num = call_intf_num;
+                       data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
                        control_interface = intf;
                } else {
-                       if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
-                               dev_dbg(&intf->dev,"No union descriptor, giving up\n");
-                               return -ENODEV;
-                       } else {
-                               dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
-                               combined_interfaces = 1;
-                               control_interface = data_interface = intf;
-                               goto look_for_collapsed_interface;
-                       }
+                       dev_dbg(&intf->dev, "No union descriptor, giving up\n");
+                       return -ENODEV;
                }
        } else {
+               int class = -1;
+
                data_intf_num = union_header->bSlaveInterface0;
                control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
                data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
+
+               if (control_interface)
+                       class = control_interface->cur_altsetting->desc.bInterfaceClass;
+
+               if (class != USB_CLASS_COMM && class != USB_CLASS_CDC_DATA) {
+                       dev_dbg(&intf->dev, "Broken union descriptor, assuming single interface\n");
+                       combined_interfaces = 1;
+                       control_interface = data_interface = intf;
+                       goto look_for_collapsed_interface;
+               }
        }
 
        if (!control_interface || !data_interface) {
                dev_dbg(&intf->dev, "no interfaces\n");
                return -ENODEV;
        }
-       if (!data_interface->cur_altsetting || !control_interface->cur_altsetting)
-               return -ENODEV;
 
        if (data_intf_num != call_intf_num)
                dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
@@ -1280,10 +1282,8 @@ look_for_collapsed_interface:
 skip_normal_probe:
 
        /*workaround for switched interfaces */
-       if (data_interface->cur_altsetting->desc.bInterfaceClass
-                                               != CDC_DATA_INTERFACE_TYPE) {
-               if (control_interface->cur_altsetting->desc.bInterfaceClass
-                                               == CDC_DATA_INTERFACE_TYPE) {
+       if (data_interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) {
+               if (control_interface->cur_altsetting->desc.bInterfaceClass == USB_CLASS_CDC_DATA) {
                        dev_dbg(&intf->dev,
                                "Your device has switched interfaces.\n");
                        swap(control_interface, data_interface);
@@ -1352,7 +1352,6 @@ made_compressed_probe:
        acm->ctrlsize = ctrlsize;
        acm->readsize = readsize;
        acm->rx_buflimit = num_rx_buf;
-       INIT_WORK(&acm->work, acm_softint);
        INIT_DELAYED_WORK(&acm->dwork, acm_softint);
        init_waitqueue_head(&acm->wioctl);
        spin_lock_init(&acm->write_lock);
@@ -1562,7 +1561,6 @@ static void acm_disconnect(struct usb_interface *intf)
        }
 
        acm_kill_urbs(acm);
-       cancel_work_sync(&acm->work);
        cancel_delayed_work_sync(&acm->dwork);
 
        tty_unregister_device(acm_tty_driver, acm->minor);
@@ -1605,7 +1603,6 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
                return 0;
 
        acm_kill_urbs(acm);
-       cancel_work_sync(&acm->work);
        cancel_delayed_work_sync(&acm->dwork);
        acm->urbs_in_error_delay = 0;
 
@@ -1876,11 +1873,6 @@ static const struct usb_device_id acm_ids[] = {
 
        /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
 
-       /* Support for Droids MuIn LCD */
-       { USB_DEVICE(0x04d8, 0x000b),
-       .driver_info = NO_DATA_INTERFACE,
-       },
-
 #if IS_ENABLED(CONFIG_INPUT_IMS_PCU)
        { USB_DEVICE(0x04d8, 0x0082),   /* Application mode */
        .driver_info = IGNORE_DEVICE,
@@ -1906,6 +1898,17 @@ static const struct usb_device_id acm_ids[] = {
        .driver_info = IGNORE_DEVICE,
        },
 
+       /* Exclude ETAS ES58x */
+       { USB_DEVICE(0x108c, 0x0159), /* ES581.4 */
+       .driver_info = IGNORE_DEVICE,
+       },
+       { USB_DEVICE(0x108c, 0x0168), /* ES582.1 */
+       .driver_info = IGNORE_DEVICE,
+       },
+       { USB_DEVICE(0x108c, 0x0169), /* ES584.1 */
+       .driver_info = IGNORE_DEVICE,
+       },
+
        { USB_DEVICE(0x1bc7, 0x0021), /* Telit 3G ACM only composition */
        .driver_info = SEND_ZERO_PACKET,
        },