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

Commit 8fdb7e9f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (45 commits)
  USB: gadget/multi: cdc_do_config: remove redundant check
  usb: r8a66597-hcd: fix removed from an attached hub
  USB: xhci: Make endpoint interval debugging clearer.
  USB: Fix usb_fill_int_urb for SuperSpeed devices
  USB: cp210x: Remove double usb_control_msg from cp210x_set_config
  USB: Remove last bit of CONFIG_USB_BERRY_CHARGE
  USB: gadget: add gadget controller number for s3c-hsotg driver
  USB: ftdi_sio: Fix locking for change_speed() function
  USB: g_mass_storage: fixed module name in Kconfig
  USB: gadget: f_mass_storage::fsg_bind(): fix error handling
  USB: g_mass_storage: fix section mismatch warnings
  USB: gadget: fix Blackfin builds after gadget cleansing
  USB: goku_udc: remove potential null dereference
  USB: option.c: Add Pirelli VID/PID and indicate Pirelli's modem interface is 0xff
  USB: serial: Fix module name typo for qcaux Kconfig entry.
  usb: cdc-wdm: Fix deadlock between write and resume
  usb: cdc-wdm: Fix order in disconnect and fix locking
  usb: cdc-wdm:Fix loss of data due to autosuspend
  usb: cdc-wdm: Fix submission of URB after suspension
  usb: cdc-wdm: Fix race between disconnect and debug messages
  ...
parents fc7f99cf 4cb80cda
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@ Description:
		match the driver to the device.  For example:
		# echo "046d c315" > /sys/bus/usb/drivers/foo/remove_id

What:		/sys/bus/usb/device/.../avoid_reset
What:		/sys/bus/usb/device/.../avoid_reset_quirk
Date:		December 2009
Contact:	Oliver Neukum <oliver@neukum.org>
Description:
+1 −1
Original line number Diff line number Diff line
@@ -1441,7 +1441,7 @@ static int acm_resume(struct usb_interface *intf)
			wb = acm->delayed_wb;
			acm->delayed_wb = NULL;
			spin_unlock_irq(&acm->write_lock);
			acm_start_wb(acm, acm->delayed_wb);
			acm_start_wb(acm, wb);
		} else {
			spin_unlock_irq(&acm->write_lock);
		}
+78 −56
Original line number Diff line number Diff line
@@ -52,7 +52,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_READ		4
#define WDM_INT_STALL		5
#define WDM_POLL_RUNNING	6

#define WDM_RESPONDING		7
#define WDM_SUSPENDING		8

#define WDM_MAX			16

