diff options
author | Greg Suarez <gpsuarez2512@gmail.com> | 2013-10-29 13:29:10 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-29 20:02:41 -0400 |
commit | 73e06865ead1bec8d1c179e1c647dc77adde9116 (patch) | |
tree | 01e167bd8665aab09998994b83338f5d790377b0 | |
parent | 32e24930fb71c47a1366325b6f139e039cacaca4 (diff) |
USB: cdc-wdm: support back-to-back USB_CDC_NOTIFY_RESPONSE_AVAILABLE notifications
Some MBIM devices send back-to-back USB_CDC_NOTIFY_RESPONSE_AVAILABLE notifications
when sending a message over multiple fragments or when there are unsolicited
messages available.
Count up the number of USB_CDC_NOTIFY_RESPONSE_AVAILABLE notifications received
and decrement the count and submit the urb for the next response each time userspace
completes a read the response.
Signed-off-by: Greg Suarez <gsuarez@smithmicro.com>
Acked-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 38 |
1 files changed, 34 insertions, 4 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index d3318a0df8ee..589ea58d5d66 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
@@ -101,6 +101,7 @@ struct wdm_device { | |||
101 | struct work_struct rxwork; | 101 | struct work_struct rxwork; |
102 | int werr; | 102 | int werr; |
103 | int rerr; | 103 | int rerr; |
104 | int resp_count; | ||
104 | 105 | ||
105 | struct list_head device_list; | 106 | struct list_head device_list; |
106 | int (*manage_power)(struct usb_interface *, int); | 107 | int (*manage_power)(struct usb_interface *, int); |
@@ -262,9 +263,9 @@ static void wdm_int_callback(struct urb *urb) | |||
262 | } | 263 | } |
263 | 264 | ||
264 | spin_lock(&desc->iuspin); | 265 | spin_lock(&desc->iuspin); |
265 | clear_bit(WDM_READ, &desc->flags); | ||
266 | responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); | 266 | responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); |
267 | if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags) | 267 | if (!desc->resp_count++ && !responding |
268 | && !test_bit(WDM_DISCONNECTING, &desc->flags) | ||
268 | && !test_bit(WDM_SUSPENDING, &desc->flags)) { | 269 | && !test_bit(WDM_SUSPENDING, &desc->flags)) { |
269 | rv = usb_submit_urb(desc->response, GFP_ATOMIC); | 270 | rv = usb_submit_urb(desc->response, GFP_ATOMIC); |
270 | dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", | 271 | dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", |
@@ -521,10 +522,36 @@ retry: | |||
521 | 522 | ||
522 | desc->length -= cntr; | 523 | desc->length -= cntr; |
523 | /* in case we had outstanding data */ | 524 | /* in case we had outstanding data */ |
524 | if (!desc->length) | 525 | if (!desc->length) { |
525 | clear_bit(WDM_READ, &desc->flags); | 526 | clear_bit(WDM_READ, &desc->flags); |
526 | 527 | ||
527 | spin_unlock_irq(&desc->iuspin); | 528 | if (--desc->resp_count) { |
529 | set_bit(WDM_RESPONDING, &desc->flags); | ||
530 | spin_unlock_irq(&desc->iuspin); | ||
531 | |||
532 | rv = usb_submit_urb(desc->response, GFP_KERNEL); | ||
533 | if (rv) { | ||
534 | dev_err(&desc->intf->dev, | ||
535 | "%s: usb_submit_urb failed with result %d\n", | ||
536 | __func__, rv); | ||
537 | spin_lock_irq(&desc->iuspin); | ||
538 | clear_bit(WDM_RESPONDING, &desc->flags); | ||
539 | spin_unlock_irq(&desc->iuspin); | ||
540 | |||
541 | if (rv == -ENOMEM) { | ||
542 | rv = schedule_work(&desc->rxwork); | ||
543 | if (rv) | ||
544 | dev_err(&desc->intf->dev, "Cannot schedule work\n"); | ||
545 | } else { | ||
546 | spin_lock_irq(&desc->iuspin); | ||
547 | desc->resp_count = 0; | ||
548 | spin_unlock_irq(&desc->iuspin); | ||
549 | } | ||
550 | } | ||
551 | } else | ||
552 | spin_unlock_irq(&desc->iuspin); | ||
553 | } else | ||
554 | spin_unlock_irq(&desc->iuspin); | ||
528 | 555 | ||
529 | rv = cntr; | 556 | rv = cntr; |
530 | 557 | ||
@@ -635,6 +662,9 @@ static int wdm_release(struct inode *inode, struct file *file) | |||
635 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { | 662 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { |
636 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); | 663 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); |
637 | kill_urbs(desc); | 664 | kill_urbs(desc); |
665 | spin_lock_irq(&desc->iuspin); | ||
666 | desc->resp_count = 0; | ||
667 | spin_unlock_irq(&desc->iuspin); | ||
638 | desc->manage_power(desc->intf, 0); | 668 | desc->manage_power(desc->intf, 0); |
639 | } else { | 669 | } else { |
640 | /* must avoid dev_printk here as desc->intf is invalid */ | 670 | /* must avoid dev_printk here as desc->intf is invalid */ |