diff options
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 127 | 
1 files changed, 115 insertions, 12 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 731db051070..7e8e1235e4e 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -28,8 +28,9 @@  /*   * Version Information   */ -#define DRIVER_VERSION "v0.02" +#define DRIVER_VERSION "v0.03"  #define DRIVER_AUTHOR "Oliver Neukum" +#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"  static struct usb_device_id wdm_ids[] = {  	{ @@ -87,6 +88,7 @@ struct wdm_device {  	dma_addr_t		ihandle;  	struct mutex		wlock;  	struct mutex		rlock; +	struct mutex		plock;  	wait_queue_head_t	wait;  	struct work_struct	rxwork;  	int			werr; @@ -205,7 +207,7 @@ static void wdm_int_callback(struct urb *urb)  	req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;  	req->wValue = 0;  	req->wIndex = desc->inum; -	req->wLength = cpu_to_le16(desc->bMaxPacketSize0); +	req->wLength = cpu_to_le16(desc->wMaxCommand);  	usb_fill_control_urb(  		desc->response, @@ -214,7 +216,7 @@ static void wdm_int_callback(struct urb *urb)  		usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),  		(unsigned char *)req,  		desc->inbuf, -		desc->bMaxPacketSize0, +		desc->wMaxCommand,  		wdm_in_callback,  		desc  	); @@ -247,6 +249,7 @@ exit:  static void kill_urbs(struct wdm_device *desc)  { +	/* the order here is essential */  	usb_kill_urb(desc->command);  	usb_kill_urb(desc->validity);  	usb_kill_urb(desc->response); @@ -266,7 +269,7 @@ static void cleanup(struct wdm_device *desc)  			desc->sbuf,  			desc->validity->transfer_dma);  	usb_buffer_free(interface_to_usbdev(desc->intf), -			desc->wMaxPacketSize, +			desc->wMaxCommand,  			desc->inbuf,  			desc->response->transfer_dma);  	kfree(desc->orq); @@ -299,6 +302,9 @@ static ssize_t wdm_write  	if (r)  		goto outnl; +	r = usb_autopm_get_interface(desc->intf); +	if (r < 0) +		goto outnp;  	r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,  							   &desc->flags));  	if (r < 0) @@ -347,11 +353,14 @@ static ssize_t wdm_write  	if (rv < 0) {  		kfree(buf);  		clear_bit(WDM_IN_USE, &desc->flags); +		err("Tx URB error: %d", rv);  	} else {  		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",  			req->wIndex);  	}  out: +	usb_autopm_put_interface(desc->intf); +outnp:  	mutex_unlock(&desc->wlock);  outnl:  	return rv < 0 ? rv : count; @@ -376,6 +385,11 @@ retry:  		rv = wait_event_interruptible(desc->wait,  					      test_bit(WDM_READ, &desc->flags)); +		if (test_bit(WDM_DISCONNECTING, &desc->flags)) { +			rv = -ENODEV; +			goto err; +		} +		usb_mark_last_busy(interface_to_usbdev(desc->intf));  		if (rv < 0) {  			rv = -ERESTARTSYS;  			goto err; @@ -418,6 +432,9 @@ retry:  		desc->ubuf[i] = desc->ubuf[i + cntr];  	desc->length -= cntr; +	/* in case we had outstanding data */ +	if (!desc->length) +		clear_bit(WDM_READ, &desc->flags);  	rv = cntr;  err: @@ -480,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file)  	if (test_bit(WDM_DISCONNECTING, &desc->flags))  		goto out; -	desc->count++; +	;  	file->private_data = desc; -	rv = usb_submit_urb(desc->validity, GFP_KERNEL); - +	rv = usb_autopm_get_interface(desc->intf);  	if (rv < 0) { -		desc->count--; -		err("Error submitting int urb - %d", rv); +		err("Error autopm - %d", rv);  		goto out;  	} -	rv = 0; +	intf->needs_remote_wakeup = 1; +	mutex_lock(&desc->plock); +	if (!desc->count++) { +		rv = usb_submit_urb(desc->validity, GFP_KERNEL); +		if (rv < 0) { +			desc->count--; +			err("Error submitting int urb - %d", rv); +		} +	} else { +		rv = 0; +	} +	mutex_unlock(&desc->plock); +	usb_autopm_put_interface(desc->intf);  out:  	mutex_unlock(&wdm_mutex);  	return rv; @@ -502,10 +529,15 @@ 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);  	desc->count--; +	mutex_unlock(&desc->plock); +  	if (!desc->count) {  		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");  		kill_urbs(desc); +		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) +			desc->intf->needs_remote_wakeup = 0;  	}  	mutex_unlock(&wdm_mutex);  	return 0; @@ -597,6 +629,7 @@ next_desc:  		goto out;  	mutex_init(&desc->wlock);  	mutex_init(&desc->rlock); +	mutex_init(&desc->plock);  	spin_lock_init(&desc->iuspin);  	init_waitqueue_head(&desc->wait);  	desc->wMaxCommand = maxcom; @@ -698,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf)  	spin_lock_irqsave(&desc->iuspin, flags);  	set_bit(WDM_DISCONNECTING, &desc->flags);  	set_bit(WDM_READ, &desc->flags); +	/* to terminate pending flushes */  	clear_bit(WDM_IN_USE, &desc->flags);  	spin_unlock_irqrestore(&desc->iuspin, flags);  	cancel_work_sync(&desc->rxwork); @@ -708,11 +742,81 @@ static void wdm_disconnect(struct usb_interface *intf)  	mutex_unlock(&wdm_mutex);  } +static int wdm_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct wdm_device *desc = usb_get_intfdata(intf); +	int rv = 0; + +	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); + +	mutex_lock(&desc->plock); +#ifdef CONFIG_PM +	if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { +		rv = -EBUSY; +	} else { +#endif +		cancel_work_sync(&desc->rxwork); +		kill_urbs(desc); +#ifdef CONFIG_PM +	} +#endif +	mutex_unlock(&desc->plock); + +	return rv; +} + +static int recover_from_urb_loss(struct wdm_device *desc) +{ +	int rv = 0; + +	if (desc->count) { +		rv = usb_submit_urb(desc->validity, GFP_NOIO); +		if (rv < 0) +			err("Error resume submitting int urb - %d", rv); +	} +	return rv; +} +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); +	rv = recover_from_urb_loss(desc); +	mutex_unlock(&desc->plock); +	return rv; +} + +static int wdm_pre_reset(struct usb_interface *intf) +{ +	struct wdm_device *desc = usb_get_intfdata(intf); + +	mutex_lock(&desc->plock); +	return 0; +} + +static int wdm_post_reset(struct usb_interface *intf) +{ +	struct wdm_device *desc = usb_get_intfdata(intf); +	int rv; + +	rv = recover_from_urb_loss(desc); +	mutex_unlock(&desc->plock); +	return 0; +} +  static struct usb_driver wdm_driver = {  	.name =		"cdc_wdm",  	.probe =	wdm_probe,  	.disconnect =	wdm_disconnect, +	.suspend =	wdm_suspend, +	.resume =	wdm_resume, +	.reset_resume =	wdm_resume, +	.pre_reset =	wdm_pre_reset, +	.post_reset =	wdm_post_reset,  	.id_table =	wdm_ids, +	.supports_autosuspend = 1,  };  /* --- low level module stuff --- */ @@ -735,6 +839,5 @@ module_init(wdm_init);  module_exit(wdm_exit);  MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION("USB Abstract Control Model driver for " -		   "USB WCM Device Management"); +MODULE_DESCRIPTION(DRIVER_DESC);  MODULE_LICENSE("GPL");  |