@@ -87,9 +88,7 @@ struct wdm_device {
	int			count;
	dma_addr_t		shandle;
	dma_addr_t		ihandle;
	struct mutex		wlock;
	struct mutex		rlock;
	struct mutex		plock;
	struct mutex		lock;
	wait_queue_head_t	wait;
	struct work_struct	rxwork;
	int			werr;
@@ -117,21 +116,22 @@ static void wdm_in_callback(struct urb *urb)
	int status = urb->status;

	spin_lock(&desc->iuspin);
	clear_bit(WDM_RESPONDING, &desc->flags);

	if (status) {
		switch (status) {
		case -ENOENT:
			dev_dbg(&desc->intf->dev,
				"nonzero urb status received: -ENOENT");
			break;
			goto skip_error;
		case -ECONNRESET:
			dev_dbg(&desc->intf->dev,
				"nonzero urb status received: -ECONNRESET");
			break;
			goto skip_error;
		case -ESHUTDOWN:
			dev_dbg(&desc->intf->dev,
				"nonzero urb status received: -ESHUTDOWN");
			break;
			goto skip_error;
		case -EPIPE:
			dev_err(&desc->intf->dev,
				"nonzero urb status received: -EPIPE\n");
@@ -147,6 +147,7 @@ static void wdm_in_callback(struct urb *urb)
	desc->reslength = urb->actual_length;
	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
	desc->length += desc->reslength;
skip_error:
	wake_up(&desc->wait);

	set_bit(WDM_READ, &desc->flags);
@@ -229,13 +230,16 @@ static void wdm_int_callback(struct urb *urb)
	desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	spin_lock(&desc->iuspin);
	clear_bit(WDM_READ, &desc->flags);
	if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
	set_bit(WDM_RESPONDING, &desc->flags);
	if (!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",
			__func__, rv);
	}
	spin_unlock(&desc->iuspin);
	if (rv < 0) {
		clear_bit(WDM_RESPONDING, &desc->flags);
		if (rv == -EPERM)
			return;
		if (rv == -ENOMEM) {
@@ -305,14 +309,38 @@ static ssize_t wdm_write
	if (we < 0)
		return -EIO;

	r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
	if (!buf) {
		rv = -ENOMEM;
		goto outnl;
	}

	r = copy_from_user(buf, buffer, count);
	if (r > 0) {
		kfree(buf);
		rv = -EFAULT;
		goto outnl;
	}

	/* concurrent writes and disconnect */
	r = mutex_lock_interruptible(&desc->lock);
	rv = -ERESTARTSYS;
	if (r)
	if (r) {
		kfree(buf);
		goto outnl;
	}

	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
		kfree(buf);
		rv = -ENODEV;
		goto outnp;
	}

	r = usb_autopm_get_interface(desc->intf);
	if (r < 0)
	if (r < 0) {
		kfree(buf);
		goto outnp;
	}

	if (!file->f_flags && O_NONBLOCK)
		r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
@@ -320,24 +348,8 @@ static ssize_t wdm_write
	else
		if (test_bit(WDM_IN_USE, &desc->flags))
			r = -EAGAIN;
	if (r < 0)
		goto out;

	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
		rv = -ENODEV;
		goto out;
	}

	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
	if (!buf) {
		rv = -ENOMEM;
		goto out;
	}

	r = copy_from_user(buf, buffer, count);
	if (r > 0) {
	if (r < 0) {
		kfree(buf);
		rv = -EFAULT;
		goto out;
	}

@@ -374,7 +386,7 @@ static ssize_t wdm_write
out:
	usb_autopm_put_interface(desc->intf);
outnp:
	mutex_unlock(&desc->wlock);
	mutex_unlock(&desc->lock);
outnl:
	return rv < 0 ? rv : count;
}
@@ -387,7 +399,7 @@ static ssize_t wdm_read
	struct wdm_device *desc = file->private_data;


	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
	rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
	if (rv < 0)
		return -ERESTARTSYS;

@@ -424,11 +436,8 @@ static ssize_t wdm_read
		spin_lock_irq(&desc->iuspin);

		if (desc->rerr) { /* read completed, error happened */
			int t = desc->rerr;
			desc->rerr = 0;
			spin_unlock_irq(&desc->iuspin);
			dev_err(&desc->intf->dev,
				"reading had resulted in %d\n", t);
			rv = -EIO;
			goto err;
		}
@@ -465,9 +474,7 @@ static ssize_t wdm_read
	rv = cntr;

err:
	mutex_unlock(&desc->rlock);
	if (rv < 0 && rv != -EAGAIN)
		dev_err(&desc->intf->dev, "wdm_read: exit error\n");
	mutex_unlock(&desc->lock);
	return rv;
}

@@ -533,7 +540,7 @@ static int wdm_open(struct inode *inode, struct file *file)
	}
	intf->needs_remote_wakeup = 1;

	mutex_lock(&desc->plock);
	mutex_lock(&desc->lock);
	if (!desc->count++) {
		rv = usb_submit_urb(desc->validity, GFP_KERNEL);
		if (rv < 0) {
@@ -544,7 +551,7 @@ static int wdm_open(struct inode *inode, struct file *file)
	} else {
		rv = 0;
	}
	mutex_unlock(&desc->plock);
	mutex_unlock(&desc->lock);
	usb_autopm_put_interface(desc->intf);
out:
	mutex_unlock(&wdm_mutex);
@@ -556,9 +563,9 @@ static int wdm_release(struct inode *inode, struct file *file)
	struct wdm_device *desc = file->private_data;

	mutex_lock(&wdm_mutex);
	mutex_lock(&desc->plock);
	mutex_lock(&desc->lock);
	desc->count--;
	mutex_unlock(&desc->plock);
	mutex_unlock(&desc->lock);

	if (!desc->count) {
		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
@@ -655,9 +662,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
	if (!desc)
		goto out;
	mutex_init(&desc->wlock);
	mutex_init(&desc->rlock);
	mutex_init(&desc->plock);
	mutex_init(&desc->lock);
	spin_lock_init(&desc->iuspin);
	init_waitqueue_head(&desc->wait);
	desc->wMaxCommand = maxcom;
@@ -771,14 +776,17 @@ static void wdm_disconnect(struct usb_interface *intf)
	/* to terminate pending flushes */
	clear_bit(WDM_IN_USE, &desc->flags);
	spin_unlock_irqrestore(&desc->iuspin, flags);
	cancel_work_sync(&desc->rxwork);
	mutex_lock(&desc->lock);
	kill_urbs(desc);
	cancel_work_sync(&desc->rxwork);
	mutex_unlock(&desc->lock);
	wake_up_all(&desc->wait);
	if (!desc->count)
		cleanup(desc);
	mutex_unlock(&wdm_mutex);
}

#ifdef CONFIG_PM
static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct wdm_device *desc = usb_get_intfdata(intf);
@@ -786,22 +794,30 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)

	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);

	mutex_lock(&desc->plock);
