diff options
| author | David S. Miller <davem@davemloft.net> | 2010-03-22 20:05:26 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-03-22 20:05:26 -0700 | 
| commit | 91c030b4d6445ebe9fbddb86d584441f300df15c (patch) | |
| tree | e8a48e3ad9e0448c690268175e555fa651983546 /drivers/usb/class | |
| parent | 7c3456fdb503071787f7f972de1069b9cacd16f0 (diff) | |
| parent | ae6be51ed01d6c4aaf249a207b4434bc7785853b (diff) | |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Diffstat (limited to 'drivers/usb/class')
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 2 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 134 | 
2 files changed, 79 insertions, 57 deletions
| diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 975d556b4787..be6331e2c276 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -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);  		} diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 18aafcb08fc8..189141ca4e05 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -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 @@ retry:  		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 @@ retry:  	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 @@ next_desc:  	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, | 
