diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-12 18:49:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-12 18:49:10 -0400 |
commit | 117494a1b65183f0e3fcc817b07944bc5c465050 (patch) | |
tree | c375cf06bdf869f2b870fe61808b060c4fadab45 /drivers/usb/core/driver.c | |
parent | 4d5709a7b7d54fc5882d2943a14988a92d48c00a (diff) | |
parent | d1aa3e6aa8edfeb864af7c930523d9e588b28bea (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (142 commits)
USB: fix race in autosuspend reschedule
atmel_usba_udc: Keep track of the device status
USB: Nikon D40X unusual_devs entry
USB: serial core should respect driver requirements
USB: documentation for USB power management
USB: skip autosuspended devices during system resume
USB: mutual exclusion for EHCI init and port resets
USB: allow usbstorage to have LUNS greater than 2Tb
USB: Adding support for SHARP WS011SH to ipaq.c
USB: add atmel_usba_udc driver
USB: ohci SSB bus glue
USB: ehci build fixes on au1xxx, ppc-soc
USB: add runtime frame_no quirk for big-endian OHCI
USB: funsoft: Fix termios
USB: visor: termios bits
USB: unusual_devs entry for Nikon DSC D2Xs
USB: re-remove <linux/usb_sl811.h>
USB: move <linux/usb_gadget.h> to <linux/usb/gadget.h>
USB: Export URB statistics for powertop
USB: serial gadget: Disable endpoints on unload
...
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r-- | drivers/usb/core/driver.c | 90 |
1 files changed, 57 insertions, 33 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 63b1243a913..c27bc080d84 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -202,6 +202,11 @@ static int usb_probe_interface(struct device *dev) | |||
202 | intf = to_usb_interface(dev); | 202 | intf = to_usb_interface(dev); |
203 | udev = interface_to_usbdev(intf); | 203 | udev = interface_to_usbdev(intf); |
204 | 204 | ||
205 | if (udev->authorized == 0) { | ||
206 | dev_err(&intf->dev, "Device is not authorized for usage\n"); | ||
207 | return -ENODEV; | ||
208 | } | ||
209 | |||
205 | id = usb_match_id(intf, driver->id_table); | 210 | id = usb_match_id(intf, driver->id_table); |
206 | if (!id) | 211 | if (!id) |
207 | id = usb_match_dynamic_id(intf, driver); | 212 | id = usb_match_dynamic_id(intf, driver); |
@@ -945,11 +950,11 @@ done: | |||
945 | #ifdef CONFIG_USB_SUSPEND | 950 | #ifdef CONFIG_USB_SUSPEND |
946 | 951 | ||
947 | /* Internal routine to check whether we may autosuspend a device. */ | 952 | /* Internal routine to check whether we may autosuspend a device. */ |
948 | static int autosuspend_check(struct usb_device *udev) | 953 | static int autosuspend_check(struct usb_device *udev, int reschedule) |
949 | { | 954 | { |
950 | int i; | 955 | int i; |
951 | struct usb_interface *intf; | 956 | struct usb_interface *intf; |
952 | unsigned long suspend_time; | 957 | unsigned long suspend_time, j; |
953 | 958 | ||
954 | /* For autosuspend, fail fast if anything is in use or autosuspend | 959 | /* For autosuspend, fail fast if anything is in use or autosuspend |
955 | * is disabled. Also fail if any interfaces require remote wakeup | 960 | * is disabled. Also fail if any interfaces require remote wakeup |
@@ -991,20 +996,20 @@ static int autosuspend_check(struct usb_device *udev) | |||
991 | } | 996 | } |
992 | 997 | ||
993 | /* If everything is okay but the device hasn't been idle for long | 998 | /* If everything is okay but the device hasn't been idle for long |
994 | * enough, queue a delayed autosuspend request. | 999 | * enough, queue a delayed autosuspend request. If the device |
1000 | * _has_ been idle for long enough and the reschedule flag is set, | ||
1001 | * likewise queue a delayed (1 second) autosuspend request. | ||
995 | */ | 1002 | */ |
996 | if (time_after(suspend_time, jiffies)) { | 1003 | j = jiffies; |
1004 | if (time_before(j, suspend_time)) | ||
1005 | reschedule = 1; | ||
1006 | else | ||
1007 | suspend_time = j + HZ; | ||
1008 | if (reschedule) { | ||
997 | if (!timer_pending(&udev->autosuspend.timer)) { | 1009 | if (!timer_pending(&udev->autosuspend.timer)) { |
998 | |||
999 | /* The value of jiffies may change between the | ||
1000 | * time_after() comparison above and the subtraction | ||
1001 | * below. That's okay; the system behaves sanely | ||
1002 | * when a timer is registered for the present moment | ||
1003 | * or for the past. | ||
1004 | */ | ||
1005 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, | 1010 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, |
1006 | round_jiffies_relative(suspend_time - jiffies)); | 1011 | round_jiffies_relative(suspend_time - j)); |
1007 | } | 1012 | } |
1008 | return -EAGAIN; | 1013 | return -EAGAIN; |
1009 | } | 1014 | } |
1010 | return 0; | 1015 | return 0; |
@@ -1012,7 +1017,7 @@ static int autosuspend_check(struct usb_device *udev) | |||
1012 | 1017 | ||
1013 | #else | 1018 | #else |
1014 | 1019 | ||
1015 | static inline int autosuspend_check(struct usb_device *udev) | 1020 | static inline int autosuspend_check(struct usb_device *udev, int reschedule) |
1016 | { | 1021 | { |
1017 | return 0; | 1022 | return 0; |
1018 | } | 1023 | } |
@@ -1069,7 +1074,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1069 | udev->do_remote_wakeup = device_may_wakeup(&udev->dev); | 1074 | udev->do_remote_wakeup = device_may_wakeup(&udev->dev); |
1070 | 1075 | ||
1071 | if (udev->auto_pm) { | 1076 | if (udev->auto_pm) { |
1072 | status = autosuspend_check(udev); | 1077 | status = autosuspend_check(udev, 0); |
1073 | if (status < 0) | 1078 | if (status < 0) |
1074 | goto done; | 1079 | goto done; |
1075 | } | 1080 | } |
@@ -1083,15 +1088,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1083 | break; | 1088 | break; |
1084 | } | 1089 | } |
1085 | } | 1090 | } |
1086 | if (status == 0) { | 1091 | if (status == 0) |
1087 | |||
1088 | /* Non-root devices don't need to do anything for FREEZE | ||
1089 | * or PRETHAW. */ | ||
1090 | if (udev->parent && (msg.event == PM_EVENT_FREEZE || | ||
1091 | msg.event == PM_EVENT_PRETHAW)) | ||
1092 | goto done; | ||
1093 | status = usb_suspend_device(udev, msg); | 1092 | status = usb_suspend_device(udev, msg); |
1094 | } | ||
1095 | 1093 | ||
1096 | /* If the suspend failed, resume interfaces that did get suspended */ | 1094 | /* If the suspend failed, resume interfaces that did get suspended */ |
1097 | if (status != 0) { | 1095 | if (status != 0) { |
@@ -1102,12 +1100,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1102 | 1100 | ||
1103 | /* Try another autosuspend when the interfaces aren't busy */ | 1101 | /* Try another autosuspend when the interfaces aren't busy */ |
1104 | if (udev->auto_pm) | 1102 | if (udev->auto_pm) |
1105 | autosuspend_check(udev); | 1103 | autosuspend_check(udev, status == -EBUSY); |
1106 | 1104 | ||
1107 | /* If the suspend succeeded, propagate it up the tree */ | 1105 | /* If the suspend succeeded then prevent any more URB submissions, |
1106 | * flush any outstanding URBs, and propagate the suspend up the tree. | ||
1107 | */ | ||
1108 | } else { | 1108 | } else { |
1109 | cancel_delayed_work(&udev->autosuspend); | 1109 | cancel_delayed_work(&udev->autosuspend); |
1110 | if (parent) | 1110 | udev->can_submit = 0; |
1111 | for (i = 0; i < 16; ++i) { | ||
1112 | usb_hcd_flush_endpoint(udev, udev->ep_out[i]); | ||
1113 | usb_hcd_flush_endpoint(udev, udev->ep_in[i]); | ||
1114 | } | ||
1115 | |||
1116 | /* If this is just a FREEZE or a PRETHAW, udev might | ||
1117 | * not really be suspended. Only true suspends get | ||
1118 | * propagated up the device tree. | ||
1119 | */ | ||
1120 | if (parent && udev->state == USB_STATE_SUSPENDED) | ||
1111 | usb_autosuspend_device(parent); | 1121 | usb_autosuspend_device(parent); |
1112 | } | 1122 | } |
1113 | 1123 | ||
@@ -1156,6 +1166,7 @@ static int usb_resume_both(struct usb_device *udev) | |||
1156 | status = -ENODEV; | 1166 | status = -ENODEV; |
1157 | goto done; | 1167 | goto done; |
1158 | } | 1168 | } |
1169 | udev->can_submit = 1; | ||
1159 | 1170 | ||
1160 | /* Propagate the resume up the tree, if necessary */ | 1171 | /* Propagate the resume up the tree, if necessary */ |
1161 | if (udev->state == USB_STATE_SUSPENDED) { | 1172 | if (udev->state == USB_STATE_SUSPENDED) { |
@@ -1529,9 +1540,21 @@ int usb_external_resume_device(struct usb_device *udev) | |||
1529 | 1540 | ||
1530 | static int usb_suspend(struct device *dev, pm_message_t message) | 1541 | static int usb_suspend(struct device *dev, pm_message_t message) |
1531 | { | 1542 | { |
1543 | struct usb_device *udev; | ||
1544 | |||
1532 | if (!is_usb_device(dev)) /* Ignore PM for interfaces */ | 1545 | if (!is_usb_device(dev)) /* Ignore PM for interfaces */ |
1533 | return 0; | 1546 | return 0; |
1534 | return usb_external_suspend_device(to_usb_device(dev), message); | 1547 | udev = to_usb_device(dev); |
1548 | |||
1549 | /* If udev is already suspended, we can skip this suspend and | ||
1550 | * we should also skip the upcoming system resume. */ | ||
1551 | if (udev->state == USB_STATE_SUSPENDED) { | ||
1552 | udev->skip_sys_resume = 1; | ||
1553 | return 0; | ||
1554 | } | ||
1555 | |||
1556 | udev->skip_sys_resume = 0; | ||
1557 | return usb_external_suspend_device(udev, message); | ||
1535 | } | 1558 | } |
1536 | 1559 | ||
1537 | static int usb_resume(struct device *dev) | 1560 | static int usb_resume(struct device *dev) |
@@ -1542,13 +1565,14 @@ static int usb_resume(struct device *dev) | |||
1542 | return 0; | 1565 | return 0; |
1543 | udev = to_usb_device(dev); | 1566 | udev = to_usb_device(dev); |
1544 | 1567 | ||
1545 | /* If autoresume is disabled then we also want to prevent resume | 1568 | /* If udev->skip_sys_resume is set then udev was already suspended |
1546 | * during system wakeup. However, a "persistent-device" reset-resume | 1569 | * when the system suspend started, so we don't want to resume |
1547 | * after power loss counts as a wakeup event. So allow a | 1570 | * udev during this system wakeup. However a reset-resume counts |
1548 | * reset-resume to occur if remote wakeup is enabled. */ | 1571 | * as a wakeup event, so allow a reset-resume to occur if remote |
1549 | if (udev->autoresume_disabled) { | 1572 | * wakeup is enabled. */ |
1573 | if (udev->skip_sys_resume) { | ||
1550 | if (!(udev->reset_resume && udev->do_remote_wakeup)) | 1574 | if (!(udev->reset_resume && udev->do_remote_wakeup)) |
1551 | return -EPERM; | 1575 | return -EHOSTUNREACH; |
1552 | } | 1576 | } |
1553 | return usb_external_resume_device(udev); | 1577 | return usb_external_resume_device(udev); |
1554 | } | 1578 | } |