|
@@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
|
|
|
|
|
|
is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
|
|
|
stream->bEndpointAddress &= 0x0f;
|
|
|
- stream->ep->hcpriv = NULL;
|
|
|
+ if (stream->ep)
|
|
|
+ stream->ep->hcpriv = NULL;
|
|
|
|
|
|
if (stream->rescheduled) {
|
|
|
ehci_info (ehci, "ep%d%s-iso rescheduled "
|
|
@@ -1653,14 +1654,28 @@ itd_complete (
|
|
|
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
|
|
}
|
|
|
iso_stream_put (ehci, stream);
|
|
|
- /* OK to recycle this ITD now that its completion callback ran. */
|
|
|
+
|
|
|
done:
|
|
|
usb_put_urb(urb);
|
|
|
itd->urb = NULL;
|
|
|
- itd->stream = NULL;
|
|
|
- list_move(&itd->itd_list, &stream->free_list);
|
|
|
- iso_stream_put(ehci, stream);
|
|
|
-
|
|
|
+ if (ehci->clock_frame != itd->frame || itd->index[7] != -1) {
|
|
|
+ /* OK to recycle this ITD now. */
|
|
|
+ itd->stream = NULL;
|
|
|
+ list_move(&itd->itd_list, &stream->free_list);
|
|
|
+ iso_stream_put(ehci, stream);
|
|
|
+ } else {
|
|
|
+ /* HW might remember this ITD, so we can't recycle it yet.
|
|
|
+ * Move it to a safe place until a new frame starts.
|
|
|
+ */
|
|
|
+ list_move(&itd->itd_list, &ehci->cached_itd_list);
|
|
|
+ if (stream->refcount == 2) {
|
|
|
+ /* If iso_stream_put() were called here, stream
|
|
|
+ * would be freed. Instead, just prevent reuse.
|
|
|
+ */
|
|
|
+ stream->ep->hcpriv = NULL;
|
|
|
+ stream->ep = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
@@ -2101,6 +2116,20 @@ done:
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
+static void free_cached_itd_list(struct ehci_hcd *ehci)
|
|
|
+{
|
|
|
+ struct ehci_itd *itd, *n;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
|
|
|
+ struct ehci_iso_stream *stream = itd->stream;
|
|
|
+ itd->stream = NULL;
|
|
|
+ list_move(&itd->itd_list, &stream->free_list);
|
|
|
+ iso_stream_put(ehci, stream);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+
|
|
|
static void
|
|
|
scan_periodic (struct ehci_hcd *ehci)
|
|
|
{
|
|
@@ -2115,10 +2144,17 @@ scan_periodic (struct ehci_hcd *ehci)
|
|
|
* Touches as few pages as possible: cache-friendly.
|
|
|
*/
|
|
|
now_uframe = ehci->next_uframe;
|
|
|
- if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
|
|
|
+ if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
|
|
|
clock = ehci_readl(ehci, &ehci->regs->frame_index);
|
|
|
- else
|
|
|
+ clock_frame = (clock >> 3) % ehci->periodic_size;
|
|
|
+ } else {
|
|
|
clock = now_uframe + mod - 1;
|
|
|
+ clock_frame = -1;
|
|
|
+ }
|
|
|
+ if (ehci->clock_frame != clock_frame) {
|
|
|
+ free_cached_itd_list(ehci);
|
|
|
+ ehci->clock_frame = clock_frame;
|
|
|
+ }
|
|
|
clock %= mod;
|
|
|
clock_frame = clock >> 3;
|
|
|
|
|
@@ -2277,6 +2313,10 @@ restart:
|
|
|
/* rescan the rest of this frame, then ... */
|
|
|
clock = now;
|
|
|
clock_frame = clock >> 3;
|
|
|
+ if (ehci->clock_frame != clock_frame) {
|
|
|
+ free_cached_itd_list(ehci);
|
|
|
+ ehci->clock_frame = clock_frame;
|
|
|
+ }
|
|
|
} else {
|
|
|
now_uframe++;
|
|
|
now_uframe %= mod;
|