diff options
Diffstat (limited to 'drivers/usb/misc/usblcd.c')
-rw-r--r-- | drivers/usb/misc/usblcd.c | 67 |
1 files changed, 47 insertions, 20 deletions
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 12bad8a205a7..504f7221b0d0 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c | |||
@@ -45,13 +45,13 @@ struct usb_lcd { | |||
45 | struct kref kref; | 45 | struct kref kref; |
46 | struct semaphore limit_sem; /* to stop writes at full throttle from | 46 | struct semaphore limit_sem; /* to stop writes at full throttle from |
47 | * using up all RAM */ | 47 | * using up all RAM */ |
48 | struct usb_anchor submitted; /* URBs to wait for before suspend */ | ||
48 | }; | 49 | }; |
49 | #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) | 50 | #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) |
50 | 51 | ||
51 | #define USB_LCD_CONCURRENT_WRITES 5 | 52 | #define USB_LCD_CONCURRENT_WRITES 5 |
52 | 53 | ||
53 | static struct usb_driver lcd_driver; | 54 | static struct usb_driver lcd_driver; |
54 | static DEFINE_MUTEX(usb_lcd_open_mutex); | ||
55 | 55 | ||
56 | 56 | ||
57 | static void lcd_delete(struct kref *kref) | 57 | static void lcd_delete(struct kref *kref) |
@@ -68,35 +68,35 @@ static int lcd_open(struct inode *inode, struct file *file) | |||
68 | { | 68 | { |
69 | struct usb_lcd *dev; | 69 | struct usb_lcd *dev; |
70 | struct usb_interface *interface; | 70 | struct usb_interface *interface; |
71 | int subminor; | 71 | int subminor, r; |
72 | int retval = 0; | ||
73 | 72 | ||
74 | subminor = iminor(inode); | 73 | subminor = iminor(inode); |
75 | 74 | ||
76 | mutex_lock(&usb_lcd_open_mutex); | ||
77 | interface = usb_find_interface(&lcd_driver, subminor); | 75 | interface = usb_find_interface(&lcd_driver, subminor); |
78 | if (!interface) { | 76 | if (!interface) { |
79 | err ("USBLCD: %s - error, can't find device for minor %d", | 77 | err ("USBLCD: %s - error, can't find device for minor %d", |
80 | __FUNCTION__, subminor); | 78 | __FUNCTION__, subminor); |
81 | retval = -ENODEV; | 79 | return -ENODEV; |
82 | goto exit; | ||
83 | } | 80 | } |
84 | 81 | ||
85 | dev = usb_get_intfdata(interface); | 82 | dev = usb_get_intfdata(interface); |
86 | if (!dev) { | 83 | if (!dev) |
87 | retval = -ENODEV; | 84 | return -ENODEV; |
88 | goto exit; | ||
89 | } | ||
90 | 85 | ||
91 | /* increment our usage count for the device */ | 86 | /* increment our usage count for the device */ |
92 | kref_get(&dev->kref); | 87 | kref_get(&dev->kref); |
93 | 88 | ||
89 | /* grab a power reference */ | ||
90 | r = usb_autopm_get_interface(interface); | ||
91 | if (r < 0) { | ||
92 | kref_put(&dev->kref, lcd_delete); | ||
93 | return r; | ||
94 | } | ||
95 | |||
94 | /* save our object in the file's private structure */ | 96 | /* save our object in the file's private structure */ |
95 | file->private_data = dev; | 97 | file->private_data = dev; |
96 | 98 | ||
97 | exit: | 99 | return 0; |
98 | mutex_unlock(&usb_lcd_open_mutex); | ||
99 | return retval; | ||
100 | } | 100 | } |
101 | 101 | ||
102 | static int lcd_release(struct inode *inode, struct file *file) | 102 | static int lcd_release(struct inode *inode, struct file *file) |
@@ -108,6 +108,7 @@ static int lcd_release(struct inode *inode, struct file *file) | |||
108 | return -ENODEV; | 108 | return -ENODEV; |
109 | 109 | ||
110 | /* decrement the count on our device */ | 110 | /* decrement the count on our device */ |
111 | usb_autopm_put_interface(dev->interface); | ||
111 | kref_put(&dev->kref, lcd_delete); | 112 | kref_put(&dev->kref, lcd_delete); |
112 | return 0; | 113 | return 0; |
113 | } | 114 | } |
@@ -233,12 +234,14 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz | |||
233 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), | 234 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), |
234 | buf, count, lcd_write_bulk_callback, dev); | 235 | buf, count, lcd_write_bulk_callback, dev); |
235 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 236 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
237 | |||
238 | usb_anchor_urb(urb, &dev->submitted); | ||
236 | 239 | ||
237 | /* send the data out the bulk port */ | 240 | /* send the data out the bulk port */ |
238 | retval = usb_submit_urb(urb, GFP_KERNEL); | 241 | retval = usb_submit_urb(urb, GFP_KERNEL); |
239 | if (retval) { | 242 | if (retval) { |
240 | err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval); | 243 | err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval); |
241 | goto error; | 244 | goto error_unanchor; |
242 | } | 245 | } |
243 | 246 | ||
244 | /* release our reference to this urb, the USB core will eventually free it entirely */ | 247 | /* release our reference to this urb, the USB core will eventually free it entirely */ |
@@ -246,7 +249,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz | |||
246 | 249 | ||
247 | exit: | 250 | exit: |
248 | return count; | 251 | return count; |
249 | 252 | error_unanchor: | |
253 | usb_unanchor_urb(urb); | ||
250 | error: | 254 | error: |
251 | usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); | 255 | usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); |
252 | usb_free_urb(urb); | 256 | usb_free_urb(urb); |
@@ -291,6 +295,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id | |||
291 | } | 295 | } |
292 | kref_init(&dev->kref); | 296 | kref_init(&dev->kref); |
293 | sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); | 297 | sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); |
298 | init_usb_anchor(&dev->submitted); | ||
294 | 299 | ||
295 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); | 300 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); |
296 | dev->interface = interface; | 301 | dev->interface = interface; |
@@ -358,22 +363,41 @@ error: | |||
358 | return retval; | 363 | return retval; |
359 | } | 364 | } |
360 | 365 | ||
366 | static void lcd_draw_down(struct usb_lcd *dev) | ||
367 | { | ||
368 | int time; | ||
369 | |||
370 | time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); | ||
371 | if (!time) | ||
372 | usb_kill_anchored_urbs(&dev->submitted); | ||
373 | } | ||
374 | |||
375 | static int lcd_suspend(struct usb_interface *intf, pm_message_t message) | ||
376 | { | ||
377 | struct usb_lcd *dev = usb_get_intfdata(intf); | ||
378 | |||
379 | if (!dev) | ||
380 | return 0; | ||
381 | lcd_draw_down(dev); | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static int lcd_resume (struct usb_interface *intf) | ||
386 | { | ||
387 | return 0; | ||
388 | } | ||
389 | |||
361 | static void lcd_disconnect(struct usb_interface *interface) | 390 | static void lcd_disconnect(struct usb_interface *interface) |
362 | { | 391 | { |
363 | struct usb_lcd *dev; | 392 | struct usb_lcd *dev; |
364 | int minor = interface->minor; | 393 | int minor = interface->minor; |
365 | 394 | ||
366 | /* prevent skel_open() from racing skel_disconnect() */ | ||
367 | mutex_lock(&usb_lcd_open_mutex); | ||
368 | |||
369 | dev = usb_get_intfdata(interface); | 395 | dev = usb_get_intfdata(interface); |
370 | usb_set_intfdata(interface, NULL); | 396 | usb_set_intfdata(interface, NULL); |
371 | 397 | ||
372 | /* give back our minor */ | 398 | /* give back our minor */ |
373 | usb_deregister_dev(interface, &lcd_class); | 399 | usb_deregister_dev(interface, &lcd_class); |
374 | 400 | ||
375 | mutex_unlock(&usb_lcd_open_mutex); | ||
376 | |||
377 | /* decrement our usage count */ | 401 | /* decrement our usage count */ |
378 | kref_put(&dev->kref, lcd_delete); | 402 | kref_put(&dev->kref, lcd_delete); |
379 | 403 | ||
@@ -384,7 +408,10 @@ static struct usb_driver lcd_driver = { | |||
384 | .name = "usblcd", | 408 | .name = "usblcd", |
385 | .probe = lcd_probe, | 409 | .probe = lcd_probe, |
386 | .disconnect = lcd_disconnect, | 410 | .disconnect = lcd_disconnect, |
411 | .suspend = lcd_suspend, | ||
412 | .resume = lcd_resume, | ||
387 | .id_table = id_table, | 413 | .id_table = id_table, |
414 | .supports_autosuspend = 1, | ||
388 | }; | 415 | }; |
389 | 416 | ||
390 | static int __init usb_lcd_init(void) | 417 | static int __init usb_lcd_init(void) |