diff options
author | Oliver Neukum <oliver@neukum.org> | 2010-02-27 14:56:47 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-19 10:24:17 -0400 |
commit | d93d16e9aa58887feadd999ea26b7b8139e98b56 (patch) | |
tree | 47cd5e95bee9ccd52aa5ccfff4c87af78d605ad7 /drivers/usb | |
parent | 62e6685470fb04fb7688ecef96c39160498721d5 (diff) |
usb: cdc-wdm: Fix order in disconnect and fix locking
- as the callback can schedule work, URBs must be killed first
- if the driver causes an autoresume, the caller must handle locking
Signed-off-by: Oliver Neukum <neukum@b1-systems.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 07c12974fe14..b57490508860 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
@@ -776,9 +776,9 @@ static void wdm_disconnect(struct usb_interface *intf) | |||
776 | /* to terminate pending flushes */ | 776 | /* to terminate pending flushes */ |
777 | clear_bit(WDM_IN_USE, &desc->flags); | 777 | clear_bit(WDM_IN_USE, &desc->flags); |
778 | spin_unlock_irqrestore(&desc->iuspin, flags); | 778 | spin_unlock_irqrestore(&desc->iuspin, flags); |
779 | cancel_work_sync(&desc->rxwork); | ||
780 | mutex_lock(&desc->lock); | 779 | mutex_lock(&desc->lock); |
781 | kill_urbs(desc); | 780 | kill_urbs(desc); |
781 | cancel_work_sync(&desc->rxwork); | ||
782 | mutex_unlock(&desc->lock); | 782 | mutex_unlock(&desc->lock); |
783 | wake_up_all(&desc->wait); | 783 | wake_up_all(&desc->wait); |
784 | if (!desc->count) | 784 | if (!desc->count) |
@@ -786,6 +786,7 @@ static void wdm_disconnect(struct usb_interface *intf) | |||
786 | mutex_unlock(&wdm_mutex); | 786 | mutex_unlock(&wdm_mutex); |
787 | } | 787 | } |
788 | 788 | ||
789 | #ifdef CONFIG_PM | ||
789 | static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | 790 | static int wdm_suspend(struct usb_interface *intf, pm_message_t message) |
790 | { | 791 | { |
791 | struct wdm_device *desc = usb_get_intfdata(intf); | 792 | struct wdm_device *desc = usb_get_intfdata(intf); |
@@ -793,27 +794,30 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | |||
793 | 794 | ||
794 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); | 795 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); |
795 | 796 | ||
796 | mutex_lock(&desc->lock); | 797 | /* if this is an autosuspend the caller does the locking */ |
798 | if (!(message.event & PM_EVENT_AUTO)) | ||
799 | mutex_lock(&desc->lock); | ||
797 | spin_lock_irq(&desc->iuspin); | 800 | spin_lock_irq(&desc->iuspin); |
798 | #ifdef CONFIG_PM | 801 | |
799 | if ((message.event & PM_EVENT_AUTO) && | 802 | if ((message.event & PM_EVENT_AUTO) && |
800 | (test_bit(WDM_IN_USE, &desc->flags) | 803 | (test_bit(WDM_IN_USE, &desc->flags) |
801 | || test_bit(WDM_RESPONDING, &desc->flags))) { | 804 | || test_bit(WDM_RESPONDING, &desc->flags))) { |
802 | spin_unlock_irq(&desc->iuspin); | 805 | spin_unlock_irq(&desc->iuspin); |
803 | rv = -EBUSY; | 806 | rv = -EBUSY; |
804 | } else { | 807 | } else { |
805 | #endif | 808 | |
806 | set_bit(WDM_SUSPENDING, &desc->flags); | 809 | set_bit(WDM_SUSPENDING, &desc->flags); |
807 | spin_unlock_irq(&desc->iuspin); | 810 | spin_unlock_irq(&desc->iuspin); |
808 | cancel_work_sync(&desc->rxwork); | 811 | /* callback submits work - order is essential */ |
809 | kill_urbs(desc); | 812 | kill_urbs(desc); |
810 | #ifdef CONFIG_PM | 813 | cancel_work_sync(&desc->rxwork); |
811 | } | 814 | } |
812 | #endif | 815 | if (!(message.event & PM_EVENT_AUTO)) |
813 | mutex_unlock(&desc->lock); | 816 | mutex_unlock(&desc->lock); |
814 | 817 | ||
815 | return rv; | 818 | return rv; |
816 | } | 819 | } |
820 | #endif | ||
817 | 821 | ||
818 | static int recover_from_urb_loss(struct wdm_device *desc) | 822 | static int recover_from_urb_loss(struct wdm_device *desc) |
819 | { | 823 | { |
@@ -827,6 +831,8 @@ static int recover_from_urb_loss(struct wdm_device *desc) | |||
827 | } | 831 | } |
828 | return rv; | 832 | return rv; |
829 | } | 833 | } |
834 | |||
835 | #ifdef CONFIG_PM | ||
830 | static int wdm_resume(struct usb_interface *intf) | 836 | static int wdm_resume(struct usb_interface *intf) |
831 | { | 837 | { |
832 | struct wdm_device *desc = usb_get_intfdata(intf); | 838 | struct wdm_device *desc = usb_get_intfdata(intf); |
@@ -839,6 +845,7 @@ static int wdm_resume(struct usb_interface *intf) | |||
839 | mutex_unlock(&desc->lock); | 845 | mutex_unlock(&desc->lock); |
840 | return rv; | 846 | return rv; |
841 | } | 847 | } |
848 | #endif | ||
842 | 849 | ||
843 | static int wdm_pre_reset(struct usb_interface *intf) | 850 | static int wdm_pre_reset(struct usb_interface *intf) |
844 | { | 851 | { |
@@ -862,9 +869,11 @@ static struct usb_driver wdm_driver = { | |||
862 | .name = "cdc_wdm", | 869 | .name = "cdc_wdm", |
863 | .probe = wdm_probe, | 870 | .probe = wdm_probe, |
864 | .disconnect = wdm_disconnect, | 871 | .disconnect = wdm_disconnect, |
872 | #ifdef CONFIG_PM | ||
865 | .suspend = wdm_suspend, | 873 | .suspend = wdm_suspend, |
866 | .resume = wdm_resume, | 874 | .resume = wdm_resume, |
867 | .reset_resume = wdm_resume, | 875 | .reset_resume = wdm_resume, |
876 | #endif | ||
868 | .pre_reset = wdm_pre_reset, | 877 | .pre_reset = wdm_pre_reset, |
869 | .post_reset = wdm_post_reset, | 878 | .post_reset = wdm_post_reset, |
870 | .id_table = wdm_ids, | 879 | .id_table = wdm_ids, |