USB: simplify hub_restart() logic
This patch (as1081) straightens out the logic of the hub_restart() routine. Each port of the hub is scanned and the driver makes sure that ports which are supposed to be disabled really _are_ disabled. Any ports with a significant change in status are flagged in hub->change_bits, so that khubd can focus on them without the need to scan all the ports a second time -- which means the hub->activating flag is no longer needed. Also, it is now recognized explicitly that the only reason for resuming a port which was not suspended is to carry out a reset-resume operation, which happens only in a non-CONFIG_USB_SUSPEND setting. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
9e5eace734
commit
6ee0b270c7
@@ -805,8 +805,6 @@ static int usb_resume_device(struct usb_device *udev)
|
|||||||
|
|
||||||
if (udev->state == USB_STATE_NOTATTACHED)
|
if (udev->state == USB_STATE_NOTATTACHED)
|
||||||
goto done;
|
goto done;
|
||||||
if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Can't resume it if it doesn't have a driver. */
|
/* Can't resume it if it doesn't have a driver. */
|
||||||
if (udev->dev.driver == NULL) {
|
if (udev->dev.driver == NULL) {
|
||||||
@@ -1173,11 +1171,8 @@ static int usb_resume_both(struct usb_device *udev)
|
|||||||
* then we're stuck. */
|
* then we're stuck. */
|
||||||
status = usb_resume_device(udev);
|
status = usb_resume_device(udev);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (udev->reset_resume)
|
||||||
|
|
||||||
/* Needed for reset-resume */
|
|
||||||
status = usb_resume_device(udev);
|
status = usb_resume_device(udev);
|
||||||
}
|
|
||||||
|
|
||||||
if (status == 0 && udev->actconfig) {
|
if (status == 0 && udev->actconfig) {
|
||||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||||
|
@@ -72,7 +72,6 @@ struct usb_hub {
|
|||||||
|
|
||||||
unsigned limited_power:1;
|
unsigned limited_power:1;
|
||||||
unsigned quiescing:1;
|
unsigned quiescing:1;
|
||||||
unsigned activating:1;
|
|
||||||
unsigned disconnected:1;
|
unsigned disconnected:1;
|
||||||
|
|
||||||
unsigned has_indicators:1;
|
unsigned has_indicators:1;
|
||||||
@@ -539,7 +538,6 @@ static void hub_quiesce(struct usb_hub *hub)
|
|||||||
{
|
{
|
||||||
/* (nonblocking) khubd and related activity won't re-trigger */
|
/* (nonblocking) khubd and related activity won't re-trigger */
|
||||||
hub->quiescing = 1;
|
hub->quiescing = 1;
|
||||||
hub->activating = 0;
|
|
||||||
|
|
||||||
/* (blocking) stop khubd and related activity */
|
/* (blocking) stop khubd and related activity */
|
||||||
usb_kill_urb(hub->urb);
|
usb_kill_urb(hub->urb);
|
||||||
@@ -554,7 +552,6 @@ static void hub_activate(struct usb_hub *hub)
|
|||||||
int status;
|
int status;
|
||||||
|
|
||||||
hub->quiescing = 0;
|
hub->quiescing = 0;
|
||||||
hub->activating = 1;
|
|
||||||
|
|
||||||
status = usb_submit_urb(hub->urb, GFP_NOIO);
|
status = usb_submit_urb(hub->urb, GFP_NOIO);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
@@ -638,81 +635,83 @@ static void hub_stop(struct usb_hub *hub)
|
|||||||
hub_quiesce(hub);
|
hub_quiesce(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HUB_RESET 1
|
enum hub_activation_type {
|
||||||
#define HUB_RESUME 2
|
HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
|
||||||
#define HUB_RESET_RESUME 3
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
static void hub_restart(struct usb_hub *hub, enum hub_activation_type type)
|
||||||
|
|
||||||
static void hub_restart(struct usb_hub *hub, int type)
|
|
||||||
{
|
{
|
||||||
struct usb_device *hdev = hub->hdev;
|
struct usb_device *hdev = hub->hdev;
|
||||||
int port1;
|
int port1;
|
||||||
|
|
||||||
/* Check each of the children to see if they require
|
/* Check each port and set hub->change_bits to let khubd know
|
||||||
* USB-PERSIST handling or disconnection. Also check
|
* which ports need attention.
|
||||||
* each unoccupied port to make sure it is still disabled.
|
|
||||||
*/
|
*/
|
||||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||||
struct usb_device *udev = hdev->children[port1-1];
|
struct usb_device *udev = hdev->children[port1-1];
|
||||||
int status = 0;
|
int status;
|
||||||
u16 portstatus, portchange;
|
u16 portstatus, portchange;
|
||||||
|
|
||||||
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
|
portstatus = portchange = 0;
|
||||||
if (type != HUB_RESET) {
|
status = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||||
status = hub_port_status(hub, port1,
|
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
|
||||||
&portstatus, &portchange);
|
dev_dbg(hub->intfdev,
|
||||||
if (status == 0 && (portstatus &
|
"port %d: status %04x change %04x\n",
|
||||||
USB_PORT_STAT_ENABLE))
|
port1, portstatus, portchange);
|
||||||
clear_port_feature(hdev, port1,
|
|
||||||
USB_PORT_FEAT_ENABLE);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Was the power session lost while we were suspended? */
|
/* After anything other than HUB_RESUME (i.e., initialization
|
||||||
switch (type) {
|
* or any sort of reset), every port should be disabled.
|
||||||
case HUB_RESET_RESUME:
|
* Unconnected ports should likewise be disabled (paranoia),
|
||||||
portstatus = 0;
|
* and so should ports for which we have no usb_device.
|
||||||
portchange = USB_PORT_STAT_C_CONNECTION;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HUB_RESET:
|
|
||||||
case HUB_RESUME:
|
|
||||||
status = hub_port_status(hub, port1,
|
|
||||||
&portstatus, &portchange);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For "USB_PERSIST"-enabled children we must
|
|
||||||
* mark the child device for reset-resume and
|
|
||||||
* turn off the various status changes to prevent
|
|
||||||
* khubd from disconnecting it later.
|
|
||||||
*/
|
*/
|
||||||
if (udev->persist_enabled && status == 0 &&
|
if ((portstatus & USB_PORT_STAT_ENABLE) && (
|
||||||
!(portstatus & USB_PORT_STAT_ENABLE)) {
|
type != HUB_RESUME ||
|
||||||
|
!(portstatus & USB_PORT_STAT_CONNECTION) ||
|
||||||
|
!udev ||
|
||||||
|
udev->state == USB_STATE_NOTATTACHED)) {
|
||||||
|
clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||||
|
portstatus &= ~USB_PORT_STAT_ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
|
||||||
|
/* Tell khubd to disconnect the device or
|
||||||
|
* check for a new connection
|
||||||
|
*/
|
||||||
|
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
|
||||||
|
set_bit(port1, hub->change_bits);
|
||||||
|
|
||||||
|
} else if (portstatus & USB_PORT_STAT_ENABLE) {
|
||||||
|
/* The power session apparently survived the resume.
|
||||||
|
* If there was an overcurrent or suspend change
|
||||||
|
* (i.e., remote wakeup request), have khubd
|
||||||
|
* take care of it.
|
||||||
|
*/
|
||||||
|
if (portchange)
|
||||||
|
set_bit(port1, hub->change_bits);
|
||||||
|
|
||||||
|
} else if (udev->persist_enabled) {
|
||||||
|
/* Turn off the status changes to prevent khubd
|
||||||
|
* from disconnecting the device.
|
||||||
|
*/
|
||||||
if (portchange & USB_PORT_STAT_C_ENABLE)
|
if (portchange & USB_PORT_STAT_C_ENABLE)
|
||||||
clear_port_feature(hub->hdev, port1,
|
clear_port_feature(hub->hdev, port1,
|
||||||
USB_PORT_FEAT_C_ENABLE);
|
USB_PORT_FEAT_C_ENABLE);
|
||||||
if (portchange & USB_PORT_STAT_C_CONNECTION)
|
if (portchange & USB_PORT_STAT_C_CONNECTION)
|
||||||
clear_port_feature(hub->hdev, port1,
|
clear_port_feature(hub->hdev, port1,
|
||||||
USB_PORT_FEAT_C_CONNECTION);
|
USB_PORT_FEAT_C_CONNECTION);
|
||||||
|
#ifdef CONFIG_PM
|
||||||
udev->reset_resume = 1;
|
udev->reset_resume = 1;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
/* The power session is gone; tell khubd */
|
||||||
|
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
|
||||||
|
set_bit(port1, hub->change_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise for a reset_resume we must disconnect the child,
|
|
||||||
* but as we may not lock the child device here
|
|
||||||
* we have to do a "logical" disconnect.
|
|
||||||
*/
|
|
||||||
else if (type == HUB_RESET_RESUME)
|
|
||||||
hub_port_logical_disconnect(hub, port1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hub_activate(hub);
|
hub_activate(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
|
||||||
|
|
||||||
/* caller has locked the hub device */
|
/* caller has locked the hub device */
|
||||||
static int hub_pre_reset(struct usb_interface *intf)
|
static int hub_pre_reset(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
@@ -728,7 +727,7 @@ static int hub_post_reset(struct usb_interface *intf)
|
|||||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||||
|
|
||||||
hub_power_on(hub);
|
hub_power_on(hub);
|
||||||
hub_activate(hub);
|
hub_restart(hub, HUB_POST_RESET);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2903,7 +2902,7 @@ static void hub_events(void)
|
|||||||
continue;
|
continue;
|
||||||
connect_change = test_bit(i, hub->change_bits);
|
connect_change = test_bit(i, hub->change_bits);
|
||||||
if (!test_and_clear_bit(i, hub->event_bits) &&
|
if (!test_and_clear_bit(i, hub->event_bits) &&
|
||||||
!connect_change && !hub->activating)
|
!connect_change)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = hub_port_status(hub, i,
|
ret = hub_port_status(hub, i,
|
||||||
@@ -2911,11 +2910,6 @@ static void hub_events(void)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (hub->activating && !hdev->children[i-1] &&
|
|
||||||
(portstatus &
|
|
||||||
USB_PORT_STAT_CONNECTION))
|
|
||||||
connect_change = 1;
|
|
||||||
|
|
||||||
if (portchange & USB_PORT_STAT_C_CONNECTION) {
|
if (portchange & USB_PORT_STAT_C_CONNECTION) {
|
||||||
clear_port_feature(hdev, i,
|
clear_port_feature(hdev, i,
|
||||||
USB_PORT_FEAT_C_CONNECTION);
|
USB_PORT_FEAT_C_CONNECTION);
|
||||||
@@ -3011,8 +3005,6 @@ static void hub_events(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hub->activating = 0;
|
|
||||||
|
|
||||||
/* If this is a root hub, tell the HCD it's okay to
|
/* If this is a root hub, tell the HCD it's okay to
|
||||||
* re-enable port-change interrupts now. */
|
* re-enable port-change interrupts now. */
|
||||||
if (!hdev->parent && !hub->busy_bits[0])
|
if (!hdev->parent && !hub->busy_bits[0])
|
||||||
|
Reference in New Issue
Block a user