Commit 73e06865 authored by Greg Suarez's avatar Greg Suarez Committed by Greg Kroah-Hartman

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: default avatarGreg Suarez <gsuarez@smithmicro.com>
Acked-by: default avatarBjørn Mork <bjorn@mork.no>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 32e24930
...@@ -101,6 +101,7 @@ struct wdm_device { ...@@ -101,6 +101,7 @@ struct wdm_device {
struct work_struct rxwork; struct work_struct rxwork;
int werr; int werr;
int rerr; int rerr;
int resp_count;
struct list_head device_list; struct list_head device_list;
int (*manage_power)(struct usb_interface *, int); int (*manage_power)(struct usb_interface *, int);
...@@ -262,9 +263,9 @@ static void wdm_int_callback(struct urb *urb) ...@@ -262,9 +263,9 @@ static void wdm_int_callback(struct urb *urb)
} }
spin_lock(&desc->iuspin); spin_lock(&desc->iuspin);
clear_bit(WDM_READ, &desc->flags);
responding = test_and_set_bit(WDM_RESPONDING, &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)) { && !test_bit(WDM_SUSPENDING, &desc->flags)) {
rv = usb_submit_urb(desc->response, GFP_ATOMIC); rv = usb_submit_urb(desc->response, GFP_ATOMIC);
dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
...@@ -521,9 +522,35 @@ static ssize_t wdm_read ...@@ -521,9 +522,35 @@ static ssize_t wdm_read
desc->length -= cntr; desc->length -= cntr;
/* in case we had outstanding data */ /* in case we had outstanding data */
if (!desc->length) if (!desc->length) {
clear_bit(WDM_READ, &desc->flags); clear_bit(WDM_READ, &desc->flags);
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); spin_unlock_irq(&desc->iuspin);
rv = cntr; rv = cntr;
...@@ -635,6 +662,9 @@ static int wdm_release(struct inode *inode, struct file *file) ...@@ -635,6 +662,9 @@ static int wdm_release(struct inode *inode, struct file *file)
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
kill_urbs(desc); kill_urbs(desc);
spin_lock_irq(&desc->iuspin);
desc->resp_count = 0;
spin_unlock_irq(&desc->iuspin);
desc->manage_power(desc->intf, 0); desc->manage_power(desc->intf, 0);
} else { } else {
/* must avoid dev_printk here as desc->intf is invalid */ /* must avoid dev_printk here as desc->intf is invalid */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment