From 72ebddb59a644732ff0240cdf5d216952a2b0c6b Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 11 Apr 2005 18:34:17 -0700 Subject: [PATCH] USB: turn a user mode driver error into a hard error This patch turns a user mode driver error into a hard error, and updates the relevant diagnostic slightly to help troubleshooting. gphoto was known to have this problem, hopefully it is now fixed (they have had plenty of warning...) This had been left as a soft error to give various user mode drivers a change to be properly fixed, with the statement that starting in about 2.6.10 it would be changed. It had been mostly safe as a soft error ... but that can not be guaranteed. Now that a year has passed, it's time to really insist that the user mode drivers finally fix their relevant bugs. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 6bfab4bcaa9e..787c27a63c51 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -784,16 +784,16 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { if (usb_interface_claimed(actconfig->interface[i])) { dev_warn (&ps->dev->dev, - "usbfs: interface %d claimed " + "usbfs: interface %d claimed by %s " "while '%s' sets config #%d\n", actconfig->interface[i] ->cur_altsetting ->desc.bInterfaceNumber, + actconfig->interface[i] + ->dev.driver->name, current->comm, u); -#if 0 /* FIXME: enable in 2.6.10 or so */ status = -EBUSY; break; -#endif } } } -- cgit From d5926ae7a827bdd06b588ffbc56fd4525cd9214a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 21 Apr 2005 15:56:37 -0400 Subject: [PATCH] usbcore support for root-hub IRQ instead of polling This is a revised version of an earlier patch to add support to usbcore for driving root hubs by interrupts rather than polling. There's a temporary flag added to struct usb_hcd, marking devices whose drivers are aware of the new mechanism. By default that flag doesn't get set so drivers will continue to see the same polling behavior as before. This way we can convert the HCDs one by one to use interrupt-based event reporting, and the temporary flag can be removed when they're all done. Also included is a small change to the hcd_disable_endpoint routine. Although endpoints normally shouldn't be disabled while a controller is suspended, it's legal to do so when the controller's driver is being rmmod'ed. Lastly the patch adds a new callback, .hub_irq_enable, for use by HCDs where the root hub's port-change interrupts are level-triggered rather than edge-triggered. The callback is invoked each time khubd has finished processing a root hub, to let the HCD know that the interrupt can safely be re-enabled. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 207 +++++++++++++++++++++++++++++-------------------- drivers/usb/core/hcd.h | 15 +++- drivers/usb/core/hub.c | 5 ++ 3 files changed, 140 insertions(+), 87 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0da23732e807..1180c157b717 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -519,119 +519,120 @@ error: /*-------------------------------------------------------------------------*/ /* - * Root Hub interrupt transfers are synthesized with a timer. - * Completions are called in_interrupt() but not in_irq(). + * Root Hub interrupt transfers are polled using a timer if the + * driver requests it; otherwise the driver is responsible for + * calling usb_hcd_poll_rh_status() when an event occurs. * - * Note: some root hubs (including common UHCI based designs) can't - * correctly issue port change IRQs. They're the ones that _need_ a - * timer; most other root hubs don't. Some systems could save a - * lot of battery power by eliminating these root hub timer IRQs. + * Completions are called in_interrupt(), but they may or may not + * be in_irq(). */ +void usb_hcd_poll_rh_status(struct usb_hcd *hcd) +{ + struct urb *urb; + int length; + unsigned long flags; + char buffer[4]; /* Any root hubs with > 31 ports? */ -static void rh_report_status (unsigned long ptr); + if (!hcd->uses_new_polling && !hcd->status_urb) + return; -static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) -{ - int len = 1 + (urb->dev->maxchild / 8); + length = hcd->driver->hub_status_data(hcd, buffer); + if (length > 0) { - /* rh_timer protected by hcd_data_lock */ - if (hcd->rh_timer.data || urb->transfer_buffer_length < len) { - dev_dbg (hcd->self.controller, - "not queuing rh status urb, stat %d\n", - urb->status); - return -EINVAL; + /* try to complete the status urb */ + local_irq_save (flags); + spin_lock(&hcd_root_hub_lock); + urb = hcd->status_urb; + if (urb) { + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) { + hcd->poll_pending = 0; + hcd->status_urb = NULL; + urb->status = 0; + urb->hcpriv = NULL; + urb->actual_length = length; + memcpy(urb->transfer_buffer, buffer, length); + } else /* urb has been unlinked */ + length = 0; + spin_unlock(&urb->lock); + } else + length = 0; + spin_unlock(&hcd_root_hub_lock); + + /* local irqs are always blocked in completions */ + if (length > 0) + usb_hcd_giveback_urb (hcd, urb, NULL); + else + hcd->poll_pending = 1; + local_irq_restore (flags); } - init_timer (&hcd->rh_timer); - hcd->rh_timer.function = rh_report_status; - hcd->rh_timer.data = (unsigned long) urb; - /* USB 2.0 spec says 256msec; this is close enough */ - hcd->rh_timer.expires = jiffies + HZ/4; - add_timer (&hcd->rh_timer); - urb->hcpriv = hcd; /* nonzero to indicate it's queued */ - return 0; + /* The USB 2.0 spec says 256 ms. This is close enough and won't + * exceed that limit if HZ is 100. */ + if (hcd->uses_new_polling ? hcd->poll_rh : + (length == 0 && hcd->status_urb != NULL)) + mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250)); } +EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); /* timer callback */ +static void rh_timer_func (unsigned long _hcd) +{ + usb_hcd_poll_rh_status((struct usb_hcd *) _hcd); +} + +/*-------------------------------------------------------------------------*/ -static void rh_report_status (unsigned long ptr) +static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) { - struct urb *urb; - struct usb_hcd *hcd; - int length = 0; + int retval; unsigned long flags; + int len = 1 + (urb->dev->maxchild / 8); - urb = (struct urb *) ptr; - local_irq_save (flags); - spin_lock (&urb->lock); + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (urb->status != -EINPROGRESS) /* already unlinked */ + retval = urb->status; + else if (hcd->status_urb || urb->transfer_buffer_length < len) { + dev_dbg (hcd->self.controller, "not queuing rh status urb\n"); + retval = -EINVAL; + } else { + hcd->status_urb = urb; + urb->hcpriv = hcd; /* indicate it's queued */ - /* do nothing if the urb's been unlinked */ - if (!urb->dev - || urb->status != -EINPROGRESS - || (hcd = urb->dev->bus->hcpriv) == NULL) { - spin_unlock (&urb->lock); - local_irq_restore (flags); - return; - } + if (!hcd->uses_new_polling) + mod_timer (&hcd->rh_timer, jiffies + + msecs_to_jiffies(250)); - /* complete the status urb, or retrigger the timer */ - spin_lock (&hcd_data_lock); - if (urb->dev->state == USB_STATE_CONFIGURED) { - length = hcd->driver->hub_status_data ( - hcd, urb->transfer_buffer); - if (length > 0) { - hcd->rh_timer.data = 0; - urb->actual_length = length; - urb->status = 0; - urb->hcpriv = NULL; - } else - mod_timer (&hcd->rh_timer, jiffies + HZ/4); + /* If a status change has already occurred, report it ASAP */ + else if (hcd->poll_pending) + mod_timer (&hcd->rh_timer, jiffies); + retval = 0; } - spin_unlock (&hcd_data_lock); - spin_unlock (&urb->lock); - - /* local irqs are always blocked in completions */ - if (length > 0) - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); + return retval; } -/*-------------------------------------------------------------------------*/ - static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) { - if (usb_pipeint (urb->pipe)) { - int retval; - unsigned long flags; - - spin_lock_irqsave (&hcd_data_lock, flags); - retval = rh_status_urb (hcd, urb); - spin_unlock_irqrestore (&hcd_data_lock, flags); - return retval; - } + if (usb_pipeint (urb->pipe)) + return rh_queue_status (hcd, urb); if (usb_pipecontrol (urb->pipe)) return rh_call_control (hcd, urb); - else - return -EINVAL; + return -EINVAL; } /*-------------------------------------------------------------------------*/ +/* Asynchronous unlinks of root-hub control URBs are legal, but they + * don't do anything. Status URB unlinks must be made in process context + * with interrupts enabled. + */ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { - unsigned long flags; - - /* note: always a synchronous unlink */ - if ((unsigned long) urb == hcd->rh_timer.data) { - del_timer_sync (&hcd->rh_timer); - hcd->rh_timer.data = 0; - - local_irq_save (flags); - urb->hcpriv = NULL; - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_restore (flags); + if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */ + if (in_interrupt()) + return 0; /* nothing to do */ - } else if (usb_pipeendpoint(urb->pipe) == 0) { spin_lock_irq(&urb->lock); /* from usb_kill_urb */ ++urb->reject; spin_unlock_irq(&urb->lock); @@ -642,8 +643,22 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) spin_lock_irq(&urb->lock); --urb->reject; spin_unlock_irq(&urb->lock); - } else - return -EINVAL; + + } else { /* Status URB */ + if (!hcd->uses_new_polling) + del_timer_sync (&hcd->rh_timer); + local_irq_disable (); + spin_lock (&hcd_root_hub_lock); + if (urb == hcd->status_urb) { + hcd->status_urb = NULL; + urb->hcpriv = NULL; + } else + urb = NULL; /* wasn't fully queued */ + spin_unlock (&hcd_root_hub_lock); + if (urb) + usb_hcd_giveback_urb (hcd, urb, NULL); + local_irq_enable (); + } return 0; } @@ -885,6 +900,16 @@ int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) } EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub); +void usb_enable_root_hub_irq (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of (bus, struct usb_hcd, self); + if (hcd->driver->hub_irq_enable && !hcd->poll_rh && + hcd->state != HC_STATE_HALT) + hcd->driver->hub_irq_enable (hcd); +} + /*-------------------------------------------------------------------------*/ @@ -1348,7 +1373,8 @@ hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) hcd = udev->bus->hcpriv; - WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); + WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && + udev->state != USB_STATE_NOTATTACHED); local_irq_disable (); @@ -1612,6 +1638,8 @@ void usb_hc_died (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) { + hcd->poll_rh = 0; + del_timer(&hcd->rh_timer); /* make khubd clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, @@ -1665,6 +1693,8 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, hcd->self.bus_name = bus_name; init_timer(&hcd->rh_timer); + hcd->rh_timer.function = rh_timer_func; + hcd->rh_timer.data = (unsigned long) hcd; hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -1748,6 +1778,8 @@ int usb_add_hcd(struct usb_hcd *hcd, goto err3; } + if (hcd->uses_new_polling && hcd->poll_rh) + usb_hcd_poll_rh_status(hcd); return retval; err3: @@ -1782,6 +1814,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); usb_disconnect(&hcd->self.root_hub); + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 325a51656c3f..ac5752778e39 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -65,7 +65,8 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ const char *product_desc; /* product/vendor string */ char irq_descr[24]; /* driver + bus # */ - struct timer_list rh_timer; /* drives root hub */ + struct timer_list rh_timer; /* drives root-hub polling */ + struct urb *status_urb; /* the current status urb */ /* * hardware info/state @@ -76,6 +77,12 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ unsigned remote_wakeup:1;/* sw should use wakeup? */ unsigned rh_registered:1;/* is root hub registered? */ + /* The next flag is a stopgap, to be removed when all the HCDs + * support the new root-hub polling mechanism. */ + unsigned uses_new_polling:1; + unsigned poll_rh:1; /* poll for rh status? */ + unsigned poll_pending:1; /* status has changed? */ + int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ u64 rsrc_start; /* memory/io resource start */ @@ -207,6 +214,8 @@ struct hc_driver { int (*hub_suspend)(struct usb_hcd *); int (*hub_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned port_num); + void (*hub_irq_enable)(struct usb_hcd *); + /* Needed only if port-change IRQs are level-triggered */ }; extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); @@ -243,7 +252,9 @@ void hcd_buffer_free (struct usb_bus *bus, size_t size, /* generic bus glue, needed for host controllers that don't use PCI */ extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); + extern void usb_hc_died (struct usb_hcd *hcd); +extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); /* -------------------------------------------------------------------------- */ @@ -360,6 +371,8 @@ extern wait_queue_head_t usb_kill_urb_queue; extern struct usb_bus *usb_bus_get (struct usb_bus *bus); extern void usb_bus_put (struct usb_bus *bus); +extern void usb_enable_root_hub_irq (struct usb_bus *bus); + extern int usb_find_interface_driver (struct usb_device *dev, struct usb_interface *interface); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a8d879a85d04..6d1a330d577b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2787,6 +2787,11 @@ static void hub_events(void) hub->activating = 0; + /* If this is a root hub, tell the HCD it's okay to + * re-enable port-change interrupts now. */ + if (!hdev->parent) + usb_enable_root_hub_irq(hdev->bus); + loop: usb_unlock_device(hdev); usb_put_intf(intf); -- cgit From 7d35b9298539d2818c51fe9070b08cf9876016f4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 25 Apr 2005 11:18:32 -0400 Subject: [PATCH] usbcore: Remove hub_set_power_budget This patch removes the hub_set_power_budget routine, which was used by a couple of HCDs to indicate that the root hub was running on battery power. In its place is a new field added to struct usb_hcd, which HCDs can set before the root hub is registered. Special-case code in the hub driver knows to look at this field when configuring a root hub. Signed-off-by: Alan Stern Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.h | 1 + drivers/usb/core/hub.c | 12 +++++++++--- drivers/usb/core/hub.h | 11 ----------- 3 files changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index ac5752778e39..3837f68bb7b3 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -87,6 +87,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ void __iomem *regs; /* device memory/io */ u64 rsrc_start; /* memory/io resource start */ u64 rsrc_len; /* memory/io resource length */ + unsigned power_budget; /* in mA, 0 = no limit */ #define HCD_BUFFER_POOLS 4 struct dma_pool *pool [HCD_BUFFER_POOLS]; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 6d1a330d577b..63ee3d97b6a9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -643,15 +643,21 @@ static int hub_configure(struct usb_hub *hub, message = "can't get hub status"; goto fail; } - cpu_to_le16s(&hubstatus); - if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { + le16_to_cpus(&hubstatus); + if (hdev == hdev->bus->root_hub) { + struct usb_hcd *hcd = + container_of(hdev->bus, struct usb_hcd, self); + + hub->power_budget = min(500u, hcd->power_budget) / 2; + } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", hub->descriptor->bHubContrCurrent); hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) / 2; + } + if (hub->power_budget) dev_dbg(hub_dev, "%dmA bus power budget for children\n", hub->power_budget * 2); - } ret = hub_hub_status(hub, &hubstatus, &hubchange); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index d114b847d56f..53bf5649621e 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -224,15 +224,4 @@ struct usb_hub { struct work_struct leds; }; -/* use this for low-powered root hubs */ -static inline void -hub_set_power_budget (struct usb_device *hubdev, unsigned mA) -{ - struct usb_hub *hub; - - hub = (struct usb_hub *) - usb_get_intfdata (hubdev->actconfig->interface[0]); - hub->power_budget = min(mA,(unsigned)500)/2; -} - #endif /* __LINUX_HUB_H */ -- cgit From 8ec8d20b21f00a36343ca0ebd6c6be9421724a1e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 25 Apr 2005 11:25:17 -0400 Subject: [PATCH] usbcore: register root hub in usb_add_hcd This patch makes usbcore automatically allocate and register the root hub device for a new host controller when the controller is registered. This way the HCDs don't all have to include the same boilerplate code. As a pleasant side benefit, the register_root_hub routine can now be made static and not EXPORTed. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 72 ++++++++++++++++++++++++++++++++++---------------- drivers/usb/core/hcd.h | 3 --- 2 files changed, 49 insertions(+), 26 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 1180c157b717..83e732a0d64a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -832,30 +832,22 @@ static void usb_deregister_bus (struct usb_bus *bus) } /** - * usb_hcd_register_root_hub - called by HCD to register its root hub + * register_root_hub - called by usb_add_hcd() to register a root hub * @usb_dev: the usb root hub device to be registered. * @hcd: host controller for this root hub * - * The USB host controller calls this function to register the root hub - * properly with the USB subsystem. It sets up the device properly in - * the device tree and stores the root_hub pointer in the bus structure, - * then calls usb_new_device() to register the usb device. It also - * assigns the root hub's USB address (always 1). + * This function registers the root hub with the USB subsystem. It sets up + * the device properly in the device tree and stores the root_hub pointer + * in the bus structure, then calls usb_new_device() to register the usb + * device. It also assigns the root hub's USB address (always 1). */ -int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) +static int register_root_hub (struct usb_device *usb_dev, + struct usb_hcd *hcd) { struct device *parent_dev = hcd->self.controller; const int devnum = 1; int retval; - /* hcd->driver->start() reported can_wakeup, probably with - * assistance from board's boot firmware. - * NOTE: normal devices won't enable wakeup by default. - */ - if (hcd->can_wakeup) - dev_dbg (parent_dev, "supports USB remote wakeup\n"); - hcd->remote_wakeup = hcd->can_wakeup; - usb_dev->devnum = devnum; usb_dev->bus->devnum_next = devnum + 1; memset (&usb_dev->bus->devmap.devicemap, 0, @@ -898,7 +890,6 @@ int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub); void usb_enable_root_hub_irq (struct usb_bus *bus) { @@ -1724,7 +1715,8 @@ EXPORT_SYMBOL (usb_put_hcd); int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags) { - int retval; + int retval; + struct usb_device *rhdev; dev_info(hcd->self.controller, "%s\n", hcd->product_desc); @@ -1740,7 +1732,7 @@ int usb_add_hcd(struct usb_hcd *hcd, } if ((retval = usb_register_bus(&hcd->self)) < 0) - goto err1; + goto err_register_bus; if (hcd->driver->irq) { char buf[8], *bufp = buf; @@ -1757,7 +1749,7 @@ int usb_add_hcd(struct usb_hcd *hcd, hcd->irq_descr, hcd)) != 0) { dev_err(hcd->self.controller, "request interrupt %s failed\n", bufp); - goto err2; + goto err_request_irq; } hcd->irq = irqnum; dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp, @@ -1773,21 +1765,55 @@ int usb_add_hcd(struct usb_hcd *hcd, (unsigned long long)hcd->rsrc_start); } + /* Allocate the root hub before calling hcd->driver->start(), + * but don't register it until afterward so that the hardware + * is running. + */ + if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { + dev_err(hcd->self.controller, "unable to allocate root hub\n"); + retval = -ENOMEM; + goto err_allocate_root_hub; + } + rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : + USB_SPEED_FULL; + + /* Although in principle hcd->driver->start() might need to use rhdev, + * none of the current drivers do. + */ if ((retval = hcd->driver->start(hcd)) < 0) { dev_err(hcd->self.controller, "startup error %d\n", retval); - goto err3; + goto err_hcd_driver_start; } + /* hcd->driver->start() reported can_wakeup, probably with + * assistance from board's boot firmware. + * NOTE: normal devices won't enable wakeup by default. + */ + if (hcd->can_wakeup) + dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); + hcd->remote_wakeup = hcd->can_wakeup; + + if ((retval = register_root_hub(rhdev, hcd)) != 0) + goto err_register_root_hub; + if (hcd->uses_new_polling && hcd->poll_rh) usb_hcd_poll_rh_status(hcd); return retval; - err3: + err_register_root_hub: + hcd->driver->stop(hcd); + + err_hcd_driver_start: + usb_put_dev(rhdev); + + err_allocate_root_hub: if (hcd->irq >= 0) free_irq(irqnum, hcd); - err2: + + err_request_irq: usb_deregister_bus(&hcd->self); - err1: + + err_register_bus: hcd_buffer_destroy(hcd); return retval; } diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 3837f68bb7b3..8dc13cde2f73 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -353,9 +353,6 @@ extern long usb_calc_bus_time (int speed, int is_input, extern struct usb_bus *usb_alloc_bus (struct usb_operations *); -extern int usb_hcd_register_root_hub (struct usb_device *usb_dev, - struct usb_hcd *hcd); - extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); extern void usb_set_device_state(struct usb_device *udev, -- cgit From 0ed0c0c48c508578c30aa58f755ca0d692636906 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 15 Jun 2005 15:49:48 -0400 Subject: [PATCH] USB: usbcore: inverted test for resuming interfaces This one-liner fixes a test for interfaces that are already resumed. It would be nice if this could get into 2.6.12, but it's not critical since it only affects people doing selective (runtime) suspend/resume. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 63ee3d97b6a9..32ff32181852 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1733,7 +1733,7 @@ static int finish_port_resume(struct usb_device *udev) struct usb_driver *driver; intf = udev->actconfig->interface[i]; - if (intf->dev.power.power_state == PMSG_SUSPEND) + if (intf->dev.power.power_state == PMSG_ON) continue; if (!intf->dev.driver) { /* FIXME maybe force to alt 0 */ -- cgit