Commit 808f57d4 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB: Allow device resets for hubs

This patch adds support for calling usb_reset_device() on hubs (other than
root hubs).  It uses some of the new routines added recently by David
Brownell to shorten and simplify the code.  The only place where this is
called is if khubd encounters a serious error with a hub, so I don't
expect it to turn up very often in ordinary use.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 2fd4d42b
......@@ -651,8 +651,6 @@ static void hub_quiesce(struct usb_hub *hub)
flush_scheduled_work();
}
#ifdef CONFIG_USB_SUSPEND
static void hub_reactivate(struct usb_hub *hub)
{
int status;
......@@ -665,8 +663,6 @@ static void hub_reactivate(struct usb_hub *hub)
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
}
#endif
static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata (intf);
......@@ -808,54 +804,26 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
/* caller has locked the hub and must own the device lock */
static int hub_reset(struct usb_hub *hub)
/* caller has locked the hub device */
static void hub_pre_reset(struct usb_device *hdev)
{
struct usb_device *hdev = hub->hdev;
struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
int i;
/* Disconnect any attached devices */
for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
for (i = 0; i < hdev->maxchild; ++i) {
if (hdev->children[i])
usb_disconnect(&hdev->children[i]);
}
/* Attempt to reset the hub */
if (hub->urb)
usb_kill_urb(hub->urb);
else
return -1;
if (usb_reset_device(hdev))
return -1;
hub->urb->dev = hdev;
if (usb_submit_urb(hub->urb, GFP_KERNEL))
return -1;
hub_power_on(hub);
return 0;
hub_quiesce(hub);
}
/* caller has locked the hub */
/* FIXME! This routine should be subsumed into hub_reset */
static void hub_start_disconnect(struct usb_device *hdev)
/* caller has locked the hub device */
static void hub_post_reset(struct usb_device *hdev)
{
struct usb_device *parent = hdev->parent;
int i;
/* Find the device pointer to disconnect */
if (parent) {
for (i = 0; i < parent->maxchild; i++) {
if (parent->children[i] == hdev) {
usb_disconnect(&parent->children[i]);
return;
}
}
}
struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
dev_err(&hdev->dev, "cannot disconnect hub!\n");
hub_reactivate(hub);
hub_power_on(hub);
}
......@@ -2493,10 +2461,10 @@ static void hub_events(void)
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
if (hub_reset(hub)) {
ret = usb_reset_device(hdev);
if (ret) {
dev_dbg (hub_dev,
"can't reset; disconnecting\n");
hub_start_disconnect(hdev);
"error resetting hub: %d\n", ret);
goto loop;
}
......@@ -2767,6 +2735,7 @@ int usb_reset_device(struct usb_device *udev)
struct usb_device *parent = udev->parent;
struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret, port = -1;
int udev_is_a_hub = 0;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
......@@ -2775,13 +2744,9 @@ int usb_reset_device(struct usb_device *udev)
return -EINVAL;
}
/* FIXME: This should be legal for regular hubs. Root hubs may
* have special requirements. */
if (udev->maxchild) {
/* this requires hub- or hcd-specific logic;
* see hub_reset() and OHCI hc_restart()
*/
dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__);
if (!parent) {
/* this requires hcd-specific logic; see OHCI hc_restart() */
dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
return -EISDIR;
}
......@@ -2797,6 +2762,14 @@ int usb_reset_device(struct usb_device *udev)
return -ENOENT;
}
/* If we're resetting an active hub, take some special actions */
if (udev->actconfig &&
udev->actconfig->interface[0]->dev.driver ==
&hub_driver.driver) {
udev_is_a_hub = 1;
hub_pre_reset(udev);
}
/* ep0 maxpacket size may change; let the HCD know about it.
* Other endpoints will be handled by re-enumeration. */
usb_disable_endpoint(udev, 0);
......@@ -2815,7 +2788,7 @@ int usb_reset_device(struct usb_device *udev)
}
if (!udev->actconfig)
return 0;
goto done;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0,
......@@ -2849,6 +2822,9 @@ int usb_reset_device(struct usb_device *udev)
}
}
done:
if (udev_is_a_hub)
hub_post_reset(udev);
return 0;
re_enumerate:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment