diff options
author | Jiri Kosina <jkosina@suse.cz> | 2013-12-19 15:08:03 +0100 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2013-12-19 15:08:32 +0100 |
commit | e23c34bb41da65f354fb7eee04300c56ee48f60c (patch) | |
tree | 549fbe449d55273b81ef104a9755109bf4ae7817 /drivers/usb/class/cdc-wdm.c | |
parent | b481c2cb3534c85dca625973b33eba15f9af3e4c (diff) | |
parent | 319e2e3f63c348a9b66db4667efa73178e18b17d (diff) |
Merge branch 'master' into for-next
Sync with Linus' tree to be able to apply fixes on top of newer things
in tree (efi-stub).
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 42 |
1 files changed, 38 insertions, 4 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index d3318a0df8ee..4d387596f3f0 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -101,6 +101,7 @@ struct wdm_device { struct work_struct rxwork; int werr; int rerr; + int resp_count; struct list_head device_list; int (*manage_power)(struct usb_interface *, int); @@ -253,6 +254,10 @@ static void wdm_int_callback(struct urb *urb) "NOTIFY_NETWORK_CONNECTION %s network", dr->wValue ? "connected to" : "disconnected from"); goto exit; + case USB_CDC_NOTIFY_SPEED_CHANGE: + dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)", + urb->actual_length); + goto exit; default: clear_bit(WDM_POLL_RUNNING, &desc->flags); dev_err(&desc->intf->dev, @@ -262,9 +267,9 @@ static void wdm_int_callback(struct urb *urb) } spin_lock(&desc->iuspin); - clear_bit(WDM_READ, &desc->flags); responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); - if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags) + if (!desc->resp_count++ && !responding + && !test_bit(WDM_DISCONNECTING, &desc->flags) && !test_bit(WDM_SUSPENDING, &desc->flags)) { rv = usb_submit_urb(desc->response, GFP_ATOMIC); dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", @@ -521,10 +526,36 @@ retry: desc->length -= cntr; /* in case we had outstanding data */ - if (!desc->length) + if (!desc->length) { clear_bit(WDM_READ, &desc->flags); - spin_unlock_irq(&desc->iuspin); + if (--desc->resp_count) { + set_bit(WDM_RESPONDING, &desc->flags); + spin_unlock_irq(&desc->iuspin); + + rv = usb_submit_urb(desc->response, GFP_KERNEL); + if (rv) { + dev_err(&desc->intf->dev, + "%s: usb_submit_urb failed with result %d\n", + __func__, rv); + spin_lock_irq(&desc->iuspin); + clear_bit(WDM_RESPONDING, &desc->flags); + spin_unlock_irq(&desc->iuspin); + + if (rv == -ENOMEM) { + rv = schedule_work(&desc->rxwork); + if (rv) + dev_err(&desc->intf->dev, "Cannot schedule work\n"); + } else { + spin_lock_irq(&desc->iuspin); + desc->resp_count = 0; + spin_unlock_irq(&desc->iuspin); + } + } + } else + spin_unlock_irq(&desc->iuspin); + } else + spin_unlock_irq(&desc->iuspin); rv = cntr; @@ -635,6 +666,9 @@ static int wdm_release(struct inode *inode, struct file *file) if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); kill_urbs(desc); + spin_lock_irq(&desc->iuspin); + desc->resp_count = 0; + spin_unlock_irq(&desc->iuspin); desc->manage_power(desc->intf, 0); } else { /* must avoid dev_printk here as desc->intf is invalid */ |