diff options
-rw-r--r-- | drivers/usb/core/hcd.c | 207 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 15 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 5 |
3 files changed, 140 insertions, 87 deletions
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: | |||
519 | /*-------------------------------------------------------------------------*/ | 519 | /*-------------------------------------------------------------------------*/ |
520 | 520 | ||
521 | /* | 521 | /* |
522 | * Root Hub interrupt transfers are synthesized with a timer. | 522 | * Root Hub interrupt transfers are polled using a timer if the |
523 | * Completions are called in_interrupt() but not in_irq(). | 523 | * driver requests it; otherwise the driver is responsible for |
524 | * calling usb_hcd_poll_rh_status() when an event occurs. | ||
524 | * | 525 | * |
525 | * Note: some root hubs (including common UHCI based designs) can't | 526 | * Completions are called in_interrupt(), but they may or may not |
526 | * correctly issue port change IRQs. They're the ones that _need_ a | 527 | * be in_irq(). |
527 | * timer; most other root hubs don't. Some systems could save a | ||
528 | * lot of battery power by eliminating these root hub timer IRQs. | ||
529 | */ | 528 | */ |
529 | void usb_hcd_poll_rh_status(struct usb_hcd *hcd) | ||
530 | { | ||
531 | struct urb *urb; | ||
532 | int length; | ||
533 | unsigned long flags; | ||
534 | char buffer[4]; /* Any root hubs with > 31 ports? */ | ||
530 | 535 | ||
531 | static void rh_report_status (unsigned long ptr); | 536 | if (!hcd->uses_new_polling && !hcd->status_urb) |
537 | return; | ||
532 | 538 | ||
533 | static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) | 539 | length = hcd->driver->hub_status_data(hcd, buffer); |
534 | { | 540 | if (length > 0) { |
535 | int len = 1 + (urb->dev->maxchild / 8); | ||
536 | 541 | ||
537 | /* rh_timer protected by hcd_data_lock */ | 542 | /* try to complete the status urb */ |
538 | if (hcd->rh_timer.data || urb->transfer_buffer_length < len) { | 543 | local_irq_save (flags); |
539 | dev_dbg (hcd->self.controller, | 544 | spin_lock(&hcd_root_hub_lock); |
540 | "not queuing rh status urb, stat %d\n", | 545 | urb = hcd->status_urb; |
541 | urb->status); | 546 | if (urb) { |
542 | return -EINVAL; | 547 | spin_lock(&urb->lock); |
548 | if (urb->status == -EINPROGRESS) { | ||
549 | hcd->poll_pending = 0; | ||
550 | hcd->status_urb = NULL; | ||
551 | urb->status = 0; | ||
552 | urb->hcpriv = NULL; | ||
553 | urb->actual_length = length; | ||
554 | memcpy(urb->transfer_buffer, buffer, length); | ||
555 | } else /* urb has been unlinked */ | ||
556 | length = 0; | ||
557 | spin_unlock(&urb->lock); | ||
558 | } else | ||
559 | length = 0; | ||
560 | spin_unlock(&hcd_root_hub_lock); | ||
561 | |||
562 | /* local irqs are always blocked in completions */ | ||
563 | if (length > 0) | ||
564 | usb_hcd_giveback_urb (hcd, urb, NULL); | ||
565 | else | ||
566 | hcd->poll_pending = 1; | ||
567 | local_irq_restore (flags); | ||
543 | } | 568 | } |
544 | 569 | ||
545 | init_timer (&hcd->rh_timer); | 570 | /* The USB 2.0 spec says 256 ms. This is close enough and won't |
546 | hcd->rh_timer.function = rh_report_status; | 571 | * exceed that limit if HZ is 100. */ |
547 | hcd->rh_timer.data = (unsigned long) urb; | 572 | if (hcd->uses_new_polling ? hcd->poll_rh : |
548 | /* USB 2.0 spec says 256msec; this is close enough */ | 573 | (length == 0 && hcd->status_urb != NULL)) |
549 | hcd->rh_timer.expires = jiffies + HZ/4; | 574 | mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250)); |
550 | add_timer (&hcd->rh_timer); | ||
551 | urb->hcpriv = hcd; /* nonzero to indicate it's queued */ | ||
552 | return 0; | ||
553 | } | 575 | } |
576 | EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); | ||
554 | 577 | ||
555 | /* timer callback */ | 578 | /* timer callback */ |
579 | static void rh_timer_func (unsigned long _hcd) | ||
580 | { | ||
581 | usb_hcd_poll_rh_status((struct usb_hcd *) _hcd); | ||
582 | } | ||
583 | |||
584 | /*-------------------------------------------------------------------------*/ | ||
556 | 585 | ||
557 | static void rh_report_status (unsigned long ptr) | 586 | static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) |
558 | { | 587 | { |
559 | struct urb *urb; | 588 | int retval; |
560 | struct usb_hcd *hcd; | ||
561 | int length = 0; | ||
562 | unsigned long flags; | 589 | unsigned long flags; |
590 | int len = 1 + (urb->dev->maxchild / 8); | ||
563 | 591 | ||
564 | urb = (struct urb *) ptr; | 592 | spin_lock_irqsave (&hcd_root_hub_lock, flags); |
565 | local_irq_save (flags); | 593 | if (urb->status != -EINPROGRESS) /* already unlinked */ |
566 | spin_lock (&urb->lock); | 594 | retval = urb->status; |
595 | else if (hcd->status_urb || urb->transfer_buffer_length < len) { | ||
596 | dev_dbg (hcd->self.controller, "not queuing rh status urb\n"); | ||
597 | retval = -EINVAL; | ||
598 | } else { | ||
599 | hcd->status_urb = urb; | ||
600 | urb->hcpriv = hcd; /* indicate it's queued */ | ||
567 | 601 | ||
568 | /* do nothing if the urb's been unlinked */ | 602 | if (!hcd->uses_new_polling) |
569 | if (!urb->dev | 603 | mod_timer (&hcd->rh_timer, jiffies + |
570 | || urb->status != -EINPROGRESS | 604 | msecs_to_jiffies(250)); |
571 | || (hcd = urb->dev->bus->hcpriv) == NULL) { | ||
572 | spin_unlock (&urb->lock); | ||
573 | local_irq_restore (flags); | ||
574 | return; | ||
575 | } | ||
576 | 605 | ||
577 | /* complete the status urb, or retrigger the timer */ | 606 | /* If a status change has already occurred, report it ASAP */ |
578 | spin_lock (&hcd_data_lock); | 607 | else if (hcd->poll_pending) |
579 | if (urb->dev->state == USB_STATE_CONFIGURED) { | 608 | mod_timer (&hcd->rh_timer, jiffies); |
580 | length = hcd->driver->hub_status_data ( | 609 | retval = 0; |
581 | hcd, urb->transfer_buffer); | ||
582 | if (length > 0) { | ||
583 | hcd->rh_timer.data = 0; | ||
584 | urb->actual_length = length; | ||
585 | urb->status = 0; | ||
586 | urb->hcpriv = NULL; | ||
587 | } else | ||
588 | mod_timer (&hcd->rh_timer, jiffies + HZ/4); | ||
589 | } | 610 | } |
590 | spin_unlock (&hcd_data_lock); | 611 | spin_unlock_irqrestore (&hcd_root_hub_lock, flags); |
591 | spin_unlock (&urb->lock); | 612 | return retval; |
592 | |||
593 | /* local irqs are always blocked in completions */ | ||
594 | if (length > 0) | ||
595 | usb_hcd_giveback_urb (hcd, urb, NULL); | ||
596 | local_irq_restore (flags); | ||
597 | } | 613 | } |
598 | 614 | ||
599 | /*-------------------------------------------------------------------------*/ | ||
600 | |||
601 | static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) | 615 | static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) |
602 | { | 616 | { |
603 | if (usb_pipeint (urb->pipe)) { | 617 | if (usb_pipeint (urb->pipe)) |
604 | int retval; | 618 | return rh_queue_status (hcd, urb); |
605 | unsigned long flags; | ||
606 | |||
607 | spin_lock_irqsave (&hcd_data_lock, flags); | ||
608 | retval = rh_status_urb (hcd, urb); | ||
609 | spin_unlock_irqrestore (&hcd_data_lock, flags); | ||
610 | return retval; | ||
611 | } | ||
612 | if (usb_pipecontrol (urb->pipe)) | 619 | if (usb_pipecontrol (urb->pipe)) |
613 | return rh_call_control (hcd, urb); | 620 | return rh_call_control (hcd, urb); |
614 | else | 621 | return -EINVAL; |
615 | return -EINVAL; | ||
616 | } | 622 | } |
617 | 623 | ||
618 | /*-------------------------------------------------------------------------*/ | 624 | /*-------------------------------------------------------------------------*/ |
619 | 625 | ||
626 | /* Asynchronous unlinks of root-hub control URBs are legal, but they | ||
627 | * don't do anything. Status URB unlinks must be made in process context | ||
628 | * with interrupts enabled. | ||
629 | */ | ||
620 | static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) | 630 | static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) |
621 | { | 631 | { |
622 | unsigned long flags; | 632 | if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */ |
623 | 633 | if (in_interrupt()) | |
624 | /* note: always a synchronous unlink */ | 634 | return 0; /* nothing to do */ |
625 | if ((unsigned long) urb == hcd->rh_timer.data) { | ||
626 | del_timer_sync (&hcd->rh_timer); | ||
627 | hcd->rh_timer.data = 0; | ||
628 | |||
629 | local_irq_save (flags); | ||
630 | urb->hcpriv = NULL; | ||
631 | usb_hcd_giveback_urb (hcd, urb, NULL); | ||
632 | local_irq_restore (flags); | ||
633 | 635 | ||
634 | } else if (usb_pipeendpoint(urb->pipe) == 0) { | ||
635 | spin_lock_irq(&urb->lock); /* from usb_kill_urb */ | 636 | spin_lock_irq(&urb->lock); /* from usb_kill_urb */ |
636 | ++urb->reject; | 637 | ++urb->reject; |
637 | spin_unlock_irq(&urb->lock); | 638 | spin_unlock_irq(&urb->lock); |
@@ -642,8 +643,22 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) | |||
642 | spin_lock_irq(&urb->lock); | 643 | spin_lock_irq(&urb->lock); |
643 | --urb->reject; | 644 | --urb->reject; |
644 | spin_unlock_irq(&urb->lock); | 645 | spin_unlock_irq(&urb->lock); |
645 | } else | 646 | |
646 | return -EINVAL; | 647 | } else { /* Status URB */ |
648 | if (!hcd->uses_new_polling) | ||
649 | del_timer_sync (&hcd->rh_timer); | ||
650 | local_irq_disable (); | ||
651 | spin_lock (&hcd_root_hub_lock); | ||
652 | if (urb == hcd->status_urb) { | ||
653 | hcd->status_urb = NULL; | ||
654 | urb->hcpriv = NULL; | ||
655 | } else | ||
656 | urb = NULL; /* wasn't fully queued */ | ||
657 | spin_unlock (&hcd_root_hub_lock); | ||
658 | if (urb) | ||
659 | usb_hcd_giveback_urb (hcd, urb, NULL); | ||
660 | local_irq_enable (); | ||
661 | } | ||
647 | 662 | ||
648 | return 0; | 663 | return 0; |
649 | } | 664 | } |
@@ -885,6 +900,16 @@ int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) | |||
885 | } | 900 | } |
886 | EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub); | 901 | EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub); |
887 | 902 | ||
903 | void usb_enable_root_hub_irq (struct usb_bus *bus) | ||
904 | { | ||
905 | struct usb_hcd *hcd; | ||
906 | |||
907 | hcd = container_of (bus, struct usb_hcd, self); | ||
908 | if (hcd->driver->hub_irq_enable && !hcd->poll_rh && | ||
909 | hcd->state != HC_STATE_HALT) | ||
910 | hcd->driver->hub_irq_enable (hcd); | ||
911 | } | ||
912 | |||
888 | 913 | ||
889 | /*-------------------------------------------------------------------------*/ | 914 | /*-------------------------------------------------------------------------*/ |
890 | 915 | ||
@@ -1348,7 +1373,8 @@ hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) | |||
1348 | 1373 | ||
1349 | hcd = udev->bus->hcpriv; | 1374 | hcd = udev->bus->hcpriv; |
1350 | 1375 | ||
1351 | WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); | 1376 | WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && |
1377 | udev->state != USB_STATE_NOTATTACHED); | ||
1352 | 1378 | ||
1353 | local_irq_disable (); | 1379 | local_irq_disable (); |
1354 | 1380 | ||
@@ -1612,6 +1638,8 @@ void usb_hc_died (struct usb_hcd *hcd) | |||
1612 | 1638 | ||
1613 | spin_lock_irqsave (&hcd_root_hub_lock, flags); | 1639 | spin_lock_irqsave (&hcd_root_hub_lock, flags); |
1614 | if (hcd->rh_registered) { | 1640 | if (hcd->rh_registered) { |
1641 | hcd->poll_rh = 0; | ||
1642 | del_timer(&hcd->rh_timer); | ||
1615 | 1643 | ||
1616 | /* make khubd clean up old urbs and devices */ | 1644 | /* make khubd clean up old urbs and devices */ |
1617 | usb_set_device_state (hcd->self.root_hub, | 1645 | usb_set_device_state (hcd->self.root_hub, |
@@ -1665,6 +1693,8 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, | |||
1665 | hcd->self.bus_name = bus_name; | 1693 | hcd->self.bus_name = bus_name; |
1666 | 1694 | ||
1667 | init_timer(&hcd->rh_timer); | 1695 | init_timer(&hcd->rh_timer); |
1696 | hcd->rh_timer.function = rh_timer_func; | ||
1697 | hcd->rh_timer.data = (unsigned long) hcd; | ||
1668 | 1698 | ||
1669 | hcd->driver = driver; | 1699 | hcd->driver = driver; |
1670 | hcd->product_desc = (driver->product_desc) ? driver->product_desc : | 1700 | hcd->product_desc = (driver->product_desc) ? driver->product_desc : |
@@ -1748,6 +1778,8 @@ int usb_add_hcd(struct usb_hcd *hcd, | |||
1748 | goto err3; | 1778 | goto err3; |
1749 | } | 1779 | } |
1750 | 1780 | ||
1781 | if (hcd->uses_new_polling && hcd->poll_rh) | ||
1782 | usb_hcd_poll_rh_status(hcd); | ||
1751 | return retval; | 1783 | return retval; |
1752 | 1784 | ||
1753 | err3: | 1785 | err3: |
@@ -1782,6 +1814,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) | |||
1782 | spin_unlock_irq (&hcd_root_hub_lock); | 1814 | spin_unlock_irq (&hcd_root_hub_lock); |
1783 | usb_disconnect(&hcd->self.root_hub); | 1815 | usb_disconnect(&hcd->self.root_hub); |
1784 | 1816 | ||
1817 | hcd->poll_rh = 0; | ||
1818 | del_timer_sync(&hcd->rh_timer); | ||
1819 | |||
1785 | hcd->driver->stop(hcd); | 1820 | hcd->driver->stop(hcd); |
1786 | hcd->state = HC_STATE_HALT; | 1821 | hcd->state = HC_STATE_HALT; |
1787 | 1822 | ||
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 */ | |||
65 | const char *product_desc; /* product/vendor string */ | 65 | const char *product_desc; /* product/vendor string */ |
66 | char irq_descr[24]; /* driver + bus # */ | 66 | char irq_descr[24]; /* driver + bus # */ |
67 | 67 | ||
68 | struct timer_list rh_timer; /* drives root hub */ | 68 | struct timer_list rh_timer; /* drives root-hub polling */ |
69 | struct urb *status_urb; /* the current status urb */ | ||
69 | 70 | ||
70 | /* | 71 | /* |
71 | * hardware info/state | 72 | * hardware info/state |
@@ -76,6 +77,12 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ | |||
76 | unsigned remote_wakeup:1;/* sw should use wakeup? */ | 77 | unsigned remote_wakeup:1;/* sw should use wakeup? */ |
77 | unsigned rh_registered:1;/* is root hub registered? */ | 78 | unsigned rh_registered:1;/* is root hub registered? */ |
78 | 79 | ||
80 | /* The next flag is a stopgap, to be removed when all the HCDs | ||
81 | * support the new root-hub polling mechanism. */ | ||
82 | unsigned uses_new_polling:1; | ||
83 | unsigned poll_rh:1; /* poll for rh status? */ | ||
84 | unsigned poll_pending:1; /* status has changed? */ | ||
85 | |||
79 | int irq; /* irq allocated */ | 86 | int irq; /* irq allocated */ |
80 | void __iomem *regs; /* device memory/io */ | 87 | void __iomem *regs; /* device memory/io */ |
81 | u64 rsrc_start; /* memory/io resource start */ | 88 | u64 rsrc_start; /* memory/io resource start */ |
@@ -207,6 +214,8 @@ struct hc_driver { | |||
207 | int (*hub_suspend)(struct usb_hcd *); | 214 | int (*hub_suspend)(struct usb_hcd *); |
208 | int (*hub_resume)(struct usb_hcd *); | 215 | int (*hub_resume)(struct usb_hcd *); |
209 | int (*start_port_reset)(struct usb_hcd *, unsigned port_num); | 216 | int (*start_port_reset)(struct usb_hcd *, unsigned port_num); |
217 | void (*hub_irq_enable)(struct usb_hcd *); | ||
218 | /* Needed only if port-change IRQs are level-triggered */ | ||
210 | }; | 219 | }; |
211 | 220 | ||
212 | extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); | 221 | 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, | |||
243 | 252 | ||
244 | /* generic bus glue, needed for host controllers that don't use PCI */ | 253 | /* generic bus glue, needed for host controllers that don't use PCI */ |
245 | extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); | 254 | extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); |
255 | |||
246 | extern void usb_hc_died (struct usb_hcd *hcd); | 256 | extern void usb_hc_died (struct usb_hcd *hcd); |
257 | extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); | ||
247 | 258 | ||
248 | /* -------------------------------------------------------------------------- */ | 259 | /* -------------------------------------------------------------------------- */ |
249 | 260 | ||
@@ -360,6 +371,8 @@ extern wait_queue_head_t usb_kill_urb_queue; | |||
360 | extern struct usb_bus *usb_bus_get (struct usb_bus *bus); | 371 | extern struct usb_bus *usb_bus_get (struct usb_bus *bus); |
361 | extern void usb_bus_put (struct usb_bus *bus); | 372 | extern void usb_bus_put (struct usb_bus *bus); |
362 | 373 | ||
374 | extern void usb_enable_root_hub_irq (struct usb_bus *bus); | ||
375 | |||
363 | extern int usb_find_interface_driver (struct usb_device *dev, | 376 | extern int usb_find_interface_driver (struct usb_device *dev, |
364 | struct usb_interface *interface); | 377 | struct usb_interface *interface); |
365 | 378 | ||
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) | |||
2787 | 2787 | ||
2788 | hub->activating = 0; | 2788 | hub->activating = 0; |
2789 | 2789 | ||
2790 | /* If this is a root hub, tell the HCD it's okay to | ||
2791 | * re-enable port-change interrupts now. */ | ||
2792 | if (!hdev->parent) | ||
2793 | usb_enable_root_hub_irq(hdev->bus); | ||
2794 | |||
2790 | loop: | 2795 | loop: |
2791 | usb_unlock_device(hdev); | 2796 | usb_unlock_device(hdev); |
2792 | usb_put_intf(intf); | 2797 | usb_put_intf(intf); |