diff options
author | Oliver Neukum <oliver@neukum.org> | 2008-06-24 09:56:10 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-21 18:16:36 -0400 |
commit | 17d80d562fd78a035e994afde88f354973e76236 (patch) | |
tree | f7fb0410119569b88da3f7e228dc700e1ccf357b /drivers/usb/class | |
parent | 11ea859d64b69a747d6b060b9ed1520eab1161fe (diff) |
USB: autosuspend for cdc-wdm
this patch implements
- suspend/resume
- aggressive autosuspend for the cdc-wdm driver
- pre/post_reset
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 107 |
1 files changed, 101 insertions, 6 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 3a656f8f9935..1103ce7016b1 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
@@ -88,6 +88,7 @@ struct wdm_device { | |||
88 | dma_addr_t ihandle; | 88 | dma_addr_t ihandle; |
89 | struct mutex wlock; | 89 | struct mutex wlock; |
90 | struct mutex rlock; | 90 | struct mutex rlock; |
91 | struct mutex plock; | ||
91 | wait_queue_head_t wait; | 92 | wait_queue_head_t wait; |
92 | struct work_struct rxwork; | 93 | struct work_struct rxwork; |
93 | int werr; | 94 | int werr; |
@@ -248,6 +249,7 @@ exit: | |||
248 | 249 | ||
249 | static void kill_urbs(struct wdm_device *desc) | 250 | static void kill_urbs(struct wdm_device *desc) |
250 | { | 251 | { |
252 | /* the order here is essential */ | ||
251 | usb_kill_urb(desc->command); | 253 | usb_kill_urb(desc->command); |
252 | usb_kill_urb(desc->validity); | 254 | usb_kill_urb(desc->validity); |
253 | usb_kill_urb(desc->response); | 255 | usb_kill_urb(desc->response); |
@@ -300,6 +302,9 @@ static ssize_t wdm_write | |||
300 | if (r) | 302 | if (r) |
301 | goto outnl; | 303 | goto outnl; |
302 | 304 | ||
305 | r = usb_autopm_get_interface(desc->intf); | ||
306 | if (r < 0) | ||
307 | goto outnp; | ||
303 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, | 308 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, |
304 | &desc->flags)); | 309 | &desc->flags)); |
305 | if (r < 0) | 310 | if (r < 0) |
@@ -354,6 +359,8 @@ static ssize_t wdm_write | |||
354 | req->wIndex); | 359 | req->wIndex); |
355 | } | 360 | } |
356 | out: | 361 | out: |
362 | usb_autopm_put_interface(desc->intf); | ||
363 | outnp: | ||
357 | mutex_unlock(&desc->wlock); | 364 | mutex_unlock(&desc->wlock); |
358 | outnl: | 365 | outnl: |
359 | return rv < 0 ? rv : count; | 366 | return rv < 0 ? rv : count; |
@@ -378,6 +385,11 @@ retry: | |||
378 | rv = wait_event_interruptible(desc->wait, | 385 | rv = wait_event_interruptible(desc->wait, |
379 | test_bit(WDM_READ, &desc->flags)); | 386 | test_bit(WDM_READ, &desc->flags)); |
380 | 387 | ||
388 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { | ||
389 | rv = -ENODEV; | ||
390 | goto err; | ||
391 | } | ||
392 | usb_mark_last_busy(interface_to_usbdev(desc->intf)); | ||
381 | if (rv < 0) { | 393 | if (rv < 0) { |
382 | rv = -ERESTARTSYS; | 394 | rv = -ERESTARTSYS; |
383 | goto err; | 395 | goto err; |
@@ -485,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file) | |||
485 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) | 497 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) |
486 | goto out; | 498 | goto out; |
487 | 499 | ||
488 | desc->count++; | 500 | ; |
489 | file->private_data = desc; | 501 | file->private_data = desc; |
490 | 502 | ||
491 | rv = usb_submit_urb(desc->validity, GFP_KERNEL); | 503 | rv = usb_autopm_get_interface(desc->intf); |
492 | |||
493 | if (rv < 0) { | 504 | if (rv < 0) { |
494 | desc->count--; | 505 | err("Error autopm - %d", rv); |
495 | err("Error submitting int urb - %d", rv); | ||
496 | goto out; | 506 | goto out; |
497 | } | 507 | } |
498 | rv = 0; | 508 | intf->needs_remote_wakeup = 1; |
499 | 509 | ||
510 | mutex_lock(&desc->plock); | ||
511 | if (!desc->count++) { | ||
512 | rv = usb_submit_urb(desc->validity, GFP_KERNEL); | ||
513 | if (rv < 0) { | ||
514 | desc->count--; | ||
515 | err("Error submitting int urb - %d", rv); | ||
516 | } | ||
517 | } else { | ||
518 | rv = 0; | ||
519 | } | ||
520 | mutex_unlock(&desc->plock); | ||
521 | usb_autopm_put_interface(desc->intf); | ||
500 | out: | 522 | out: |
501 | mutex_unlock(&wdm_mutex); | 523 | mutex_unlock(&wdm_mutex); |
502 | return rv; | 524 | return rv; |
@@ -507,10 +529,15 @@ static int wdm_release(struct inode *inode, struct file *file) | |||
507 | struct wdm_device *desc = file->private_data; | 529 | struct wdm_device *desc = file->private_data; |
508 | 530 | ||
509 | mutex_lock(&wdm_mutex); | 531 | mutex_lock(&wdm_mutex); |
532 | mutex_lock(&desc->plock); | ||
510 | desc->count--; | 533 | desc->count--; |
534 | mutex_unlock(&desc->plock); | ||
535 | |||
511 | if (!desc->count) { | 536 | if (!desc->count) { |
512 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); | 537 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); |
513 | kill_urbs(desc); | 538 | kill_urbs(desc); |
539 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) | ||
540 | desc->intf->needs_remote_wakeup = 0; | ||
514 | } | 541 | } |
515 | mutex_unlock(&wdm_mutex); | 542 | mutex_unlock(&wdm_mutex); |
516 | return 0; | 543 | return 0; |
@@ -602,6 +629,7 @@ next_desc: | |||
602 | goto out; | 629 | goto out; |
603 | mutex_init(&desc->wlock); | 630 | mutex_init(&desc->wlock); |
604 | mutex_init(&desc->rlock); | 631 | mutex_init(&desc->rlock); |
632 | mutex_init(&desc->plock); | ||
605 | spin_lock_init(&desc->iuspin); | 633 | spin_lock_init(&desc->iuspin); |
606 | init_waitqueue_head(&desc->wait); | 634 | init_waitqueue_head(&desc->wait); |
607 | desc->wMaxCommand = maxcom; | 635 | desc->wMaxCommand = maxcom; |
@@ -703,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf) | |||
703 | spin_lock_irqsave(&desc->iuspin, flags); | 731 | spin_lock_irqsave(&desc->iuspin, flags); |
704 | set_bit(WDM_DISCONNECTING, &desc->flags); | 732 | set_bit(WDM_DISCONNECTING, &desc->flags); |
705 | set_bit(WDM_READ, &desc->flags); | 733 | set_bit(WDM_READ, &desc->flags); |
734 | /* to terminate pending flushes */ | ||
706 | clear_bit(WDM_IN_USE, &desc->flags); | 735 | clear_bit(WDM_IN_USE, &desc->flags); |
707 | spin_unlock_irqrestore(&desc->iuspin, flags); | 736 | spin_unlock_irqrestore(&desc->iuspin, flags); |
708 | cancel_work_sync(&desc->rxwork); | 737 | cancel_work_sync(&desc->rxwork); |
@@ -713,11 +742,77 @@ static void wdm_disconnect(struct usb_interface *intf) | |||
713 | mutex_unlock(&wdm_mutex); | 742 | mutex_unlock(&wdm_mutex); |
714 | } | 743 | } |
715 | 744 | ||
745 | static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | ||
746 | { | ||
747 | struct wdm_device *desc = usb_get_intfdata(intf); | ||
748 | int rv = 0; | ||
749 | |||
750 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); | ||
751 | |||
752 | mutex_lock(&desc->plock); | ||
753 | if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { | ||
754 | rv = -EBUSY; | ||
755 | } else { | ||
756 | cancel_work_sync(&desc->rxwork); | ||
757 | kill_urbs(desc); | ||
758 | } | ||
759 | mutex_unlock(&desc->plock); | ||
760 | |||
761 | return rv; | ||
762 | } | ||
763 | |||
764 | static int recover_from_urb_loss(struct wdm_device *desc) | ||
765 | { | ||
766 | int rv = 0; | ||
767 | |||
768 | if (desc->count) { | ||
769 | rv = usb_submit_urb(desc->validity, GFP_NOIO); | ||
770 | if (rv < 0) | ||
771 | err("Error resume submitting int urb - %d", rv); | ||
772 | } | ||
773 | return rv; | ||
774 | } | ||
775 | static int wdm_resume(struct usb_interface *intf) | ||
776 | { | ||
777 | struct wdm_device *desc = usb_get_intfdata(intf); | ||
778 | int rv; | ||
779 | |||
780 | dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); | ||
781 | mutex_lock(&desc->plock); | ||
782 | rv = recover_from_urb_loss(desc); | ||
783 | mutex_unlock(&desc->plock); | ||
784 | return rv; | ||
785 | } | ||
786 | |||
787 | static int wdm_pre_reset(struct usb_interface *intf) | ||
788 | { | ||
789 | struct wdm_device *desc = usb_get_intfdata(intf); | ||
790 | |||
791 | mutex_lock(&desc->plock); | ||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static int wdm_post_reset(struct usb_interface *intf) | ||
796 | { | ||
797 | struct wdm_device *desc = usb_get_intfdata(intf); | ||
798 | int rv; | ||
799 | |||
800 | rv = recover_from_urb_loss(desc); | ||
801 | mutex_unlock(&desc->plock); | ||
802 | return 0; | ||
803 | } | ||
804 | |||
716 | static struct usb_driver wdm_driver = { | 805 | static struct usb_driver wdm_driver = { |
717 | .name = "cdc_wdm", | 806 | .name = "cdc_wdm", |
718 | .probe = wdm_probe, | 807 | .probe = wdm_probe, |
719 | .disconnect = wdm_disconnect, | 808 | .disconnect = wdm_disconnect, |
809 | .suspend = wdm_suspend, | ||
810 | .resume = wdm_resume, | ||
811 | .reset_resume = wdm_resume, | ||
812 | .pre_reset = wdm_pre_reset, | ||
813 | .post_reset = wdm_post_reset, | ||
720 | .id_table = wdm_ids, | 814 | .id_table = wdm_ids, |
815 | .supports_autosuspend = 1, | ||
721 | }; | 816 | }; |
722 | 817 | ||
723 | /* --- low level module stuff --- */ | 818 | /* --- low level module stuff --- */ |