Commit 17d80d56 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

USB: autosuspend for cdc-wdm

this patch implements

- suspend/resume
- aggressive autosuspend for the cdc-wdm driver
- pre/post_reset
Signed-off-by: default avatarOliver Neukum <oneukum@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 11ea859d
...@@ -88,6 +88,7 @@ struct wdm_device { ...@@ -88,6 +88,7 @@ struct wdm_device {
dma_addr_t ihandle; dma_addr_t ihandle;
struct mutex wlock; struct mutex wlock;
struct mutex rlock; struct mutex rlock;
struct mutex plock;
wait_queue_head_t wait; wait_queue_head_t wait;
struct work_struct rxwork; struct work_struct rxwork;
int werr; int werr;
...@@ -248,6 +249,7 @@ static void wdm_int_callback(struct urb *urb) ...@@ -248,6 +249,7 @@ static void wdm_int_callback(struct urb *urb)
static void kill_urbs(struct wdm_device *desc) static void kill_urbs(struct wdm_device *desc)
{ {
/* the order here is essential */
usb_kill_urb(desc->command); usb_kill_urb(desc->command);
usb_kill_urb(desc->validity); usb_kill_urb(desc->validity);
usb_kill_urb(desc->response); usb_kill_urb(desc->response);
...@@ -300,6 +302,9 @@ static ssize_t wdm_write ...@@ -300,6 +302,9 @@ static ssize_t wdm_write
if (r) if (r)
goto outnl; 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, r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
&desc->flags)); &desc->flags));
if (r < 0) if (r < 0)
...@@ -354,6 +359,8 @@ static ssize_t wdm_write ...@@ -354,6 +359,8 @@ static ssize_t wdm_write
req->wIndex); req->wIndex);
} }
out: out:
usb_autopm_put_interface(desc->intf);
outnp:
mutex_unlock(&desc->wlock); mutex_unlock(&desc->wlock);
outnl: outnl:
return rv < 0 ? rv : count; return rv < 0 ? rv : count;
...@@ -378,6 +385,11 @@ static ssize_t wdm_read ...@@ -378,6 +385,11 @@ static ssize_t wdm_read
rv = wait_event_interruptible(desc->wait, rv = wait_event_interruptible(desc->wait,
test_bit(WDM_READ, &desc->flags)); 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) { if (rv < 0) {
rv = -ERESTARTSYS; rv = -ERESTARTSYS;
goto err; goto err;
...@@ -485,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file) ...@@ -485,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file)
if (test_bit(WDM_DISCONNECTING, &desc->flags)) if (test_bit(WDM_DISCONNECTING, &desc->flags))
goto out; goto out;
desc->count++; ;
file->private_data = desc; file->private_data = desc;
rv = usb_submit_urb(desc->validity, GFP_KERNEL); rv = usb_autopm_get_interface(desc->intf);
if (rv < 0) { if (rv < 0) {
desc->count--; err("Error autopm - %d", rv);
err("Error submitting int urb - %d", rv);
goto out; 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: out:
mutex_unlock(&wdm_mutex); mutex_unlock(&wdm_mutex);
return rv; return rv;
...@@ -507,10 +529,15 @@ static int wdm_release(struct inode *inode, struct file *file) ...@@ -507,10 +529,15 @@ static int wdm_release(struct inode *inode, struct file *file)
struct wdm_device *desc = file->private_data; struct wdm_device *desc = file->private_data;
mutex_lock(&wdm_mutex); mutex_lock(&wdm_mutex);
mutex_lock(&desc->plock);
desc->count--; desc->count--;
mutex_unlock(&desc->plock);
if (!desc->count) { if (!desc->count) {
dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
kill_urbs(desc); kill_urbs(desc);
if (!test_bit(WDM_DISCONNECTING, &desc->flags))
desc->intf->needs_remote_wakeup = 0;
} }
mutex_unlock(&wdm_mutex); mutex_unlock(&wdm_mutex);
return 0; return 0;
...@@ -602,6 +629,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -602,6 +629,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto out; goto out;
mutex_init(&desc->wlock); mutex_init(&desc->wlock);
mutex_init(&desc->rlock); mutex_init(&desc->rlock);
mutex_init(&desc->plock);
spin_lock_init(&desc->iuspin); spin_lock_init(&desc->iuspin);
init_waitqueue_head(&desc->wait); init_waitqueue_head(&desc->wait);
desc->wMaxCommand = maxcom; desc->wMaxCommand = maxcom;
...@@ -703,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf) ...@@ -703,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf)
spin_lock_irqsave(&desc->iuspin, flags); spin_lock_irqsave(&desc->iuspin, flags);
set_bit(WDM_DISCONNECTING, &desc->flags); set_bit(WDM_DISCONNECTING, &desc->flags);
set_bit(WDM_READ, &desc->flags); set_bit(WDM_READ, &desc->flags);
/* to terminate pending flushes */
clear_bit(WDM_IN_USE, &desc->flags); clear_bit(WDM_IN_USE, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags); spin_unlock_irqrestore(&desc->iuspin, flags);
cancel_work_sync(&desc->rxwork); cancel_work_sync(&desc->rxwork);
...@@ -713,11 +742,77 @@ static void wdm_disconnect(struct usb_interface *intf) ...@@ -713,11 +742,77 @@ static void wdm_disconnect(struct usb_interface *intf)
mutex_unlock(&wdm_mutex); 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);
if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
rv = -EBUSY;
} else {
cancel_work_sync(&desc->rxwork);
kill_urbs(desc);
}
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 = { static struct usb_driver wdm_driver = {
.name = "cdc_wdm", .name = "cdc_wdm",
.probe = wdm_probe, .probe = wdm_probe,
.disconnect = wdm_disconnect, .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, .id_table = wdm_ids,
.supports_autosuspend = 1,
}; };
/* --- low level module stuff --- */ /* --- low level module stuff --- */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment