Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 3664
b: refs/heads/master
c: d5926ae
h: refs/heads/master
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jun 27, 2005
1 parent 8080128 commit 28e5d19
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 88 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: 02597d2deec2a3de0e2b52c1f83904b65626a0d5
refs/heads/master: d5926ae7a827bdd06b588ffbc56fd4525cd9214a
207 changes: 121 additions & 86 deletions trunk/drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,119 +519,120 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/

/*
* 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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
}


/*-------------------------------------------------------------------------*/

Expand Down Expand Up @@ -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 ();

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 :
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;

Expand Down
15 changes: 14 additions & 1 deletion trunk/drivers/usb/core/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

/* -------------------------------------------------------------------------- */

Expand Down Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 28e5d19

Please sign in to comment.