USB: unbind all interfaces before rebinding any
When a driver doesn't have pre_reset, post_reset, or reset_resume methods, the USB core unbinds that driver when its device undergoes a reset or a reset-resume, and then rebinds it afterward. The existing straightforward implementation can lead to problems, because each interface gets unbound and rebound before the next interface is handled. If a driver claims additional interfaces, the claim may fail because the old binding instance may still own the additional interface when the new instance tries to claim it. This patch fixes the problem by first unbinding all the interfaces that are marked (i.e., their needs_binding flag is set) and then rebinding all of them. The patch also makes the helper functions in driver.c a little more uniform and adjusts some out-of-date comments. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-tested-by: "Poulain, Loic" <loic.poulain@intel.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
ead5178bf4
Коммит
6aec044cc2
|
@ -1011,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister);
|
||||||
* it doesn't support pre_reset/post_reset/reset_resume or
|
* it doesn't support pre_reset/post_reset/reset_resume or
|
||||||
* because it doesn't support suspend/resume.
|
* because it doesn't support suspend/resume.
|
||||||
*
|
*
|
||||||
* The caller must hold @intf's device's lock, but not its pm_mutex
|
* The caller must hold @intf's device's lock, but not @intf's lock.
|
||||||
* and not @intf->dev.sem.
|
|
||||||
*/
|
*/
|
||||||
void usb_forced_unbind_intf(struct usb_interface *intf)
|
void usb_forced_unbind_intf(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
|
@ -1025,16 +1024,37 @@ void usb_forced_unbind_intf(struct usb_interface *intf)
|
||||||
intf->needs_binding = 1;
|
intf->needs_binding = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unbind drivers for @udev's marked interfaces. These interfaces have
|
||||||
|
* the needs_binding flag set, for example by usb_resume_interface().
|
||||||
|
*
|
||||||
|
* The caller must hold @udev's device lock.
|
||||||
|
*/
|
||||||
|
static void unbind_marked_interfaces(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct usb_host_config *config;
|
||||||
|
int i;
|
||||||
|
struct usb_interface *intf;
|
||||||
|
|
||||||
|
config = udev->actconfig;
|
||||||
|
if (config) {
|
||||||
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||||
|
intf = config->interface[i];
|
||||||
|
if (intf->dev.driver && intf->needs_binding)
|
||||||
|
usb_forced_unbind_intf(intf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Delayed forced unbinding of a USB interface driver and scan
|
/* Delayed forced unbinding of a USB interface driver and scan
|
||||||
* for rebinding.
|
* for rebinding.
|
||||||
*
|
*
|
||||||
* The caller must hold @intf's device's lock, but not its pm_mutex
|
* The caller must hold @intf's device's lock, but not @intf's lock.
|
||||||
* and not @intf->dev.sem.
|
|
||||||
*
|
*
|
||||||
* Note: Rebinds will be skipped if a system sleep transition is in
|
* Note: Rebinds will be skipped if a system sleep transition is in
|
||||||
* progress and the PM "complete" callback hasn't occurred yet.
|
* progress and the PM "complete" callback hasn't occurred yet.
|
||||||
*/
|
*/
|
||||||
void usb_rebind_intf(struct usb_interface *intf)
|
static void usb_rebind_intf(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
@ -1051,6 +1071,41 @@ void usb_rebind_intf(struct usb_interface *intf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rebind drivers to @udev's marked interfaces. These interfaces have
|
||||||
|
* the needs_binding flag set.
|
||||||
|
*
|
||||||
|
* The caller must hold @udev's device lock.
|
||||||
|
*/
|
||||||
|
static void rebind_marked_interfaces(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct usb_host_config *config;
|
||||||
|
int i;
|
||||||
|
struct usb_interface *intf;
|
||||||
|
|
||||||
|
config = udev->actconfig;
|
||||||
|
if (config) {
|
||||||
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||||
|
intf = config->interface[i];
|
||||||
|
if (intf->needs_binding)
|
||||||
|
usb_rebind_intf(intf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unbind all of @udev's marked interfaces and then rebind all of them.
|
||||||
|
* This ordering is necessary because some drivers claim several interfaces
|
||||||
|
* when they are first probed.
|
||||||
|
*
|
||||||
|
* The caller must hold @udev's device lock.
|
||||||
|
*/
|
||||||
|
void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
unbind_marked_interfaces(udev);
|
||||||
|
rebind_marked_interfaces(udev);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
/* Unbind drivers for @udev's interfaces that don't support suspend/resume
|
/* Unbind drivers for @udev's interfaces that don't support suspend/resume
|
||||||
|
@ -1080,43 +1135,6 @@ static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
|
|
||||||
* These interfaces have the needs_binding flag set by usb_resume_interface().
|
|
||||||
*
|
|
||||||
* The caller must hold @udev's device lock.
|
|
||||||
*/
|
|
||||||
static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
|
|
||||||
{
|
|
||||||
struct usb_host_config *config;
|
|
||||||
int i;
|
|
||||||
struct usb_interface *intf;
|
|
||||||
|
|
||||||
config = udev->actconfig;
|
|
||||||
if (config) {
|
|
||||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
|
||||||
intf = config->interface[i];
|
|
||||||
if (intf->dev.driver && intf->needs_binding)
|
|
||||||
usb_forced_unbind_intf(intf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_rebind_interfaces(struct usb_device *udev)
|
|
||||||
{
|
|
||||||
struct usb_host_config *config;
|
|
||||||
int i;
|
|
||||||
struct usb_interface *intf;
|
|
||||||
|
|
||||||
config = udev->actconfig;
|
|
||||||
if (config) {
|
|
||||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
|
||||||
intf = config->interface[i];
|
|
||||||
if (intf->needs_binding)
|
|
||||||
usb_rebind_intf(intf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
||||||
{
|
{
|
||||||
struct usb_device_driver *udriver;
|
struct usb_device_driver *udriver;
|
||||||
|
@ -1441,7 +1459,7 @@ int usb_resume_complete(struct device *dev)
|
||||||
* whose needs_binding flag is set
|
* whose needs_binding flag is set
|
||||||
*/
|
*/
|
||||||
if (udev->state != USB_STATE_NOTATTACHED)
|
if (udev->state != USB_STATE_NOTATTACHED)
|
||||||
do_rebind_interfaces(udev);
|
rebind_marked_interfaces(udev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1463,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
pm_runtime_set_active(dev);
|
pm_runtime_set_active(dev);
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
unbind_no_reset_resume_drivers_interfaces(udev);
|
unbind_marked_interfaces(udev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avoid PM error messages for devices disconnected while suspended
|
/* Avoid PM error messages for devices disconnected while suspended
|
||||||
|
|
|
@ -5380,10 +5380,11 @@ int usb_reset_device(struct usb_device *udev)
|
||||||
else if (cintf->condition ==
|
else if (cintf->condition ==
|
||||||
USB_INTERFACE_BOUND)
|
USB_INTERFACE_BOUND)
|
||||||
rebind = 1;
|
rebind = 1;
|
||||||
|
if (rebind)
|
||||||
|
cintf->needs_binding = 1;
|
||||||
}
|
}
|
||||||
if (ret == 0 && rebind)
|
|
||||||
usb_rebind_intf(cintf);
|
|
||||||
}
|
}
|
||||||
|
usb_unbind_and_rebind_marked_interfaces(udev);
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_autosuspend_device(udev);
|
usb_autosuspend_device(udev);
|
||||||
|
|
|
@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct usb_device *dev,
|
||||||
extern int usb_match_device(struct usb_device *dev,
|
extern int usb_match_device(struct usb_device *dev,
|
||||||
const struct usb_device_id *id);
|
const struct usb_device_id *id);
|
||||||
extern void usb_forced_unbind_intf(struct usb_interface *intf);
|
extern void usb_forced_unbind_intf(struct usb_interface *intf);
|
||||||
extern void usb_rebind_intf(struct usb_interface *intf);
|
extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
|
||||||
|
|
||||||
extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
|
extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
|
||||||
struct dev_state *owner);
|
struct dev_state *owner);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче