Commit 7bbe990c authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

USB: autosuspend for usblcd

this patch implements autosuspend for the usblcd driver. It uses
the new usb_anchor infrastructure. Many thanks to Georges for testing.
Signed-off-by: default avatarOliver Neukum <oneukum@suse.de>
Cc: Georges Toth <g.toth@e-biz.lu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 55b3fd41
...@@ -45,6 +45,7 @@ struct usb_lcd { ...@@ -45,6 +45,7 @@ struct usb_lcd {
struct kref kref; struct kref kref;
struct semaphore limit_sem; /* to stop writes at full throttle from struct semaphore limit_sem; /* to stop writes at full throttle from
* using up all RAM */ * using up all RAM */
struct usb_anchor submitted; /* URBs to wait for before suspend */
}; };
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
...@@ -67,7 +68,7 @@ static int lcd_open(struct inode *inode, struct file *file) ...@@ -67,7 +68,7 @@ static int lcd_open(struct inode *inode, struct file *file)
{ {
struct usb_lcd *dev; struct usb_lcd *dev;
struct usb_interface *interface; struct usb_interface *interface;
int subminor; int subminor, r;
subminor = iminor(inode); subminor = iminor(inode);
...@@ -85,6 +86,13 @@ static int lcd_open(struct inode *inode, struct file *file) ...@@ -85,6 +86,13 @@ static int lcd_open(struct inode *inode, struct file *file)
/* increment our usage count for the device */ /* increment our usage count for the device */
kref_get(&dev->kref); kref_get(&dev->kref);
/* grab a power reference */
r = usb_autopm_get_interface(interface);
if (r < 0) {
kref_put(&dev->kref, lcd_delete);
return r;
}
/* save our object in the file's private structure */ /* save our object in the file's private structure */
file->private_data = dev; file->private_data = dev;
...@@ -100,6 +108,7 @@ static int lcd_release(struct inode *inode, struct file *file) ...@@ -100,6 +108,7 @@ static int lcd_release(struct inode *inode, struct file *file)
return -ENODEV; return -ENODEV;
/* decrement the count on our device */ /* decrement the count on our device */
usb_autopm_put_interface(dev->interface);
kref_put(&dev->kref, lcd_delete); kref_put(&dev->kref, lcd_delete);
return 0; return 0;
} }
...@@ -225,12 +234,14 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz ...@@ -225,12 +234,14 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, lcd_write_bulk_callback, dev); buf, count, lcd_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
/* send the data out the bulk port */ /* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL); retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) { if (retval) {
err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval); err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error; goto error_unanchor;
} }
/* release our reference to this urb, the USB core will eventually free it entirely */ /* release our reference to this urb, the USB core will eventually free it entirely */
...@@ -238,7 +249,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz ...@@ -238,7 +249,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz
exit: exit:
return count; return count;
error_unanchor:
usb_unanchor_urb(urb);
error: error:
usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
usb_free_urb(urb); usb_free_urb(urb);
...@@ -283,6 +295,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id ...@@ -283,6 +295,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
} }
kref_init(&dev->kref); kref_init(&dev->kref);
sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
init_usb_anchor(&dev->submitted);
dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface; dev->interface = interface;
...@@ -350,6 +363,30 @@ error: ...@@ -350,6 +363,30 @@ error:
return retval; return retval;
} }
static void lcd_draw_down(struct usb_lcd *dev)
{
int time;
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&dev->submitted);
}
static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_lcd *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
lcd_draw_down(dev);
return 0;
}
static int lcd_resume (struct usb_interface *intf)
{
return 0;
}
static void lcd_disconnect(struct usb_interface *interface) static void lcd_disconnect(struct usb_interface *interface)
{ {
struct usb_lcd *dev; struct usb_lcd *dev;
...@@ -371,7 +408,10 @@ static struct usb_driver lcd_driver = { ...@@ -371,7 +408,10 @@ static struct usb_driver lcd_driver = {
.name = "usblcd", .name = "usblcd",
.probe = lcd_probe, .probe = lcd_probe,
.disconnect = lcd_disconnect, .disconnect = lcd_disconnect,
.suspend = lcd_suspend,
.resume = lcd_resume,
.id_table = id_table, .id_table = id_table,
.supports_autosuspend = 1,
}; };
static int __init usb_lcd_init(void) static int __init usb_lcd_init(void)
......
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