Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 115766
b: refs/heads/master
c: 8520f38
h: refs/heads/master
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Oct 17, 2008
1 parent 0b47b6b commit 4687bf8
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 11 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: 3c4bb71f96c69ef3c81fda108c96b633a2000de2
refs/heads/master: 8520f38099ccfdac2147a0852f84ee7a8ee5e197
91 changes: 81 additions & 10 deletions trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct usb_hub {
unsigned has_indicators:1;
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
};


Expand Down Expand Up @@ -515,10 +516,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
}
EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer);

static void hub_power_on(struct usb_hub *hub)
/* If do_delay is false, return the number of milliseconds the caller
* needs to delay.
*/
static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
{
int port1;
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
unsigned delay;
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);

Expand All @@ -537,7 +542,10 @@ static void hub_power_on(struct usb_hub *hub)
set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);

/* Wait at least 100 msec for power to become stable */
msleep(max(pgood_delay, (unsigned) 100));
delay = max(pgood_delay, (unsigned) 100);
if (do_delay)
msleep(delay);
return delay;
}

static int hub_hub_status(struct usb_hub *hub,
Expand Down Expand Up @@ -599,21 +607,55 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
}

enum hub_activation_type {
HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
HUB_INIT, HUB_INIT2, HUB_INIT3,
HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};

static void hub_init_func2(struct work_struct *ws);
static void hub_init_func3(struct work_struct *ws);

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
struct usb_device *hdev = hub->hdev;
int port1;
int status;
bool need_debounce_delay = false;
unsigned delay;

/* Continue a partial initialization */
if (type == HUB_INIT2)
goto init2;
if (type == HUB_INIT3)
goto init3;

/* After a resume, port power should still be on.
* For any other type of activation, turn it on.
*/
if (type != HUB_RESUME)
hub_power_on(hub);
if (type != HUB_RESUME) {

/* Speed up system boot by using a delayed_work for the
* hub's initial power-up delays. This is pretty awkward
* and the implementation looks like a home-brewed sort of
* setjmp/longjmp, but it saves at least 100 ms for each
* root hub (assuming usbcore is compiled into the kernel
* rather than as a module). It adds up.
*
* This can't be done for HUB_RESUME or HUB_RESET_RESUME
* because for those activation types the ports have to be
* operational when we return. In theory this could be done
* for HUB_POST_RESET, but it's easier not to.
*/
if (type == HUB_INIT) {
delay = hub_power_on(hub, false);
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
schedule_delayed_work(&hub->init_work,
msecs_to_jiffies(delay));
return; /* Continues at init2: below */
} else {
hub_power_on(hub, true);
}
}
init2:

/* Check each port and set hub->change_bits to let khubd know
* which ports need attention.
Expand Down Expand Up @@ -692,9 +734,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
* If any port-status changes do occur during this delay, khubd
* will see them later and handle them normally.
*/
if (need_debounce_delay)
msleep(HUB_DEBOUNCE_STABLE);

if (need_debounce_delay) {
delay = HUB_DEBOUNCE_STABLE;

/* Don't do a long sleep inside a workqueue routine */
if (type == HUB_INIT2) {
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
schedule_delayed_work(&hub->init_work,
msecs_to_jiffies(delay));
return; /* Continues at init3: below */
} else {
msleep(delay);
}
}
init3:
hub->quiescing = 0;

status = usb_submit_urb(hub->urb, GFP_NOIO);
Expand All @@ -707,6 +760,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
kick_khubd(hub);
}

/* Implement the continuations for the delays above */
static void hub_init_func2(struct work_struct *ws)
{
struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);

hub_activate(hub, HUB_INIT2);
}

static void hub_init_func3(struct work_struct *ws)
{
struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);

hub_activate(hub, HUB_INIT3);
}

enum hub_quiescing_type {
HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
};
Expand All @@ -716,6 +784,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
struct usb_device *hdev = hub->hdev;
int i;

cancel_delayed_work_sync(&hub->init_work);

/* khubd and related activity won't re-trigger */
hub->quiescing = 1;

Expand Down Expand Up @@ -1099,6 +1169,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
hub->intfdev = &intf->dev;
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
usb_get_intf(intf);

usb_set_intfdata (intf, hub);
Expand Down Expand Up @@ -3035,7 +3106,7 @@ static void hub_events(void)
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_OVER_CURRENT);
hub_power_on(hub);
hub_power_on(hub, true);
}

if (portchange & USB_PORT_STAT_C_RESET) {
Expand Down Expand Up @@ -3070,7 +3141,7 @@ static void hub_events(void)
dev_dbg (hub_dev, "overcurrent change\n");
msleep(500); /* Cool down */
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
hub_power_on(hub);
hub_power_on(hub, true);
}
}

Expand Down

0 comments on commit 4687bf8

Please sign in to comment.