#ifdef CONFIG_PM
	/* if this is an autosuspend the caller does the locking */
	if (!(message.event & PM_EVENT_AUTO))
		mutex_lock(&desc->lock);
	spin_lock_irq(&desc->iuspin);

	if ((message.event & PM_EVENT_AUTO) &&
			test_bit(WDM_IN_USE, &desc->flags)) {
			(test_bit(WDM_IN_USE, &desc->flags)
			|| test_bit(WDM_RESPONDING, &desc->flags))) {
		spin_unlock_irq(&desc->iuspin);
		rv = -EBUSY;
	} else {
#endif
		cancel_work_sync(&desc->rxwork);

		set_bit(WDM_SUSPENDING, &desc->flags);
		spin_unlock_irq(&desc->iuspin);
		/* callback submits work - order is essential */
		kill_urbs(desc);
#ifdef CONFIG_PM
		cancel_work_sync(&desc->rxwork);
	}
#endif
	mutex_unlock(&desc->plock);
	if (!(message.event & PM_EVENT_AUTO))
		mutex_unlock(&desc->lock);

	return rv;
}
#endif

static int recover_from_urb_loss(struct wdm_device *desc)
{
@@ -815,23 +831,27 @@ static int recover_from_urb_loss(struct wdm_device *desc)
	}
	return rv;
}

#ifdef CONFIG_PM
static int wdm_resume(struct usb_interface *intf)
{
	struct wdm_device *desc = usb_get_intfdata(intf);
	int rv;

	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
	mutex_lock(&desc->plock);

	clear_bit(WDM_SUSPENDING, &desc->flags);
	rv = recover_from_urb_loss(desc);
	mutex_unlock(&desc->plock);

	return rv;
}
#endif

static int wdm_pre_reset(struct usb_interface *intf)
{
	struct wdm_device *desc = usb_get_intfdata(intf);

	mutex_lock(&desc->plock);
	mutex_lock(&desc->lock);
	return 0;
}

@@ -841,7 +861,7 @@ static int wdm_post_reset(struct usb_interface *intf)
	int rv;

	rv = recover_from_urb_loss(desc);
	mutex_unlock(&desc->plock);
	mutex_unlock(&desc->lock);
	return 0;
}

@@ -849,9 +869,11 @@ static struct usb_driver wdm_driver = {
	.name =		"cdc_wdm",
	.probe =	wdm_probe,
	.disconnect =	wdm_disconnect,
#ifdef CONFIG_PM
	.suspend =	wdm_suspend,
	.resume =	wdm_resume,
	.reset_resume =	wdm_resume,
#endif
	.pre_reset =	wdm_pre_reset,
	.post_reset =	wdm_post_reset,
	.id_table =	wdm_ids,
+14 −3
Original line number Diff line number Diff line
@@ -1207,6 +1207,13 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
			free_async(as);
			return -ENOMEM;
		}
		/* Isochronous input data may end up being discontiguous
		 * if some of the packets are short.  Clear the buffer so
		 * that the gaps don't leak kernel data to userspace.
		 */
		if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
			memset(as->urb->transfer_buffer, 0,
					uurb->buffer_length);
	}
	as->urb->dev = ps->dev;
	as->urb->pipe = (uurb->type << 30) |
@@ -1345,10 +1352,14 @@ static int processcompl(struct async *as, void __user * __user *arg)
	void __user *addr = as->userurb;
	unsigned int i;

	if (as->userbuffer && urb->actual_length)
		if (copy_to_user(as->userbuffer, urb->transfer_buffer,
				 urb->actual_length))
	if (as->userbuffer && urb->actual_length) {
		if (urb->number_of_packets > 0)		/* Isochronous */
			i = urb->transfer_buffer_length;
		else					/* Non-Isoc */
			i = urb->actual_length;
		if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
			goto err_out;
	}
	if (put_user(as->status, &userurb->status))
		goto err_out;
	if (put_user(urb->actual_length, &userurb->actual_length))
+1 −0
Original line number Diff line number Diff line
@@ -453,6 +453,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
			if (urb->interval > (1 << 15))
				return -EINVAL;
			max = 1 << 15;
			break;
		case USB_SPEED_WIRELESS:
			if (urb->interval > 16)
				return -EINVAL;
Loading