diff options
Diffstat (limited to 'drivers/usb/host/ehci-sched.c')
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 66 |
1 files changed, 52 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a081ee65bde6..1d0b49e3f192 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) | |||
1004 | 1004 | ||
1005 | is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; | 1005 | is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; |
1006 | stream->bEndpointAddress &= 0x0f; | 1006 | stream->bEndpointAddress &= 0x0f; |
1007 | stream->ep->hcpriv = NULL; | 1007 | if (stream->ep) |
1008 | stream->ep->hcpriv = NULL; | ||
1008 | 1009 | ||
1009 | if (stream->rescheduled) { | 1010 | if (stream->rescheduled) { |
1010 | ehci_info (ehci, "ep%d%s-iso rescheduled " | 1011 | ehci_info (ehci, "ep%d%s-iso rescheduled " |
@@ -1535,7 +1536,7 @@ itd_link_urb ( | |||
1535 | struct ehci_itd, itd_list); | 1536 | struct ehci_itd, itd_list); |
1536 | list_move_tail (&itd->itd_list, &stream->td_list); | 1537 | list_move_tail (&itd->itd_list, &stream->td_list); |
1537 | itd->stream = iso_stream_get (stream); | 1538 | itd->stream = iso_stream_get (stream); |
1538 | itd->urb = usb_get_urb (urb); | 1539 | itd->urb = urb; |
1539 | itd_init (ehci, stream, itd); | 1540 | itd_init (ehci, stream, itd); |
1540 | } | 1541 | } |
1541 | 1542 | ||
@@ -1644,7 +1645,7 @@ itd_complete ( | |||
1644 | (void) disable_periodic(ehci); | 1645 | (void) disable_periodic(ehci); |
1645 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; | 1646 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; |
1646 | 1647 | ||
1647 | if (unlikely (list_empty (&stream->td_list))) { | 1648 | if (unlikely(list_is_singular(&stream->td_list))) { |
1648 | ehci_to_hcd(ehci)->self.bandwidth_allocated | 1649 | ehci_to_hcd(ehci)->self.bandwidth_allocated |
1649 | -= stream->bandwidth; | 1650 | -= stream->bandwidth; |
1650 | ehci_vdbg (ehci, | 1651 | ehci_vdbg (ehci, |
@@ -1653,14 +1654,27 @@ itd_complete ( | |||
1653 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); | 1654 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); |
1654 | } | 1655 | } |
1655 | iso_stream_put (ehci, stream); | 1656 | iso_stream_put (ehci, stream); |
1656 | /* OK to recycle this ITD now that its completion callback ran. */ | 1657 | |
1657 | done: | 1658 | done: |
1658 | usb_put_urb(urb); | ||
1659 | itd->urb = NULL; | 1659 | itd->urb = NULL; |
1660 | itd->stream = NULL; | 1660 | if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { |
1661 | list_move(&itd->itd_list, &stream->free_list); | 1661 | /* OK to recycle this ITD now. */ |
1662 | iso_stream_put(ehci, stream); | 1662 | itd->stream = NULL; |
1663 | 1663 | list_move(&itd->itd_list, &stream->free_list); | |
1664 | iso_stream_put(ehci, stream); | ||
1665 | } else { | ||
1666 | /* HW might remember this ITD, so we can't recycle it yet. | ||
1667 | * Move it to a safe place until a new frame starts. | ||
1668 | */ | ||
1669 | list_move(&itd->itd_list, &ehci->cached_itd_list); | ||
1670 | if (stream->refcount == 2) { | ||
1671 | /* If iso_stream_put() were called here, stream | ||
1672 | * would be freed. Instead, just prevent reuse. | ||
1673 | */ | ||
1674 | stream->ep->hcpriv = NULL; | ||
1675 | stream->ep = NULL; | ||
1676 | } | ||
1677 | } | ||
1664 | return retval; | 1678 | return retval; |
1665 | } | 1679 | } |
1666 | 1680 | ||
@@ -1934,7 +1948,7 @@ sitd_link_urb ( | |||
1934 | struct ehci_sitd, sitd_list); | 1948 | struct ehci_sitd, sitd_list); |
1935 | list_move_tail (&sitd->sitd_list, &stream->td_list); | 1949 | list_move_tail (&sitd->sitd_list, &stream->td_list); |
1936 | sitd->stream = iso_stream_get (stream); | 1950 | sitd->stream = iso_stream_get (stream); |
1937 | sitd->urb = usb_get_urb (urb); | 1951 | sitd->urb = urb; |
1938 | 1952 | ||
1939 | sitd_patch(ehci, stream, sitd, sched, packet); | 1953 | sitd_patch(ehci, stream, sitd, sched, packet); |
1940 | sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, | 1954 | sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, |
@@ -2019,7 +2033,7 @@ sitd_complete ( | |||
2019 | (void) disable_periodic(ehci); | 2033 | (void) disable_periodic(ehci); |
2020 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; | 2034 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; |
2021 | 2035 | ||
2022 | if (list_empty (&stream->td_list)) { | 2036 | if (list_is_singular(&stream->td_list)) { |
2023 | ehci_to_hcd(ehci)->self.bandwidth_allocated | 2037 | ehci_to_hcd(ehci)->self.bandwidth_allocated |
2024 | -= stream->bandwidth; | 2038 | -= stream->bandwidth; |
2025 | ehci_vdbg (ehci, | 2039 | ehci_vdbg (ehci, |
@@ -2030,7 +2044,6 @@ sitd_complete ( | |||
2030 | iso_stream_put (ehci, stream); | 2044 | iso_stream_put (ehci, stream); |
2031 | /* OK to recycle this SITD now that its completion callback ran. */ | 2045 | /* OK to recycle this SITD now that its completion callback ran. */ |
2032 | done: | 2046 | done: |
2033 | usb_put_urb(urb); | ||
2034 | sitd->urb = NULL; | 2047 | sitd->urb = NULL; |
2035 | sitd->stream = NULL; | 2048 | sitd->stream = NULL; |
2036 | list_move(&sitd->sitd_list, &stream->free_list); | 2049 | list_move(&sitd->sitd_list, &stream->free_list); |
@@ -2101,6 +2114,20 @@ done: | |||
2101 | 2114 | ||
2102 | /*-------------------------------------------------------------------------*/ | 2115 | /*-------------------------------------------------------------------------*/ |
2103 | 2116 | ||
2117 | static void free_cached_itd_list(struct ehci_hcd *ehci) | ||
2118 | { | ||
2119 | struct ehci_itd *itd, *n; | ||
2120 | |||
2121 | list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { | ||
2122 | struct ehci_iso_stream *stream = itd->stream; | ||
2123 | itd->stream = NULL; | ||
2124 | list_move(&itd->itd_list, &stream->free_list); | ||
2125 | iso_stream_put(ehci, stream); | ||
2126 | } | ||
2127 | } | ||
2128 | |||
2129 | /*-------------------------------------------------------------------------*/ | ||
2130 | |||
2104 | static void | 2131 | static void |
2105 | scan_periodic (struct ehci_hcd *ehci) | 2132 | scan_periodic (struct ehci_hcd *ehci) |
2106 | { | 2133 | { |
@@ -2115,10 +2142,17 @@ scan_periodic (struct ehci_hcd *ehci) | |||
2115 | * Touches as few pages as possible: cache-friendly. | 2142 | * Touches as few pages as possible: cache-friendly. |
2116 | */ | 2143 | */ |
2117 | now_uframe = ehci->next_uframe; | 2144 | now_uframe = ehci->next_uframe; |
2118 | if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) | 2145 | if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { |
2119 | clock = ehci_readl(ehci, &ehci->regs->frame_index); | 2146 | clock = ehci_readl(ehci, &ehci->regs->frame_index); |
2120 | else | 2147 | clock_frame = (clock >> 3) % ehci->periodic_size; |
2148 | } else { | ||
2121 | clock = now_uframe + mod - 1; | 2149 | clock = now_uframe + mod - 1; |
2150 | clock_frame = -1; | ||
2151 | } | ||
2152 | if (ehci->clock_frame != clock_frame) { | ||
2153 | free_cached_itd_list(ehci); | ||
2154 | ehci->clock_frame = clock_frame; | ||
2155 | } | ||
2122 | clock %= mod; | 2156 | clock %= mod; |
2123 | clock_frame = clock >> 3; | 2157 | clock_frame = clock >> 3; |
2124 | 2158 | ||
@@ -2277,6 +2311,10 @@ restart: | |||
2277 | /* rescan the rest of this frame, then ... */ | 2311 | /* rescan the rest of this frame, then ... */ |
2278 | clock = now; | 2312 | clock = now; |
2279 | clock_frame = clock >> 3; | 2313 | clock_frame = clock >> 3; |
2314 | if (ehci->clock_frame != clock_frame) { | ||
2315 | free_cached_itd_list(ehci); | ||
2316 | ehci->clock_frame = clock_frame; | ||
2317 | } | ||
2280 | } else { | 2318 | } else { |
2281 | now_uframe++; | 2319 | now_uframe++; |
2282 | now_uframe %= mod; | 2320 | now_uframe %= mod; |