aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Suarez <gpsuarez2512@gmail.com>2013-10-29 13:29:10 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-29 20:02:41 -0400
commit73e06865ead1bec8d1c179e1c647dc77adde9116 (patch)
tree01e167bd8665aab09998994b83338f5d790377b0
parent32e24930fb71c47a1366325b6f139e039cacaca4 (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.c38
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 */