USB: don't use reset-resume if drivers don't support it

This patch tries to identify which devices are able to accept
reset-resume handling, by checking that there is at least one
interface driver bound and that all of the drivers have a reset_resume
method defined. If these conditions don't hold then during resume
processing, the device is logicall disconnected.

This is only a temporary fix. Later on we will explicitly unbind
drivers that can't handle reset-resumes.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <oliver@neukum.org>
Cc: Pavel Machek <pavel@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by Linus Torvalds and committed by Greg Kroah-Hartman 5340ba82 e6942d63

+44 -2
+44 -2
drivers/usb/core/hub.c
··· 644 645 #ifdef CONFIG_PM 646 647 static void hub_restart(struct usb_hub *hub, int type) 648 { 649 struct usb_device *hdev = hub->hdev; ··· 731 * turn off the various status changes to prevent 732 * khubd from disconnecting it later. 733 */ 734 - if (udev->persist_enabled && status == 0 && 735 - !(portstatus & USB_PORT_STAT_ENABLE)) { 736 if (portchange & USB_PORT_STAT_C_ENABLE) 737 clear_port_feature(hub->hdev, port1, 738 USB_PORT_FEAT_C_ENABLE);
··· 644 645 #ifdef CONFIG_PM 646 647 + /* Try to identify which devices need USB-PERSIST handling */ 648 + static int persistent_device(struct usb_device *udev) 649 + { 650 + int i; 651 + int retval; 652 + struct usb_host_config *actconfig; 653 + 654 + /* Explicitly not marked persistent? */ 655 + if (!udev->persist_enabled) 656 + return 0; 657 + 658 + /* No active config? */ 659 + actconfig = udev->actconfig; 660 + if (!actconfig) 661 + return 0; 662 + 663 + /* FIXME! We should check whether it's open here or not! */ 664 + 665 + /* 666 + * Check that all the interface drivers have a 667 + * 'reset_resume' entrypoint 668 + */ 669 + retval = 0; 670 + for (i = 0; i < actconfig->desc.bNumInterfaces; i++) { 671 + struct usb_interface *intf; 672 + struct usb_driver *driver; 673 + 674 + intf = actconfig->interface[i]; 675 + if (!intf->dev.driver) 676 + continue; 677 + driver = to_usb_driver(intf->dev.driver); 678 + if (!driver->reset_resume) 679 + return 0; 680 + /* 681 + * We have at least one driver, and that one 682 + * has a reset_resume method. 683 + */ 684 + retval = 1; 685 + } 686 + return retval; 687 + } 688 + 689 static void hub_restart(struct usb_hub *hub, int type) 690 { 691 struct usb_device *hdev = hub->hdev; ··· 689 * turn off the various status changes to prevent 690 * khubd from disconnecting it later. 691 */ 692 + if (status == 0 && !(portstatus & USB_PORT_STAT_ENABLE) && 693 + persistent_device(udev)) { 694 if (portchange & USB_PORT_STAT_C_ENABLE) 695 clear_port_feature(hub->hdev, port1, 696 USB_PORT_FEAT_C_ENABLE);