On Wednesday 08 September 2004 11:14 am, Greg KH wrote: > > Care to re-diff it? I can guess at what should be done...
Here you go! The last chunk moved into Alan's new logical_disconnect() routine. I also added a FIXME there, capturing some of that discussion about port power down ... it'd make PM_SUSPEND_DISK happier too. - Dave
Fix the hub probe logic so that when khubd wakes up, it will actually look at all ports on the new hub. This resolved a root hub enumeration issue, and I expect the "boot from USB disk" folk will like this too. The fix includes cleanup: centralizing the logic to make khubd look at a given hub, instead of cloning it three times (or in the case of hub probing, frankensteining it). This also adds a FIXME to the new routine centralizing disconnect processing: we also want "power down port but don't wake khubd" (for PM_SUSPEND_DISK as well as SRP). Signed-off-by: David Brownell <[EMAIL PROTECTED]> --- 1.132/drivers/usb/core/hub.c Wed Sep 8 09:41:15 2004 +++ edited/drivers/usb/core/hub.c Thu Sep 9 09:44:08 2004 @@ -39,12 +39,13 @@ /* Protect struct usb_device state and children members */ static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED; -/* Wakes up khubd */ +/* khubd's worklist and its lock */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; - static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ +/* Wakes up khubd */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); + static pid_t khubd_pid = 0; /* PID of khubd */ static DECLARE_COMPLETION(khubd_exited); @@ -226,6 +227,19 @@ data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT); } +static void kick_khubd(struct usb_hub *hub) +{ + unsigned long flags; + + spin_lock_irqsave(&hub_event_lock, flags); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); +} + + /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb, struct pt_regs *regs) { @@ -261,12 +275,7 @@ hub->nerrors = 0; /* Something happened, let khubd figure it out */ - spin_lock(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock(&hub_event_lock); + kick_khubd(hub); resubmit: if (hub->quiescing) @@ -579,6 +588,9 @@ dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); + /* scan all ports ASAP on new hubs */ + hub->change_bits[0] = ~0; + /* Start the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); @@ -604,7 +616,7 @@ } /* Wake up khubd */ - wake_up(&khubd_wait); + kick_khubd(hub); /* maybe start cycling the hub leds */ if (hub->has_indicators && blinkenlights) { @@ -1407,15 +1419,18 @@ dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1); hub_port_disable(hdev, port); + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - usb_suspend_device(dev,PM_SUSPEND_DISK) + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + hub = usb_get_intfdata(hdev->actconfig->interface[0]); set_bit(port, hub->change_bits); - - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock_irq(&hub_event_lock); + kick_khubd(hub); }