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>
This commit is contained in:
parent
32e24930fb
commit
73e06865ea
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,10 +522,36 @@ retry:
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
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;
|
rv = cntr;
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue