Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] USB: central handling for host controllers that were reset during suspend/resume

This patch (as515b) adds a routine to usbcore to simplify handling of
host controllers that lost power or were reset during suspend/resume.
The new core routine marks all the child devices of the root hub as
NOTATTACHED and tells khubd to disconnect the device structures as soon
as possible.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
1c50c317 8364d6b0

+42 -15
+1
drivers/usb/core/hcd.h
··· 380 380 #ifdef CONFIG_PM 381 381 extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd); 382 382 extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); 383 + extern void usb_root_hub_lost_power (struct usb_device *rhdev); 383 384 extern int hcd_bus_suspend (struct usb_bus *bus); 384 385 extern int hcd_bus_resume (struct usb_bus *bus); 385 386 #else
+33
drivers/usb/core/hub.c
··· 1039 1039 EXPORT_SYMBOL(usb_set_device_state); 1040 1040 1041 1041 1042 + #ifdef CONFIG_PM 1043 + 1044 + /** 1045 + * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power 1046 + * @rhdev: struct usb_device for the root hub 1047 + * 1048 + * The USB host controller driver calls this function when its root hub 1049 + * is resumed and Vbus power has been interrupted or the controller 1050 + * has been reset. The routine marks all the children of the root hub 1051 + * as NOTATTACHED and marks logical connect-change events on their ports. 1052 + */ 1053 + void usb_root_hub_lost_power(struct usb_device *rhdev) 1054 + { 1055 + struct usb_hub *hub; 1056 + int port1; 1057 + unsigned long flags; 1058 + 1059 + dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); 1060 + spin_lock_irqsave(&device_state_lock, flags); 1061 + hub = hdev_to_hub(rhdev); 1062 + for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { 1063 + if (rhdev->children[port1 - 1]) { 1064 + recursively_mark_NOTATTACHED( 1065 + rhdev->children[port1 - 1]); 1066 + set_bit(port1, hub->change_bits); 1067 + } 1068 + } 1069 + spin_unlock_irqrestore(&device_state_lock, flags); 1070 + } 1071 + EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); 1072 + 1073 + #endif 1074 + 1042 1075 static void choose_address(struct usb_device *udev) 1043 1076 { 1044 1077 int devnum;
+1 -8
drivers/usb/host/ehci-pci.c
··· 278 278 { 279 279 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 280 280 unsigned port; 281 - struct usb_device *root = hcd->self.root_hub; 282 281 struct pci_dev *pdev = to_pci_dev(hcd->self.controller); 283 282 int retval = -EINVAL; 284 283 ··· 311 312 312 313 restart: 313 314 ehci_dbg(ehci, "lost power, restarting\n"); 314 - for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) { 315 - port--; 316 - if (!root->children [port]) 317 - continue; 318 - usb_set_device_state(root->children[port], 319 - USB_STATE_NOTATTACHED); 320 - } 315 + usb_root_hub_lost_power(hcd->self.root_hub); 321 316 322 317 /* Else reset, to cope with power loss or flush-to-storage 323 318 * style "resume" having let BIOS kick in during reboot.
+1 -6
drivers/usb/host/ohci-hcd.c
··· 795 795 int temp; 796 796 int i; 797 797 struct urb_priv *priv; 798 - struct usb_device *root = ohci_to_hcd(ohci)->self.root_hub; 799 798 800 799 /* mark any devices gone, so they do nothing till khubd disconnects. 801 800 * recycle any "live" eds/tds (and urbs) right away. ··· 803 804 */ 804 805 spin_lock_irq(&ohci->lock); 805 806 disable (ohci); 806 - for (i = 0; i < root->maxchild; i++) { 807 - if (root->children [i]) 808 - usb_set_device_state (root->children[i], 809 - USB_STATE_NOTATTACHED); 810 - } 807 + usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub); 811 808 if (!list_empty (&ohci->pending)) 812 809 ohci_dbg(ohci, "abort schedule...\n"); 813 810 list_for_each_entry (priv, &ohci->pending, pending) {
+1
drivers/usb/host/sl811-hcd.c
··· 1803 1803 || !device_can_wakeup(&hcd->self.root_hub->dev)) { 1804 1804 sl811->port1 = 0; 1805 1805 port_power(sl811, 1); 1806 + usb_root_hub_lost_power(hcd->self.root_hub); 1806 1807 return 0; 1807 1808 } 1808 1809
+5 -1
drivers/usb/host/uhci-hcd.c
··· 748 748 check_and_reset_hc(uhci); 749 749 configure_hc(uhci); 750 750 751 - if (uhci->rh_state == UHCI_RH_RESET) 751 + if (uhci->rh_state == UHCI_RH_RESET) { 752 + 753 + /* The controller had to be reset */ 754 + usb_root_hub_lost_power(hcd->self.root_hub); 752 755 suspend_rh(uhci, UHCI_RH_SUSPENDED); 756 + } 753 757 754 758 spin_unlock_irq(&uhci->lock); 755 759