diff options
author | David Brownell <david-b@pacbell.net> | 2005-09-23 01:32:24 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:40 -0400 |
commit | 979d5199fee9e80290ddeb532e5993bd15506712 (patch) | |
tree | 6987772d41ec540b7e32beaa50c1493a95e3e2c8 | |
parent | 9293677af3dace2645dec0d0808efa02d36bf47b (diff) |
[PATCH] root hub changes (lesser half)
This patch collects various small updates related to root hubs, to shrink
later patches which build on them.
- For root hub suspend/resume support:
* Make the existing usb_hcd_resume_root_hub() routine respect pmcore
locking, exporting and using the dpm_runtime_resume() method.
* Add a new usb_hcd_suspend_root_hub() to pair with that routine.
(Essential to make OHCI autosuspend behave again...)
* HC_SUSPENDED by itself only refers to the root hub's downstream ports.
So let HCDs see root hub URBs unless the parent device is suspended.
- Remove an assertion we no longer need (and now, also don't want).
- Generic suspend/resume updates to work better with swsusp.
* Ignore the FREEZE vs SUSPEND distinction for hardware; trying to
use it breaks the swsusp snapshots it's supposed to help (sigh).
* On resume, mark devices as resumed right away, but then
do nothing else if the device is marked NOTATTACHED.
These changes shouldn't be very noticable by themselves.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/base/power/runtime.c | 1
drivers/usb/core/hcd.c | 64 ++++++++++++++++++++++++++++++++++++++-----
drivers/usb/core/hcd.h | 1
drivers/usb/core/hub.c | 45 ++++++++++++++++++++++++------
drivers/usb/core/usb.c | 20 +++++++++----
drivers/usb/core/usb.h | 1
6 files changed, 111 insertions(+), 21 deletions(-)
-rw-r--r-- | drivers/base/power/runtime.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 64 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 1 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 45 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 20 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 1 |
6 files changed, 111 insertions, 21 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index e8f0519f5dfa..adbc3148c039 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -36,6 +36,7 @@ void dpm_runtime_resume(struct device * dev) | |||
36 | runtime_resume(dev); | 36 | runtime_resume(dev); |
37 | up(&dpm_sem); | 37 | up(&dpm_sem); |
38 | } | 38 | } |
39 | EXPORT_SYMBOL(dpm_runtime_resume); | ||
39 | 40 | ||
40 | 41 | ||
41 | /** | 42 | /** |
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 375382f9d671..de59bb515315 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -1143,10 +1143,20 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags) | |||
1143 | else switch (hcd->state) { | 1143 | else switch (hcd->state) { |
1144 | case HC_STATE_RUNNING: | 1144 | case HC_STATE_RUNNING: |
1145 | case HC_STATE_RESUMING: | 1145 | case HC_STATE_RESUMING: |
1146 | doit: | ||
1146 | usb_get_dev (urb->dev); | 1147 | usb_get_dev (urb->dev); |
1147 | list_add_tail (&urb->urb_list, &ep->urb_list); | 1148 | list_add_tail (&urb->urb_list, &ep->urb_list); |
1148 | status = 0; | 1149 | status = 0; |
1149 | break; | 1150 | break; |
1151 | case HC_STATE_SUSPENDED: | ||
1152 | /* HC upstream links (register access, wakeup signaling) can work | ||
1153 | * even when the downstream links (and DMA etc) are quiesced; let | ||
1154 | * usbcore talk to the root hub. | ||
1155 | */ | ||
1156 | if (hcd->self.controller->power.power_state.event == PM_EVENT_ON | ||
1157 | && urb->dev->parent == NULL) | ||
1158 | goto doit; | ||
1159 | /* FALL THROUGH */ | ||
1150 | default: | 1160 | default: |
1151 | status = -ESHUTDOWN; | 1161 | status = -ESHUTDOWN; |
1152 | break; | 1162 | break; |
@@ -1294,12 +1304,6 @@ static int hcd_unlink_urb (struct urb *urb, int status) | |||
1294 | goto done; | 1304 | goto done; |
1295 | } | 1305 | } |
1296 | 1306 | ||
1297 | /* running ~= hc unlink handshake works (irq, timer, etc) | ||
1298 | * halted ~= no unlink handshake is needed | ||
1299 | * suspended, resuming == should never happen | ||
1300 | */ | ||
1301 | WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); | ||
1302 | |||
1303 | /* insist the urb is still queued */ | 1307 | /* insist the urb is still queued */ |
1304 | list_for_each(tmp, &ep->urb_list) { | 1308 | list_for_each(tmp, &ep->urb_list) { |
1305 | if (tmp == &urb->urb_list) | 1309 | if (tmp == &urb->urb_list) |
@@ -1459,6 +1463,8 @@ static int hcd_hub_resume (struct usb_bus *bus) | |||
1459 | hcd = container_of (bus, struct usb_hcd, self); | 1463 | hcd = container_of (bus, struct usb_hcd, self); |
1460 | if (!hcd->driver->hub_resume) | 1464 | if (!hcd->driver->hub_resume) |
1461 | return -ENOENT; | 1465 | return -ENOENT; |
1466 | if (hcd->state == HC_STATE_RUNNING) | ||
1467 | return 0; | ||
1462 | hcd->state = HC_STATE_RESUMING; | 1468 | hcd->state = HC_STATE_RESUMING; |
1463 | status = hcd->driver->hub_resume (hcd); | 1469 | status = hcd->driver->hub_resume (hcd); |
1464 | if (status == 0) | 1470 | if (status == 0) |
@@ -1471,6 +1477,50 @@ static int hcd_hub_resume (struct usb_bus *bus) | |||
1471 | return status; | 1477 | return status; |
1472 | } | 1478 | } |
1473 | 1479 | ||
1480 | /* | ||
1481 | * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports | ||
1482 | * @hcd: host controller for this root hub | ||
1483 | * | ||
1484 | * This call arranges that usb_hcd_resume_root_hub() is safe to call later; | ||
1485 | * that the HCD's root hub polling is deactivated; and that the root's hub | ||
1486 | * driver is suspended. HCDs may call this to autosuspend when their root | ||
1487 | * hub's downstream ports are all inactive: unpowered, disconnected, | ||
1488 | * disabled, or suspended. | ||
1489 | * | ||
1490 | * The HCD will autoresume on device connect change detection (using SRP | ||
1491 | * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling | ||
1492 | * from any ports that are suspended (if that is enabled). In most cases, | ||
1493 | * overcurrent signaling (on powered ports) will also start autoresume. | ||
1494 | * | ||
1495 | * Always called with IRQs blocked. | ||
1496 | */ | ||
1497 | void usb_hcd_suspend_root_hub (struct usb_hcd *hcd) | ||
1498 | { | ||
1499 | struct urb *urb; | ||
1500 | |||
1501 | spin_lock (&hcd_root_hub_lock); | ||
1502 | usb_suspend_root_hub (hcd->self.root_hub); | ||
1503 | |||
1504 | /* force status urb to complete/unlink while suspended */ | ||
1505 | if (hcd->status_urb) { | ||
1506 | urb = hcd->status_urb; | ||
1507 | urb->status = -ECONNRESET; | ||
1508 | urb->hcpriv = NULL; | ||
1509 | urb->actual_length = 0; | ||
1510 | |||
1511 | del_timer (&hcd->rh_timer); | ||
1512 | hcd->poll_pending = 0; | ||
1513 | hcd->status_urb = NULL; | ||
1514 | } else | ||
1515 | urb = NULL; | ||
1516 | spin_unlock (&hcd_root_hub_lock); | ||
1517 | hcd->state = HC_STATE_SUSPENDED; | ||
1518 | |||
1519 | if (urb) | ||
1520 | usb_hcd_giveback_urb (hcd, urb, NULL); | ||
1521 | } | ||
1522 | EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub); | ||
1523 | |||
1474 | /** | 1524 | /** |
1475 | * usb_hcd_resume_root_hub - called by HCD to resume its root hub | 1525 | * usb_hcd_resume_root_hub - called by HCD to resume its root hub |
1476 | * @hcd: host controller for this root hub | 1526 | * @hcd: host controller for this root hub |
@@ -1478,7 +1528,7 @@ static int hcd_hub_resume (struct usb_bus *bus) | |||
1478 | * The USB host controller calls this function when its root hub is | 1528 | * The USB host controller calls this function when its root hub is |
1479 | * suspended (with the remote wakeup feature enabled) and a remote | 1529 | * suspended (with the remote wakeup feature enabled) and a remote |
1480 | * wakeup request is received. It queues a request for khubd to | 1530 | * wakeup request is received. It queues a request for khubd to |
1481 | * resume the root hub. | 1531 | * resume the root hub (that is, manage its downstream ports again). |
1482 | */ | 1532 | */ |
1483 | void usb_hcd_resume_root_hub (struct usb_hcd *hcd) | 1533 | void usb_hcd_resume_root_hub (struct usb_hcd *hcd) |
1484 | { | 1534 | { |
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 1f1ed6211af8..eb21f13c5c74 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h | |||
@@ -355,6 +355,7 @@ extern long usb_calc_bus_time (int speed, int is_input, | |||
355 | 355 | ||
356 | extern struct usb_bus *usb_alloc_bus (struct usb_operations *); | 356 | extern struct usb_bus *usb_alloc_bus (struct usb_operations *); |
357 | 357 | ||
358 | extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd); | ||
358 | extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); | 359 | extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); |
359 | 360 | ||
360 | extern void usb_set_device_state(struct usb_device *udev, | 361 | extern void usb_set_device_state(struct usb_device *udev, |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 660064466791..3c8d8d1f993c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -449,11 +449,18 @@ static void hub_power_on(struct usb_hub *hub) | |||
449 | msleep(max(pgood_delay, (unsigned) 100)); | 449 | msleep(max(pgood_delay, (unsigned) 100)); |
450 | } | 450 | } |
451 | 451 | ||
452 | static void hub_quiesce(struct usb_hub *hub) | 452 | static inline void __hub_quiesce(struct usb_hub *hub) |
453 | { | 453 | { |
454 | /* stop khubd and related activity */ | 454 | /* (nonblocking) khubd and related activity won't re-trigger */ |
455 | hub->quiescing = 1; | 455 | hub->quiescing = 1; |
456 | hub->activating = 0; | 456 | hub->activating = 0; |
457 | hub->resume_root_hub = 0; | ||
458 | } | ||
459 | |||
460 | static void hub_quiesce(struct usb_hub *hub) | ||
461 | { | ||
462 | /* (blocking) stop khubd and related activity */ | ||
463 | __hub_quiesce(hub); | ||
457 | usb_kill_urb(hub->urb); | 464 | usb_kill_urb(hub->urb); |
458 | if (hub->has_indicators) | 465 | if (hub->has_indicators) |
459 | cancel_delayed_work(&hub->leds); | 466 | cancel_delayed_work(&hub->leds); |
@@ -467,6 +474,7 @@ static void hub_activate(struct usb_hub *hub) | |||
467 | 474 | ||
468 | hub->quiescing = 0; | 475 | hub->quiescing = 0; |
469 | hub->activating = 1; | 476 | hub->activating = 1; |
477 | hub->resume_root_hub = 0; | ||
470 | status = usb_submit_urb(hub->urb, GFP_NOIO); | 478 | status = usb_submit_urb(hub->urb, GFP_NOIO); |
471 | if (status < 0) | 479 | if (status < 0) |
472 | dev_err(hub->intfdev, "activate --> %d\n", status); | 480 | dev_err(hub->intfdev, "activate --> %d\n", status); |
@@ -1959,6 +1967,18 @@ static int hub_resume(struct usb_interface *intf) | |||
1959 | return 0; | 1967 | return 0; |
1960 | } | 1968 | } |
1961 | 1969 | ||
1970 | void usb_suspend_root_hub(struct usb_device *hdev) | ||
1971 | { | ||
1972 | struct usb_hub *hub = hdev_to_hub(hdev); | ||
1973 | |||
1974 | /* This also makes any led blinker stop retriggering. We're called | ||
1975 | * from irq, so the blinker might still be scheduled. Caller promises | ||
1976 | * that the root hub status URB will be canceled. | ||
1977 | */ | ||
1978 | __hub_quiesce(hub); | ||
1979 | mark_quiesced(to_usb_interface(hub->intfdev)); | ||
1980 | } | ||
1981 | |||
1962 | void usb_resume_root_hub(struct usb_device *hdev) | 1982 | void usb_resume_root_hub(struct usb_device *hdev) |
1963 | { | 1983 | { |
1964 | struct usb_hub *hub = hdev_to_hub(hdev); | 1984 | struct usb_hub *hub = hdev_to_hub(hdev); |
@@ -2616,21 +2636,30 @@ static void hub_events(void) | |||
2616 | intf = to_usb_interface(hub->intfdev); | 2636 | intf = to_usb_interface(hub->intfdev); |
2617 | hub_dev = &intf->dev; | 2637 | hub_dev = &intf->dev; |
2618 | 2638 | ||
2619 | dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", | 2639 | i = hub->resume_root_hub; |
2640 | |||
2641 | dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x%s\n", | ||
2620 | hdev->state, hub->descriptor | 2642 | hdev->state, hub->descriptor |
2621 | ? hub->descriptor->bNbrPorts | 2643 | ? hub->descriptor->bNbrPorts |
2622 | : 0, | 2644 | : 0, |
2623 | /* NOTE: expects max 15 ports... */ | 2645 | /* NOTE: expects max 15 ports... */ |
2624 | (u16) hub->change_bits[0], | 2646 | (u16) hub->change_bits[0], |
2625 | (u16) hub->event_bits[0]); | 2647 | (u16) hub->event_bits[0], |
2648 | i ? ", resume root" : ""); | ||
2626 | 2649 | ||
2627 | usb_get_intf(intf); | 2650 | usb_get_intf(intf); |
2628 | i = hub->resume_root_hub; | ||
2629 | spin_unlock_irq(&hub_event_lock); | 2651 | spin_unlock_irq(&hub_event_lock); |
2630 | 2652 | ||
2631 | /* Is this is a root hub wanting to be resumed? */ | 2653 | /* Is this is a root hub wanting to reactivate the downstream |
2632 | if (i) | 2654 | * ports? If so, be sure the interface resumes even if its |
2633 | usb_resume_device(hdev); | 2655 | * stub "device" node was never suspended. |
2656 | */ | ||
2657 | if (i) { | ||
2658 | extern void dpm_runtime_resume(struct device *); | ||
2659 | |||
2660 | dpm_runtime_resume(&hdev->dev); | ||
2661 | dpm_runtime_resume(&intf->dev); | ||
2662 | } | ||
2634 | 2663 | ||
2635 | /* Lock the device, then check to see if we were | 2664 | /* Lock the device, then check to see if we were |
2636 | * disconnected while waiting for the lock to succeed. */ | 2665 | * disconnected while waiting for the lock to succeed. */ |
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e89dbd43e952..2493e7d9f5b3 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
@@ -1427,6 +1427,7 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) | |||
1427 | 1427 | ||
1428 | /* USB devices enter SUSPEND state through their hubs, but can be | 1428 | /* USB devices enter SUSPEND state through their hubs, but can be |
1429 | * marked for FREEZE as soon as their children are already idled. | 1429 | * marked for FREEZE as soon as their children are already idled. |
1430 | * But those semantics are useless, so we equate the two (sigh). | ||
1430 | */ | 1431 | */ |
1431 | if (dev->driver == &usb_generic_driver) { | 1432 | if (dev->driver == &usb_generic_driver) { |
1432 | if (dev->power.power_state.event == message.event) | 1433 | if (dev->power.power_state.event == message.event) |
@@ -1435,10 +1436,6 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message) | |||
1435 | status = device_for_each_child(dev, NULL, verify_suspended); | 1436 | status = device_for_each_child(dev, NULL, verify_suspended); |
1436 | if (status) | 1437 | if (status) |
1437 | return status; | 1438 | return status; |
1438 | if (message.event == PM_EVENT_FREEZE) { | ||
1439 | dev->power.power_state = message; | ||
1440 | return 0; | ||
1441 | } | ||
1442 | return usb_suspend_device (to_usb_device(dev)); | 1439 | return usb_suspend_device (to_usb_device(dev)); |
1443 | } | 1440 | } |
1444 | 1441 | ||
@@ -1471,14 +1468,22 @@ static int usb_generic_resume(struct device *dev) | |||
1471 | { | 1468 | { |
1472 | struct usb_interface *intf; | 1469 | struct usb_interface *intf; |
1473 | struct usb_driver *driver; | 1470 | struct usb_driver *driver; |
1471 | struct usb_device *udev; | ||
1474 | int status; | 1472 | int status; |
1475 | 1473 | ||
1476 | if (dev->power.power_state.event == PM_EVENT_ON) | 1474 | if (dev->power.power_state.event == PM_EVENT_ON) |
1477 | return 0; | 1475 | return 0; |
1478 | 1476 | ||
1477 | /* mark things as "on" immediately, no matter what errors crop up */ | ||
1478 | dev->power.power_state.event = PM_EVENT_ON; | ||
1479 | |||
1479 | /* devices resume through their hubs */ | 1480 | /* devices resume through their hubs */ |
1480 | if (dev->driver == &usb_generic_driver) | 1481 | if (dev->driver == &usb_generic_driver) { |
1482 | udev = to_usb_device(dev); | ||
1483 | if (udev->state == USB_STATE_NOTATTACHED) | ||
1484 | return 0; | ||
1481 | return usb_resume_device (to_usb_device(dev)); | 1485 | return usb_resume_device (to_usb_device(dev)); |
1486 | } | ||
1482 | 1487 | ||
1483 | if ((dev->driver == NULL) || | 1488 | if ((dev->driver == NULL) || |
1484 | (dev->driver_data == &usb_generic_driver_data)) | 1489 | (dev->driver_data == &usb_generic_driver_data)) |
@@ -1487,11 +1492,14 @@ static int usb_generic_resume(struct device *dev) | |||
1487 | intf = to_usb_interface(dev); | 1492 | intf = to_usb_interface(dev); |
1488 | driver = to_usb_driver(dev->driver); | 1493 | driver = to_usb_driver(dev->driver); |
1489 | 1494 | ||
1495 | udev = interface_to_usbdev(intf); | ||
1496 | if (udev->state == USB_STATE_NOTATTACHED) | ||
1497 | return 0; | ||
1498 | |||
1490 | /* if driver was suspended, it has a resume method; | 1499 | /* if driver was suspended, it has a resume method; |
1491 | * however, sysfs can wrongly mark things as suspended | 1500 | * however, sysfs can wrongly mark things as suspended |
1492 | * (on the "no suspend method" FIXME path above) | 1501 | * (on the "no suspend method" FIXME path above) |
1493 | */ | 1502 | */ |
1494 | mark_active(intf); | ||
1495 | if (driver->resume) { | 1503 | if (driver->resume) { |
1496 | status = driver->resume(intf); | 1504 | status = driver->resume(intf); |
1497 | if (status) { | 1505 | if (status) { |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 3741a990403e..7add46ecc6a2 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -19,6 +19,7 @@ extern void usb_lock_all_devices(void); | |||
19 | extern void usb_unlock_all_devices(void); | 19 | extern void usb_unlock_all_devices(void); |
20 | 20 | ||
21 | extern void usb_kick_khubd(struct usb_device *dev); | 21 | extern void usb_kick_khubd(struct usb_device *dev); |
22 | extern void usb_suspend_root_hub(struct usb_device *hdev); | ||
22 | extern void usb_resume_root_hub(struct usb_device *dev); | 23 | extern void usb_resume_root_hub(struct usb_device *dev); |
23 | 24 | ||
24 | extern int usb_hub_init(void); | 25 | extern int usb_hub_init(void); |