Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 104614
b: refs/heads/master
c: 78d9a48
h: refs/heads/master
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jul 21, 2008
1 parent cdb81c5 commit 28013aa
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 22 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 64b3d6d11948cc71ff12124dcb693392a32f1bf4
refs/heads/master: 78d9a487ee961c356e1a934d9a92eca38ffb3a70
131 changes: 116 additions & 15 deletions trunk/drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev)

intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
intf->needs_binding = 0;

if (udev->authorized == 0) {
dev_err(&intf->dev, "Device is not authorized for usage\n");
Expand Down Expand Up @@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,

dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
iface->needs_binding = 0;

usb_pm_lock(udev);
iface->condition = USB_INTERFACE_BOUND;
Expand Down Expand Up @@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *driver)
}
EXPORT_SYMBOL_GPL(usb_deregister);


/* Forced unbinding of a USB interface driver, either because
* it doesn't support pre_reset/post_reset/reset_resume or
* because it doesn't support suspend/resume.
*
* The caller must hold @intf's device's lock, but not its pm_mutex
* and not @intf->dev.sem.
*/
void usb_forced_unbind_intf(struct usb_interface *intf)
{
struct usb_driver *driver = to_usb_driver(intf->dev.driver);

dev_dbg(&intf->dev, "forced unbind\n");
usb_driver_release_interface(driver, intf);

/* Mark the interface for later rebinding */
intf->needs_binding = 1;
}

/* Delayed forced unbinding of a USB interface driver and scan
* for rebinding.
*
* The caller must hold @intf's device's lock, but not its pm_mutex
* and not @intf->dev.sem.
*
* FIXME: The caller must block system sleep transitions.
*/
void usb_rebind_intf(struct usb_interface *intf)
{
int rc;

/* Delayed unbind of an existing driver */
if (intf->dev.driver) {
struct usb_driver *driver =
to_usb_driver(intf->dev.driver);

dev_dbg(&intf->dev, "forced unbind\n");
usb_driver_release_interface(driver, intf);
}

/* Try to rebind the interface */
intf->needs_binding = 0;
rc = device_attach(&intf->dev);
if (rc < 0)
dev_warn(&intf->dev, "rebind failed: %d\n", rc);
}

#define DO_UNBIND 0
#define DO_REBIND 1

/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
* or rebind interfaces that have been unbound, according to @action.
*
* The caller must hold @udev's device lock.
* FIXME: For rebinds, the caller must block system sleep transitions.
*/
static void do_unbind_rebind(struct usb_device *udev, int action)
{
struct usb_host_config *config;
int i;
struct usb_interface *intf;
struct usb_driver *drv;

config = udev->actconfig;
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
switch (action) {
case DO_UNBIND:
if (intf->dev.driver) {
drv = to_usb_driver(intf->dev.driver);
if (!drv->suspend || !drv->resume)
usb_forced_unbind_intf(intf);
}
break;
case DO_REBIND:
if (intf->needs_binding) {

/* FIXME: The next line is needed because we are going to probe
* the interface, but as far as the PM core is concerned the
* interface is still suspended. The problem wouldn't exist
* if we could rebind the interface during the interface's own
* resume() call, but at the time the usb_device isn't locked!
*
* The real solution will be to carry this out during the device's
* complete() callback. Until that is implemented, we have to
* use this hack.
*/
// intf->dev.power.sleeping = 0;

usb_rebind_intf(intf);
}
break;
}
}
}
}

#ifdef CONFIG_PM

/* Caller has locked udev's pm_mutex */
Expand Down Expand Up @@ -841,20 +941,18 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
goto done;
driver = to_usb_driver(intf->dev.driver);

if (driver->suspend && driver->resume) {
if (driver->suspend) {
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
else if (!interface_to_usbdev(intf)->auto_pm)
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
/*
* FIXME else if there's no suspend method, disconnect...
* Not possible if auto_pm is set...
*/
dev_warn(&intf->dev, "no suspend for driver %s?\n",
driver->name);
/* Later we will unbind the driver and reprobe */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"suspend", driver->name);
mark_quiesced(intf);
}

Expand All @@ -878,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
goto done;

/* Can't resume it if it doesn't have a driver. */
if (intf->condition == USB_INTERFACE_UNBOUND) {
status = -ENOTCONN;
if (intf->condition == USB_INTERFACE_UNBOUND)
goto done;

/* Don't resume if the interface is marked for rebinding */
if (intf->needs_binding)
goto done;
}
driver = to_usb_driver(intf->dev.driver);

if (reset_resume) {
Expand All @@ -891,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
dev_err(&intf->dev, "%s error %d\n",
"reset_resume", status);
} else {
/* status = -EOPNOTSUPP; */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"reset_resume", driver->name);
}
Expand All @@ -902,19 +1002,18 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
dev_err(&intf->dev, "%s error %d\n",
"resume", status);
} else {
/* status = -EOPNOTSUPP; */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"resume", driver->name);
}
}

done:
dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status);
if (status == 0)
if (status == 0 && intf->condition == USB_INTERFACE_BOUND)
mark_active(intf);

/* FIXME: Unbind the driver and reprobe if the resume failed
* (not possible if auto_pm is set) */
/* Later we will unbind the driver and/or reprobe, if necessary */
return status;
}

Expand Down Expand Up @@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
{
int status;

do_unbind_rebind(udev, DO_UNBIND);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, msg);
Expand Down Expand Up @@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev)
status = usb_resume_both(udev);
udev->last_busy = jiffies;
usb_pm_unlock(udev);
do_unbind_rebind(udev, DO_REBIND);

/* Now that the device is awake, we can start trying to autosuspend
* it again. */
Expand Down
27 changes: 21 additions & 6 deletions trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -3367,6 +3367,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
* this from a driver probe() routine after downloading new firmware.
* For calls that might not occur during probe(), drivers should lock
* the device using usb_lock_device_for_reset().
*
* If an interface is currently being probed or disconnected, we assume
* its driver knows how to handle resets. For all other interfaces,
* if the driver doesn't have pre_reset and post_reset methods then
* we attempt to unbind it and rebind afterward.
*/
int usb_reset_device(struct usb_device *udev)
{
Expand All @@ -3388,12 +3393,17 @@ int usb_reset_device(struct usb_device *udev)
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
int unbind = 0;

if (cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->pre_reset)
(drv->pre_reset)(cintf);
/* FIXME: Unbind if pre_reset returns an error or isn't defined */
if (drv->pre_reset && drv->post_reset)
unbind = (drv->pre_reset)(cintf);
else if (cintf->condition ==
USB_INTERFACE_BOUND)
unbind = 1;
if (unbind)
usb_forced_unbind_intf(cintf);
}
}
}
Expand All @@ -3404,13 +3414,18 @@ int usb_reset_device(struct usb_device *udev)
for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
int rebind = cintf->needs_binding;

if (cintf->dev.driver) {
if (!rebind && cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->post_reset)
(drv->post_reset)(cintf);
/* FIXME: Unbind if post_reset returns an error or isn't defined */
rebind = (drv->post_reset)(cintf);
else if (cintf->condition ==
USB_INTERFACE_BOUND)
rebind = 1;
}
if (rebind)
usb_rebind_intf(cintf);
}
}

Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/usb/core/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev);
extern void usb_kick_khubd(struct usb_device *dev);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_rebind_intf(struct usb_interface *intf);

extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ struct usb_interface {
unsigned is_active:1; /* the interface is not suspended */
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_binding:1; /* needs delayed unbind/rebind */

struct device dev; /* interface specific device info */
struct device *usb_dev;
Expand Down

0 comments on commit 28013aa

Please sign in to comment.