aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/devio.c6
-rw-r--r--drivers/usb/core/hcd.c279
-rw-r--r--drivers/usb/core/hcd.h19
-rw-r--r--drivers/usb/core/hub.c19
-rw-r--r--drivers/usb/core/hub.h11
5 files changed, 203 insertions, 131 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 6bfab4b..787c27a 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
}
}
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 0da2373..83e732a 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;
+ if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+ if (in_interrupt())
+ return 0; /* nothing to do */
- /* 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);
-
- } 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;
}
@@ -817,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,
@@ -883,7 +890,16 @@ 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)
+{
+ 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 +1364,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 +1629,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 +1684,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 :
@@ -1694,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);
@@ -1710,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;
@@ -1727,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,
@@ -1743,19 +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;
}
@@ -1782,6 +1840,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 325a516..8dc13cd 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,10 +77,17 @@ 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 */
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];
@@ -207,6 +215,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 +253,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);
/* -------------------------------------------------------------------------- */
@@ -341,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,
@@ -360,6 +369,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 a8d879a..32ff321 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);
@@ -1727,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 */
@@ -2787,6 +2793,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);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index d114b84..53bf564 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 */