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) ...@@ -651,8 +651,6 @@ static void hub_quiesce(struct usb_hub *hub)
flush_scheduled_work(); flush_scheduled_work();
} }
#ifdef CONFIG_USB_SUSPEND
static void hub_reactivate(struct usb_hub *hub) static void hub_reactivate(struct usb_hub *hub)
{ {
int status; int status;
...@@ -665,8 +663,6 @@ static void hub_reactivate(struct usb_hub *hub) ...@@ -665,8 +663,6 @@ static void hub_reactivate(struct usb_hub *hub)
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
} }
#endif
static void hub_disconnect(struct usb_interface *intf) static void hub_disconnect(struct usb_interface *intf)
{ {
struct usb_hub *hub = usb_get_intfdata (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) ...@@ -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 */ /* caller has locked the hub device */
static int hub_reset(struct usb_hub *hub) 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; int i;
/* Disconnect any attached devices */ for (i = 0; i < hdev->maxchild; ++i) {
for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
if (hdev->children[i]) if (hdev->children[i])
usb_disconnect(&hdev->children[i]); usb_disconnect(&hdev->children[i]);
} }
hub_quiesce(hub);
/* 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;
} }
/* caller has locked the hub */ /* caller has locked the hub device */
/* FIXME! This routine should be subsumed into hub_reset */ static void hub_post_reset(struct usb_device *hdev)
static void hub_start_disconnect(struct usb_device *hdev)
{ {
struct usb_device *parent = hdev->parent; struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
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;
}
}
}
dev_err(&hdev->dev, "cannot disconnect hub!\n"); hub_reactivate(hub);
hub_power_on(hub);
} }
...@@ -2493,10 +2461,10 @@ static void hub_events(void) ...@@ -2493,10 +2461,10 @@ static void hub_events(void)
dev_dbg (hub_dev, "resetting for error %d\n", dev_dbg (hub_dev, "resetting for error %d\n",
hub->error); hub->error);
if (hub_reset(hub)) { ret = usb_reset_device(hdev);
if (ret) {
dev_dbg (hub_dev, dev_dbg (hub_dev,
"can't reset; disconnecting\n"); "error resetting hub: %d\n", ret);
hub_start_disconnect(hdev);
goto loop; goto loop;
} }
...@@ -2767,6 +2735,7 @@ int usb_reset_device(struct usb_device *udev) ...@@ -2767,6 +2735,7 @@ int usb_reset_device(struct usb_device *udev)
struct usb_device *parent = udev->parent; struct usb_device *parent = udev->parent;
struct usb_device_descriptor descriptor = udev->descriptor; struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret, port = -1; int i, ret, port = -1;
int udev_is_a_hub = 0;
if (udev->state == USB_STATE_NOTATTACHED || if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) { udev->state == USB_STATE_SUSPENDED) {
...@@ -2775,13 +2744,9 @@ int usb_reset_device(struct usb_device *udev) ...@@ -2775,13 +2744,9 @@ int usb_reset_device(struct usb_device *udev)
return -EINVAL; return -EINVAL;
} }
/* FIXME: This should be legal for regular hubs. Root hubs may if (!parent) {
* have special requirements. */ /* this requires hcd-specific logic; see OHCI hc_restart() */
if (udev->maxchild) { dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
/* this requires hub- or hcd-specific logic;
* see hub_reset() and OHCI hc_restart()
*/
dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__);
return -EISDIR; return -EISDIR;
} }
...@@ -2797,6 +2762,14 @@ int usb_reset_device(struct usb_device *udev) ...@@ -2797,6 +2762,14 @@ int usb_reset_device(struct usb_device *udev)
return -ENOENT; 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. /* ep0 maxpacket size may change; let the HCD know about it.
* Other endpoints will be handled by re-enumeration. */ * Other endpoints will be handled by re-enumeration. */
usb_disable_endpoint(udev, 0); usb_disable_endpoint(udev, 0);
...@@ -2815,7 +2788,7 @@ int usb_reset_device(struct usb_device *udev) ...@@ -2815,7 +2788,7 @@ int usb_reset_device(struct usb_device *udev)
} }
if (!udev->actconfig) if (!udev->actconfig)
return 0; goto done;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0, USB_REQ_SET_CONFIGURATION, 0,
...@@ -2849,6 +2822,9 @@ int usb_reset_device(struct usb_device *udev) ...@@ -2849,6 +2822,9 @@ int usb_reset_device(struct usb_device *udev)
} }
} }
done:
if (udev_is_a_hub)
hub_post_reset(udev);
return 0; return 0;
re_enumerate: 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