diff options
author | Oliver Neukum <oneukum@suse.de> | 2007-06-13 17:13:31 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 16:34:38 -0700 |
commit | 7bbe990c989ee16f2c1be3e4ae28f8004bec788c (patch) | |
tree | c2ea2abc369f2cabeb46cb6c5d3125d3e58ca7e6 /drivers/usb | |
parent | 55b3fd41b0846929f68b5fb1058ad8077289f584 (diff) |
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: Oliver Neukum <oneukum@suse.de>
Cc: Georges Toth <g.toth@e-biz.lu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/misc/usblcd.c | 46 |
1 files changed, 43 insertions, 3 deletions
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 6e093c2aac2..504f7221b0d 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -45,6 +45,7 @@ struct usb_lcd { struct kref kref; struct semaphore limit_sem; /* to stop writes at full throttle from * 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) @@ -67,7 +68,7 @@ static int lcd_open(struct inode *inode, struct file *file) { struct usb_lcd *dev; struct usb_interface *interface; - int subminor; + int subminor, r; subminor = iminor(inode); @@ -85,6 +86,13 @@ static int lcd_open(struct inode *inode, struct file *file) /* increment our usage count for the device */ 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 */ file->private_data = dev; @@ -100,6 +108,7 @@ static int lcd_release(struct inode *inode, struct file *file) return -ENODEV; /* decrement the count on our device */ + usb_autopm_put_interface(dev->interface); kref_put(&dev->kref, lcd_delete); return 0; } @@ -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), buf, count, lcd_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &dev->submitted); /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (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 */ @@ -238,7 +249,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz exit: return count; - +error_unanchor: + usb_unanchor_urb(urb); error: usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); usb_free_urb(urb); @@ -283,6 +295,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id } kref_init(&dev->kref); sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); + init_usb_anchor(&dev->submitted); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; @@ -350,6 +363,30 @@ error: 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) { struct usb_lcd *dev; @@ -371,7 +408,10 @@ static struct usb_driver lcd_driver = { .name = "usblcd", .probe = lcd_probe, .disconnect = lcd_disconnect, + .suspend = lcd_suspend, + .resume = lcd_resume, .id_table = id_table, + .supports_autosuspend = 1, }; static int __init usb_lcd_init(void) |