diff options
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 39 |
1 files changed, 29 insertions, 10 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 4bfc48d18654..56d3ab86be7f 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -155,6 +155,9 @@ static void wdm_out_callback(struct urb *urb) wake_up(&desc->wait); } +/* forward declaration */ +static int service_outstanding_interrupt(struct wdm_device *desc); + static void wdm_in_callback(struct urb *urb) { struct wdm_device *desc = urb->context; @@ -189,7 +192,13 @@ static void wdm_in_callback(struct urb *urb) } } - desc->rerr = status; + /* + * only set a new error if there is no previous error. + * Errors are only cleared during read/open + */ + if (desc->rerr == 0) + desc->rerr = status; + if (length + desc->length > desc->wMaxCommand) { /* The buffer would overflow */ set_bit(WDM_OVERFLOW, &desc->flags); @@ -221,9 +230,19 @@ static void wdm_in_callback(struct urb *urb) } skip_error: + set_bit(WDM_READ, &desc->flags); wake_up(&desc->wait); - set_bit(WDM_READ, &desc->flags); + if (desc->rerr) { + /* + * Since there was an error, userspace may decide to not read + * any data after poll'ing. + * We should respond to further attempts from the device to send + * data, so that we can get unstuck. + */ + service_outstanding_interrupt(desc); + } + unlock: spin_unlock(&desc->iuspin); } @@ -457,17 +476,14 @@ out_free_mem: } /* - * clear WDM_READ flag and possibly submit the read urb if resp_count - * is non-zero. + * Submit the read urb if resp_count is non-zero. * * Called with desc->iuspin locked */ -static int clear_wdm_read_flag(struct wdm_device *desc) +static int service_outstanding_interrupt(struct wdm_device *desc) { int rv = 0; - clear_bit(WDM_READ, &desc->flags); - /* submit read urb only if the device is waiting for it */ if (!desc->resp_count || !--desc->resp_count) goto out; @@ -559,7 +575,8 @@ retry: if (!desc->reslength) { /* zero length read */ dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); - rv = clear_wdm_read_flag(desc); + clear_bit(WDM_READ, &desc->flags); + rv = service_outstanding_interrupt(desc); spin_unlock_irq(&desc->iuspin); if (rv < 0) goto err; @@ -584,8 +601,10 @@ retry: desc->length -= cntr; /* in case we had outstanding data */ - if (!desc->length) - clear_wdm_read_flag(desc); + if (!desc->length) { + clear_bit(WDM_READ, &desc->flags); + service_outstanding_interrupt(desc); + } spin_unlock_irq(&desc->iuspin); rv = cntr; |