|
@@ -510,7 +510,7 @@ static int disable_periodic (struct ehci_hcd *ehci)
|
|
|
ehci_writel(ehci, cmd, &ehci->regs->command);
|
|
|
/* posted write ... */
|
|
|
|
|
|
- free_cached_itd_list(ehci);
|
|
|
+ free_cached_lists(ehci);
|
|
|
|
|
|
ehci->next_uframe = -1;
|
|
|
return 0;
|
|
@@ -2139,13 +2139,27 @@ sitd_complete (
|
|
|
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
|
|
}
|
|
|
iso_stream_put (ehci, stream);
|
|
|
- /* OK to recycle this SITD now that its completion callback ran. */
|
|
|
+
|
|
|
done:
|
|
|
sitd->urb = NULL;
|
|
|
- sitd->stream = NULL;
|
|
|
- list_move(&sitd->sitd_list, &stream->free_list);
|
|
|
- iso_stream_put(ehci, stream);
|
|
|
-
|
|
|
+ if (ehci->clock_frame != sitd->frame) {
|
|
|
+ /* OK to recycle this SITD now. */
|
|
|
+ sitd->stream = NULL;
|
|
|
+ list_move(&sitd->sitd_list, &stream->free_list);
|
|
|
+ iso_stream_put(ehci, stream);
|
|
|
+ } else {
|
|
|
+ /* HW might remember this SITD, so we can't recycle it yet.
|
|
|
+ * Move it to a safe place until a new frame starts.
|
|
|
+ */
|
|
|
+ list_move(&sitd->sitd_list, &ehci->cached_sitd_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;
|
|
|
}
|
|
|
|
|
@@ -2211,9 +2225,10 @@ done:
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
-static void free_cached_itd_list(struct ehci_hcd *ehci)
|
|
|
+static void free_cached_lists(struct ehci_hcd *ehci)
|
|
|
{
|
|
|
struct ehci_itd *itd, *n;
|
|
|
+ struct ehci_sitd *sitd, *sn;
|
|
|
|
|
|
list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
|
|
|
struct ehci_iso_stream *stream = itd->stream;
|
|
@@ -2221,6 +2236,13 @@ static void free_cached_itd_list(struct ehci_hcd *ehci)
|
|
|
list_move(&itd->itd_list, &stream->free_list);
|
|
|
iso_stream_put(ehci, stream);
|
|
|
}
|
|
|
+
|
|
|
+ list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
|
|
|
+ struct ehci_iso_stream *stream = sitd->stream;
|
|
|
+ sitd->stream = NULL;
|
|
|
+ list_move(&sitd->sitd_list, &stream->free_list);
|
|
|
+ iso_stream_put(ehci, stream);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
@@ -2247,7 +2269,7 @@ scan_periodic (struct ehci_hcd *ehci)
|
|
|
clock_frame = -1;
|
|
|
}
|
|
|
if (ehci->clock_frame != clock_frame) {
|
|
|
- free_cached_itd_list(ehci);
|
|
|
+ free_cached_lists(ehci);
|
|
|
ehci->clock_frame = clock_frame;
|
|
|
}
|
|
|
clock %= mod;
|
|
@@ -2414,7 +2436,7 @@ restart:
|
|
|
clock = now;
|
|
|
clock_frame = clock >> 3;
|
|
|
if (ehci->clock_frame != clock_frame) {
|
|
|
- free_cached_itd_list(ehci);
|
|
|
+ free_cached_lists(ehci);
|
|
|
ehci->clock_frame = clock_frame;
|
|
|
}
|
|
|
} else {
|