diff options
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 49 |
1 files changed, 40 insertions, 9 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 0bb2b3248dad..ee469274a3fe 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -55,6 +55,15 @@ static const struct usb_device_id wdm_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */ }, + { + /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = HUAWEI_VENDOR_ID, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 57, /* NOTE: CDC ECM control interface! */ + }, { } }; @@ -309,9 +318,6 @@ static void free_urbs(struct wdm_device *desc) static void cleanup(struct wdm_device *desc) { - spin_lock(&wdm_device_list_lock); - list_del(&desc->device_list); - spin_unlock(&wdm_device_list_lock); kfree(desc->sbuf); kfree(desc->inbuf); kfree(desc->orq); @@ -369,6 +375,7 @@ static ssize_t wdm_write r = usb_autopm_get_interface(desc->intf); if (r < 0) { kfree(buf); + rv = usb_translate_errors(r); goto outnp; } @@ -384,6 +391,7 @@ static ssize_t wdm_write if (r < 0) { kfree(buf); + rv = r; goto out; } @@ -415,6 +423,7 @@ static ssize_t wdm_write desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); + rv = usb_translate_errors(rv); } else { dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", req->wIndex); @@ -491,6 +500,8 @@ retry: goto retry; } if (!desc->reslength) { /* zero length read */ + dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); + clear_bit(WDM_READ, &desc->flags); spin_unlock_irq(&desc->iuspin); goto retry; } @@ -530,11 +541,13 @@ static int wdm_flush(struct file *file, fl_owner_t id) struct wdm_device *desc = file->private_data; wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); - if (desc->werr < 0) + + /* cannot dereference desc->intf if WDM_DISCONNECTING */ + if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags)) dev_err(&desc->intf->dev, "Error in flush path: %d\n", desc->werr); - return desc->werr; + return usb_translate_errors(desc->werr); } static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) @@ -545,7 +558,7 @@ static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) spin_lock_irqsave(&desc->iuspin, flags); if (test_bit(WDM_DISCONNECTING, &desc->flags)) { - mask = POLLERR; + mask = POLLHUP | POLLERR; spin_unlock_irqrestore(&desc->iuspin, flags); goto desc_out; } @@ -596,6 +609,7 @@ static int wdm_open(struct inode *inode, struct file *file) desc->count--; dev_err(&desc->intf->dev, "Error submitting int urb - %d\n", rv); + rv = usb_translate_errors(rv); } } else { rv = 0; @@ -621,10 +635,15 @@ static int wdm_release(struct inode *inode, struct file *file) mutex_unlock(&desc->wlock); if (!desc->count) { - dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); - kill_urbs(desc); - if (!test_bit(WDM_DISCONNECTING, &desc->flags)) + if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { + dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); + kill_urbs(desc); desc->manage_power(desc->intf, 0); + } else { + /* must avoid dev_printk here as desc->intf is invalid */ + pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__); + cleanup(desc); + } } mutex_unlock(&wdm_mutex); return 0; @@ -771,6 +790,9 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor out: return rv; err: + spin_lock(&wdm_device_list_lock); + list_del(&desc->device_list); + spin_unlock(&wdm_device_list_lock); cleanup(desc); return rv; } @@ -896,8 +918,16 @@ static void wdm_disconnect(struct usb_interface *intf) cancel_work_sync(&desc->rxwork); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); + + /* the desc->intf pointer used as list key is now invalid */ + spin_lock(&wdm_device_list_lock); + list_del(&desc->device_list); + spin_unlock(&wdm_device_list_lock); + if (!desc->count) cleanup(desc); + else + dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count); mutex_unlock(&wdm_mutex); } @@ -1015,6 +1045,7 @@ static struct usb_driver wdm_driver = { .post_reset = wdm_post_reset, .id_table = wdm_ids, .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, }; module_usb_driver(wdm_driver); |