Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+34 −4
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ struct wdm_device {
	struct work_struct	rxwork;
	int			werr;
	int			rerr;
	int                     resp_count;

	struct list_head	device_list;
	int			(*manage_power)(struct usb_interface *, int);
@@ -262,9 +263,9 @@ static void wdm_int_callback(struct urb *urb)
	}

	spin_lock(&desc->iuspin);
	clear_bit(WDM_READ, &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)) {
		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
@@ -521,9 +522,35 @@ static ssize_t wdm_read

	desc->length -= cntr;
	/* in case we had outstanding data */
	if (!desc->length)
	if (!desc->length) {
		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);

	rv = cntr;
@@ -635,6 +662,9 @@ static int wdm_release(struct inode *inode, struct file *file)
		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
			dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
			kill_urbs(desc);
			spin_lock_irq(&desc->iuspin);
			desc->resp_count = 0;
			spin_unlock_irq(&desc->iuspin);
			desc->manage_power(desc->intf, 0);
		} else {
			/* must avoid dev_printk here as desc->intf is invalid */