* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
@ 2025-11-05 8:40 ` bugzilla-daemon
2025-11-05 8:48 ` bugzilla-daemon
` (26 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 8:40 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
Michał Pecio (michal.pecio@gmail.com) changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |michal.pecio@gmail.com
--- Comment #1 from Michał Pecio (michal.pecio@gmail.com) ---
Looks like you are right. I guess it means that synchronizing multiple
soundcards or playback/capture streams isn't working either? (If class drivers
implement that in the first place.)
I wonder if this could be the reason for some weird snd-usb-audio behavior
reported in the past (repeatedly unlinking and retrying URBs for unclear reason
when running full-duplex with very low latency). But it's just a wild guess.
Did you try the obvious?
+ if (!(urb->transfer_flags & URB_ISO_ASAP))
+ goto skip_start_over;
/* Calculate the start frame and put it in urb->start_frame. */
> instead for example in drivers/usb/host/xhci-ring.c, function
> xhci_queue_isoc_tx_prepare() the URB_ISO_ASAP flag is correctly used
Actually, this function looks OK but it isn't called correctly because this
shouldn't be limited to CFC *if* we are scheduling the first TD in a new "data
flow" (xHCI 4.11.2.5). But on modern HW it seems to be correct, and if you skip
the earlier urb->start_frame overwrite, chances are things may work for you.
> This causes issues with devices that require a specific frame interval (like
> professional audio interfaces, for example the Motu AVB line)
Does it mean there is hardware which breaks when polled at the rate specified
in its own endpoint descriptor? Would it still break if you insert zero-length
frames in the "forbidden" periods?
Using start_frame like that seems complicated and it's not feasible on Gen-1
controllers (no CFC). Per Documentation/driver-api/usb/URB drivers may specify
any service interval on per-URB basis, but this of course can't work with xHCI
and results in a warning. However, as suggested in check_interval() comments,
there could be a way of setting different polling interval at endpoint
creation. I see no reason for xHCI HW not to accept that, even Gen-1.
> https://linuxmusicians.com/viewtopic.php?p=179979#p179979
Hmm, using the sideband API to bypass kernel bugs seems even more extreme :)
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
2025-11-05 8:40 ` [Bug 220748] " bugzilla-daemon
@ 2025-11-05 8:48 ` bugzilla-daemon
2025-11-05 8:53 ` bugzilla-daemon
` (25 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 8:48 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #2 from Nicola Lunghi (nick83ola@gmail.com) ---
I haven't tried any changes as I'm not familiar with usb devices and wanted
some feedback first
> Did you try the obvious?
> + if (!(urb->transfer_flags & URB_ISO_ASAP))
> + goto skip_start_over;
> /* Calculate the start frame and put it in urb->start_frame. */
I can try this and see if it works yes. my problem is how to see/debug this?
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
2025-11-05 8:40 ` [Bug 220748] " bugzilla-daemon
2025-11-05 8:48 ` bugzilla-daemon
@ 2025-11-05 8:53 ` bugzilla-daemon
2025-11-05 9:06 ` bugzilla-daemon
` (24 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 8:53 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #3 from Nicola Lunghi (nick83ola@gmail.com) ---
sorry haven't finished responding. I can try to skip directly to
skip_start_over but the logic in ehci-sched seems more complicated than that
and not being familiar with usb protocol I don't know how for example debug
this. what could be a test setup to see the frames or record the frame that are
sent and the timing? with the ASAP scheduling if a driver try to send the
frames at a specific intervals the xhci driver 'breaks' all the syncronizations
in the way is this implemented.
the issue arised from a Motu AVB interface that expect a precise message rate
and without it it looses the channel synchronization as it expect a specific
rate for the messages.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (2 preceding siblings ...)
2025-11-05 8:53 ` bugzilla-daemon
@ 2025-11-05 9:06 ` bugzilla-daemon
2025-11-05 15:47 ` bugzilla-daemon
` (23 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 9:06 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #4 from Michał Pecio (michal.pecio@gmail.com) ---
As for debugging, you can obviously print anything to dmesg with
printk()/pr_info()/xhci_info(). But this may generate quite a noise on isoc
endpoints if you print in every interval.
If you mount debugfs and go to
/sys/kernel/debug/usb/xhci/<controller-pci-address>/devices/<device-slot-id>/
you will find epXX directories and trbs file in each. The file contains a
human-readable dump of the transfer ring written by the driver for given
endpoint, so you can inspect if the TRBs make sense. This only works while the
endpoint is enabled, i.e. if you stop streaming and driver switches to an empty
altsetting zero, the endpoint will disappear from debugfs.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (3 preceding siblings ...)
2025-11-05 9:06 ` bugzilla-daemon
@ 2025-11-05 15:47 ` bugzilla-daemon
2025-11-05 18:28 ` bugzilla-daemon
` (22 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 15:47 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #5 from Alan Stern (stern@rowland.harvard.edu) ---
For anyone who wants to know more about how isochronous scheduling is intended
to work in the Linux USB stack, see the description in the kerneldoc for
usb_submit_urb() in drivers/usb/core/urb.c.
In short, the start_frame field in struct urb is output-only. The host
controller driver doesn't pay any attention to it when an URB is submitted, but
the frame assigned to the URB's first isochronous packet gets stored in
start_frame before the URB completes. Furthermore, the URB_ISO_ASAP flag only
affects what happens to an URB that was submitted too late to keep the
endpoint's queue going (say, because interrupts were blocked for too long). If
the queue is just starting up, or the URB isn't already too late, URB_ISO_ASAP
is ignored.
Also, host controller drivers are not likely to pay attention to the interval
field. They will use the interval specified in the endpoint's descriptor.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (4 preceding siblings ...)
2025-11-05 15:47 ` bugzilla-daemon
@ 2025-11-05 18:28 ` bugzilla-daemon
2025-11-05 18:30 ` bugzilla-daemon
` (21 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 18:28 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #6 from Nicola Lunghi (nick83ola@gmail.com) ---
(In reply to Alan Stern from comment #5)
> For anyone who wants to know more about how isochronous scheduling is
> intended to work in the Linux USB stack, see the description in the
> kerneldoc for usb_submit_urb() in drivers/usb/core/urb.c.
>
> In short, the start_frame field in struct urb is output-only. The host
> controller driver doesn't pay any attention to it when an URB is submitted,
> but the frame assigned to the URB's first isochronous packet gets stored in
> start_frame before the URB completes. Furthermore, the URB_ISO_ASAP flag
> only affects what happens to an URB that was submitted too late to keep the
> endpoint's queue going (say, because interrupts were blocked for too long).
> If the queue is just starting up, or the URB isn't already too late,
> URB_ISO_ASAP is ignored.
>
> Also, host controller drivers are not likely to pay attention to the
> interval field. They will use the interval specified in the endpoint's
> descriptor.
Sorry Alan I don't understand your considerations. are you saying that this is
not valid? the documentation you pointed out states that
With few exceptions, USB device drivers should never access URB fields
* provided by usbcore or the HCD until its complete() is called.
* The exceptions relate to periodic transfer scheduling. For both
* interrupt and isochronous urbs, as part of successful URB submission
* urb->interval is modified to reflect the actual transfer period used
* (normally some power of two units). And for isochronous urbs,
* urb->start_frame is modified to reflect when the URB's transfers were
* scheduled to start.
I also found this documentation from intel
https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
pag 219 4.11.2.5 describes the start frame field.
I don't have time today I'll try to play with this during the we.
Can someone tell me if my considerations at the beginning are valid and the
behaviour that we have is missing the start_frame handling, or there's
something I don't see and the driver is actually doing the right thing?
Thanks,
Nicola
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (5 preceding siblings ...)
2025-11-05 18:28 ` bugzilla-daemon
@ 2025-11-05 18:30 ` bugzilla-daemon
2025-11-05 20:16 ` bugzilla-daemon
` (20 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 18:30 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #7 from Nicola Lunghi (nick83ola@gmail.com) ---
Sorry just rereading Michael comments they are useful! I'll take a look this we
thanks!
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (6 preceding siblings ...)
2025-11-05 18:30 ` bugzilla-daemon
@ 2025-11-05 20:16 ` bugzilla-daemon
2025-11-05 21:18 ` bugzilla-daemon
` (19 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 20:16 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #8 from Alan Stern (stern@rowland.harvard.edu) ---
Sorry, yes, urb->interval and urb->start_frame do get modified during
submission. Drivers are allowed to read those fields before the URB completes.
The documentation from Intel is irrelevant. It discusses the xHCI hardware,
but we're talking about the Linux software interface.
As for the considerations at the start of this bug report... xhci-hcd is
indeed supposed to ignore urb->start_frame during submission and overwrite it
with the actual starting (micro)frame number. And most of the time, xhci-hcd
is supposed to ignore URB_ISO_ASAP. But it's not supposed to ignore
URB_ISO_ASAP in cases where the URB was submitted too late. By "too late", I
mean that at the time the URB was submitted, the (micro)frame that it should
have been scheduled for -- one period after the last frame of the preceding URB
-- had already elapsed.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (7 preceding siblings ...)
2025-11-05 20:16 ` bugzilla-daemon
@ 2025-11-05 21:18 ` bugzilla-daemon
2025-11-06 3:52 ` bugzilla-daemon
` (18 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-05 21:18 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #9 from Michał Pecio (michal.pecio@gmail.com) ---
Thanks Alan, that's good to know, because Documentation/ is horribly out of
date and the kerneldoc doesn't really say that this field is ignored as input.
(Also, it says that if the whole URB is already expired, it would fail to
submit, but this behavior was apparently later changed to completing with
-EXDEV).
After a bit of digging, I see that urb->start_frame and URB_ISO_ASAP used to
actually work the intuitive way on UHCI in early 2000s, but then EHCI began to
act as if ASAP is always set, xHCI got the same behavior, UHCI was converted to
EHCI behavior, and ASAP got a new and different meaning.
So today it is unrealistic to expect host controller drivers to respect
start_frame, class drivers aren't setting it to anything meaningful, and the
patch I suggested earlier would likely break some class drivers unless a new
flag separate from URB_ISO_ASAP is added to force using the requested
start_frame.
I still wonder if providing means of overriding the polling interval wouldn't
be easier overall (less changes to snd-usb-audio required to support this new
HW).
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (8 preceding siblings ...)
2025-11-05 21:18 ` bugzilla-daemon
@ 2025-11-06 3:52 ` bugzilla-daemon
2025-11-06 8:27 ` bugzilla-daemon
` (17 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-06 3:52 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #10 from Alan Stern (stern@rowland.harvard.edu) ---
Figuring out how to handle isochronous transfers has been very difficult; it's
not obvious what scheme would work best. Compounding that difficulty is the
fact that different host controller drivers store their (micro)frame numbers
differently, so drivers will never know exactly what to expect.
You say that uhci-hcd used to work "the intuitive way", but in fact experience
has shown that no way is truly intuitive. Or to put it another differently,
what's intuitive for one person is not intuitive for another.
And don't forget that host controller drivers have to handle isochronous
endpoint queues that can be in three or four different possible states: empty,
partially or completely full, and partially full but totally expired. It's not
at all easy to come up with a set of flags and values that can handle all the
possibilities in ways that drivers will find convenient to use.
The matter of specifying polling intervals different from what the endpoint
descriptor says is yet another can of worms. For example, host controllers
generally can't change polling intervals in mid-stream. And some might reserve
the necessary bandwidth at the time when the alternate setting is installed,
before any URBs have been submitted -- how would one go about changing it from
the default value then?
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (9 preceding siblings ...)
2025-11-06 3:52 ` bugzilla-daemon
@ 2025-11-06 8:27 ` bugzilla-daemon
2025-11-06 8:35 ` bugzilla-daemon
` (16 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-06 8:27 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #11 from Michał Pecio (michal.pecio@gmail.com) ---
if (!qh->bandwidth_reserved) {
qh->period = urb->interval;
[...]
} else if (qh->period != urb->interval) {
return -EINVAL; /* Can't change the period */
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (10 preceding siblings ...)
2025-11-06 8:27 ` bugzilla-daemon
@ 2025-11-06 8:35 ` bugzilla-daemon
2025-11-06 15:03 ` bugzilla-daemon
` (15 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-06 8:35 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #12 from Michał Pecio (michal.pecio@gmail.com) ---
Oops, sorry for accidental post. This was meant to say that the solution to not
changing polling interval mid-stream already exists in UHCI and isn't very
hard.
As for bandwidth reservation, the problem could be ignored ("you are requesting
something really weird, deal with it"). Or in case of xHCI I think it could
also be solved - changing BW allocation involves similar steps as changing the
interval, so they could be done both (as long as there are no pending URBs).
BTW, purely for testing purposes, I suppose one could hack the kernel to modify
device's descriptor and reduce polling interval. If this helps, then there
would be grounds for wondering about how to implement interval switching more
cleanly.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (11 preceding siblings ...)
2025-11-06 8:35 ` bugzilla-daemon
@ 2025-11-06 15:03 ` bugzilla-daemon
2025-11-08 10:33 ` bugzilla-daemon
` (14 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-06 15:03 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #13 from Alan Stern (stern@rowland.harvard.edu) ---
The solution that's in uhci-hcd is specific to that driver. It won't
necessarily work with other drivers. Or rather, it might work but probably not
in the same way (since qh->bandwidth_reserved will be set and cleared at
different times in different host controller drivers). It wouldn't be
surprising if it doesn't work at all for some drivers (for example, if
bandwidth_reserved gets set when the altsetting is installed, before any URBs
can be submitted). The scheme simply is not usable as a general-purpose
approach.
Ignoring bandwidth reservation is the same as ignoring attempts to change the
polling interval, since reserved bandwidth = maxpacket size / polling interval
(roughly speaking). Besides, the USB spec does not allow USB-2 drivers to
ignore bandwidth reservation, and xHCI hardware enforces bandwidth reservation.
Now, if this turns out to be sufficiently important to enough people, we could
always add a new kernel API specifically meant for changing an endpoint's
interval. Maybe by adding a new ioctl to usbfs -- I don't know. This would
complicate the existing usbfs API for reporting endpoint descriptors: Should
the new interval be stored in the descriptor or should it be reported
separately somehow?
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (12 preceding siblings ...)
2025-11-06 15:03 ` bugzilla-daemon
@ 2025-11-08 10:33 ` bugzilla-daemon
2025-11-08 16:09 ` bugzilla-daemon
` (13 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-08 10:33 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #14 from Michał Pecio (michal.pecio@gmail.com) ---
I skimmed the linked forum thread and it seems the issue is not so much with
"needing a specific frame interval", but not tolerating any missing packets.
https://linuxmusicians.com/viewtopic.php?p=177688#p177688
So no-gaps scheduling is already the default *without* URB_ISO_ASAP and drivers
don't need to fill sequential frame IDs themselves if that's what they want.
Note that this stops working when the queue underruns - next URB is assumed to
be a new stream and will be scheduled into the future, not into the past.
If there are gaps despite the driver resubmitting on time, that may be a bug,
but a different one than originally assumed.
If drivers want to insert a gap to resynchronize after detected lost packets,
I'm afraid that's not supported in the current model. It would be technically
possible on CFC xHCI hosts and likely EHCI. A concern is how drivers would know
when frame ID overflows back to zero and how much into the future they can
schedule.
The urb->start_frame field is now output, so drivers can use it to confirm if
the URBs are scheduled sequentially as intended. At least with CFC, it appears
that these numbers should be consistent with reality, though I have never
tested it. Debugfs/tracing/printk may be used to check them against TRBs
written to the HW.
Non-CFC looks weird, maybe it could be improved somewhat but not sure. The old
xHCI 1.0 spec is limited in what it guarantees.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (13 preceding siblings ...)
2025-11-08 10:33 ` bugzilla-daemon
@ 2025-11-08 16:09 ` bugzilla-daemon
2025-11-10 10:23 ` bugzilla-daemon
` (12 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-08 16:09 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #15 from Alan Stern (stern@rowland.harvard.edu) ---
When the queue underruns is exactly when URB_ISO_ASAP is supposed to matter.
If the flag is set in the new URB then that URB should be scheduled for the
first slot in the future, leaving a logical gap in the queue. If the flag is
clear then the new URB is supposed to be scheduled for the slot that follows
the preceding URB, which means that some of its packets will never be sent
because their slots have already expired. This will still leave a physical gap
in the queue, of course -- no way to avoid that -- but it will maintain the
logical alignment of URBs and frames.
Thus by submitting all URBs with URB_ISO_ASAP clear, drivers can help ensure
that the queue remains synchronized to within the limits imposed by the host
controller driver. If xhci-hcd doesn't behave this way then it should be
changed.
As for inserting a gap to resynchronize after detected lost packets, it's
simple enough for drivers to submit isochronous URBs with some of the packet
lengths set to 0. Figuring out how many packets should have length 0 will be
tricky, but that can be adjusted after the URBs complete, by checking their
starting frame numbers.
I believe drivers can safely assume that the 8 low-order bits of the frame
number will be valid (11 bits for microframe numbers), and that they are
allowed to submit URBs up to 50 ms in the future. The kerneldoc says 10 - 200
ms, but it's being conservative. When a driver wants to keep latency to a
minimum, it won't go more than 1 or 2 ms into the future, anyway.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (14 preceding siblings ...)
2025-11-08 16:09 ` bugzilla-daemon
@ 2025-11-10 10:23 ` bugzilla-daemon
2025-11-10 10:42 ` bugzilla-daemon
` (11 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-10 10:23 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #16 from Michał Pecio (michal.pecio@gmail.com) ---
(In reply to Alan Stern from comment #15)
> When the queue underruns is exactly when URB_ISO_ASAP is supposed to matter.
> If the flag is set in the new URB then that URB should be scheduled for the
> first slot in the future, leaving a logical gap in the queue. If the flag
> is clear then the new URB is supposed to be scheduled for the slot that
> follows the preceding URB
I meant cases when the queue empties completely and there is no preceding URB.
But I see that there is a subtle difference in EHCI - it still considers a new
submission to be continuation of the earlier stream if earlier URBs have been
given back but their completion callbacks are still running. We can do it too.
But the real bug is xhci_get_isoc_frame_id(). This should convert
urb->start_frame and packet index into frame_id to be passed to HW, but it also
reschedules URBs which it doesn't like (and does some nonsense if the queue
gets too long).
This happens silently under normal conditions and the warnings at the end of
this function are bogus, almost impossible to trigger. So that's why no one
noticed. The only indicator is urb->start_frame discontinuity, but it seems
that class drivers trust HCDs and aren't validating that.
I don't know if that's the cause of those Motu issues, but it's clearly
involved in the other problem with low-latency duplex operation on CFC
controllers which was reported last year and which I can repro on my HW.
The above applies to xHCI 1.1 controllers (typically USB 3.1 and newer). On
older ones urb->start_frame returned to drivers is kinda bogus and everything
goes ASAP. Which actually seems to work better in most cases than the buggy CFC
stuff, but gaps due to late submission remain undetected.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (15 preceding siblings ...)
2025-11-10 10:23 ` bugzilla-daemon
@ 2025-11-10 10:42 ` bugzilla-daemon
2026-05-04 23:53 ` bugzilla-daemon
` (10 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2025-11-10 10:42 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #17 from Michał Pecio (michal.pecio@gmail.com) ---
Created attachment 308930
--> https://bugzilla.kernel.org/attachment.cgi?id=308930&action=edit
schedule isoc packets sequentially on CFC
This is a test patch which knocks out the problematic code and adds some
debugging on CFC controllers - interrupts are enabled on *all* packets, even if
the URB isn't done yet, and for each packet we check if its completion is
handled in the expected microframe. This may show false positives (the packet
moved as intended, but IRQ is handled later) but it would also catch cases when
HW executes (or reports execution) of a packet later than expected.
For example, it warns about every single packet for full speed devices behind
high speed hubs on my ASMedia controllers. Don't do it ;)
It needs a fair bit of testing to see if this approach is feasible and I still
see remaining issues in some tests (which may be due to xhci-hcd or
snd-usb-audio) but it seems to work generally better than unpatched driver.
May or may not help with Motu devices, I don't know what exactly goes wrong
with them. And again, for now, only CFC is expected to be scheduled accurately.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (16 preceding siblings ...)
2025-11-10 10:42 ` bugzilla-daemon
@ 2026-05-04 23:53 ` bugzilla-daemon
2026-05-04 23:54 ` bugzilla-daemon
` (9 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-04 23:53 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #18 from Nicola Lunghi (nick83ola@gmail.com) ---
hi thanks for all the suggestions!
I've finally found the time to work on this and I submitted some patches on the
linux usb kernel mailing list to address the issues on the usb audio stream.
https://lkml.org/lkml/2026/5/5/59
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (17 preceding siblings ...)
2026-05-04 23:53 ` bugzilla-daemon
@ 2026-05-04 23:54 ` bugzilla-daemon
2026-05-05 1:14 ` bugzilla-daemon
` (8 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-04 23:54 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #19 from Nicola Lunghi (nick83ola@gmail.com) ---
Sorry better link
https://lore.kernel.org/linux-usb/20260504233143.10242-2-nick83ola@gmail.com/T/#ma81ee2dadce78c645d1a4320e0f5f55b5b58ef8f
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (18 preceding siblings ...)
2026-05-04 23:54 ` bugzilla-daemon
@ 2026-05-05 1:14 ` bugzilla-daemon
2026-05-05 9:59 ` bugzilla-daemon
` (7 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-05 1:14 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
dylan_robinson@motu.com changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |dylan_robinson@motu.com
--- Comment #20 from dylan_robinson@motu.com ---
The issue, as it relates to the MOTU devices mentioned here, is that after
enabling a non-zero alternate setting with usb_set_interface(), once a transfer
is sent in a service interval (zero-length or otherwise), there must be a
transfer in every subsequent service interval. If, for any reason, a service
interval is missed, the alternate setting must be toggled back to zero and then
re-enabled, and the stream restarted.
MOTU's drivers on Windows and macOS take advantage of the ability on those
platforms to schedule the start frame of isochronous streams sufficiently far
into the future to ensure that enough URBs can be submitted before the first
URB completes.
On Linux, I have found that with snd-usb-audio, it is fairly typical for
streams to "stutter start." Because of the ASAP/SIA behavior discussed above,
when the first URB is submitted, it can sometimes complete before subsequent
URBs are enqueued. This creates a gap in the stream and causes the device
buffers to fall out of sync.
At one point, I investigated monitoring the start_frame in the completion
handler, but on non-CFC controllers it was not reliable (there were many
duplicated frame numbers or gaps that did not reflect reality).
Although more recent MOTU devices have improved buffer management and won't
fall out of sync due to missed intervals, the ability to specify a start frame
is still important for reasoning about the timing relationship between IN and
OUT streams, which helps guarantee consistent round-trip latency.
On non-Linux platforms, we have found that as long as a sufficient number of
transfers are enqueued before the first transfer starts, a missed service
interval, even on hosts that don't support CFC is quite rare.
> If drivers want to insert a gap to resynchronize after detected lost packets,
> I'm afraid that's not supported in the current model.
That would not be necessary for MOTU devices.
> I wonder if this could be the reason for some weird snd-usb-audio behavior
> reported in the past (repeatedly unlinking and retrying URBs for unclear
> reason when running full-duplex with very low latency). But it's just a wild
> guess.
This sounds like an issue I just started investigating. I am exploring the
possibility that when an isochronous endpoint is restarted quickly on a CFC
host (all URBs are killed and immediately then resubmitted), if the new URBs
are submitted before all TRBs have been dequeued, their start_frame values may
be set as if they were contiguous with the canceled TRBs. This might be causing
the entire batch to fail with missed service intervals. This then happens over
and over again.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (19 preceding siblings ...)
2026-05-05 1:14 ` bugzilla-daemon
@ 2026-05-05 9:59 ` bugzilla-daemon
2026-05-05 17:09 ` bugzilla-daemon
` (6 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-05 9:59 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #21 from Michał Pecio (michal.pecio@gmail.com) ---
Thanks for taking the time to explain.
(In reply to dylan_robinson from comment #20)
> The issue, as it relates to the MOTU devices mentioned here, is that after
> enabling a non-zero alternate setting with usb_set_interface(), once a
> transfer is sent in a service interval (zero-length or otherwise), there
> must be a transfer in every subsequent service interval. If, for any reason,
> a service interval is missed, the alternate setting must be toggled back to
> zero and then re-enabled, and the stream restarted.
That would be something for snd-usb-audio to do.
Does the same apply to missed service on IN endpoints?
Or IN endpoints with lost/corrupted packets?
And for the record, are you saying there is no hope to predict what the HW
expects to send and receive in the next interval and handle it right? Users say
that streams go out of sync but the HW continues to function in some manner.
> MOTU's drivers on Windows and macOS take advantage of the ability on those
> platforms to schedule the start frame of isochronous streams sufficiently
> far into the future to ensure that enough URBs can be submitted before the
> first URB completes.
>
> On Linux, I have found that with snd-usb-audio, it is fairly typical for
> streams to "stutter start." Because of the ASAP/SIA behavior discussed
> above, when the first URB is submitted, it can sometimes complete before
> subsequent URBs are enqueued. This creates a gap in the stream and causes
> the device buffers to fall out of sync.
I remember noticing this tendency to restart streams (unlink and resubmit URBs)
during startup with short buffers/low latency, presumably without going through
altsetting 0. So I can see how this can be a problem. IIRC it was at least
partly due xhci-hcd scheduling too far into the future. I will try to find some
time to look into it again.
> At one point, I investigated monitoring the start_frame in the completion
> handler, but on non-CFC controllers it was not reliable (there were many
> duplicated frame numbers or gaps that did not reflect reality).
These fields are filled by xhci-hcd so they may be bogus due to bugs.
For reliable information it's better to log "Frame ID" written to Isoch TDs and
MFINDEX register in process_isoc_td() (sensitive only to IRQ latency).
IIRC, on non-CFC even if things are initially right, they may fall out of sync
due to missed service, with the MSE event being the only sign of trouble.
> Although more recent MOTU devices have improved buffer management and won't
> fall out of sync due to missed intervals, the ability to specify a start
> frame is still important for reasoning about the timing relationship between
> IN and OUT streams, which helps guarantee consistent round-trip latency.
The "Linux way" appears to be guaranteeing two things to drivers:
- start_frame in completed URBs accurately reflects the time of execution
- streams are always continuous - even if an interval is missed, the next URB
frame reliably completes (or is missed) in the next interval
For continuity, class driver only needs to ensure that the URB is resubmitted
from its completion handler. If that happens several ms later, the URB is
expected to immediately "complete in the past" with EXDEV.
AFAIK that's how ehci-hcd does it, but xhci-hcd doesn't and maybe should.
Wouldn't this be enough to make IN/OUT synchronization work?
What's people's experience using MOTU HW and audio in general on ehci-hcd?
> > I wonder if this could be the reason for some weird snd-usb-audio behavior
> > reported in the past (repeatedly unlinking and retrying URBs for unclear
> > reason when running full-duplex with very low latency). But it's just a
> wild
> > guess.
>
> This sounds like an issue I just started investigating. I am exploring the
> possibility that when an isochronous endpoint is restarted quickly on a CFC
> host (all URBs are killed and immediately then resubmitted), if the new URBs
> are submitted before all TRBs have been dequeued, their start_frame values
> may be set as if they were contiguous with the canceled TRBs. This might be
> causing the entire batch to fail with missed service intervals. This then
> happens over and over again.
IIRC the repeated unlink/resubmit cycles during startup were due to
snd-usb-audio getting some "xrun" callback from ALSA core for some reason. Then
it would unlink everything and try again. I didn't know why it happens.
New URBs may be contiguous with past ones if you don't wait for them to
complete
after usb_unlink_urb(). OTOH, usb_kill_urb() is synchronous. Once all URBs are
killed synchronously or completed (including due to unlink) and the queue is
empty, then submitting a new URB should schedule it far enough into the future
to ensure no missed service. That's the theory.
See usb_submit_urb() and usb_unlink_urb() comments in drivers/usb/core/urb.c
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (20 preceding siblings ...)
2026-05-05 9:59 ` bugzilla-daemon
@ 2026-05-05 17:09 ` bugzilla-daemon
2026-05-05 17:10 ` bugzilla-daemon
` (5 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-05 17:09 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #22 from Nicola Lunghi (nick83ola@gmail.com) ---
(In reply to dylan_robinson from comment #20)
(In reply to Michał Pecio from comment #21)
Hi Dylan, Michal
I just want to report that with the patches I've submitted to the kernel the
issue with clock synchronization with the motu is gone and the interface is
super stable.
I've tested the interface running continuosly playing 12 audio channels
connected to an usb 2 hub (via another hub) while strees testing the usb
connection using fio on a separate usb disk on the same hub
Running kernel 7.1.0-rc2 with the patches applied, I can start and restart
playback repeatedly without any channel desync. usbmon confirms the frame
numbers are now perfectly sequential throughout — every
URB completion on EP 0x09 OUT comes back status 0 with start_frame
incrementing by exactly +8, no gaps, no EXDEV. I've set the motu at 16 samples
buffer / 16 samples host safety and is still rock solid.
I've implemented another patch to do a channel reinizialization if the driver
detect gap in the packets (that would break the synchronization) but on my
system I cannot trigger it. I think at this point is probably not needed. I'll
attach the extra patches to this post so you can have a look
Thanks,
Nicola
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (21 preceding siblings ...)
2026-05-05 17:09 ` bugzilla-daemon
@ 2026-05-05 17:10 ` bugzilla-daemon
2026-05-05 17:10 ` bugzilla-daemon
` (4 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-05 17:10 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #23 from Nicola Lunghi (nick83ola@gmail.com) ---
Created attachment 310037
--> https://bugzilla.kernel.org/attachment.cgi?id=310037&action=edit
first utility patch
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (22 preceding siblings ...)
2026-05-05 17:10 ` bugzilla-daemon
@ 2026-05-05 17:10 ` bugzilla-daemon
2026-05-05 17:13 ` bugzilla-daemon
` (3 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-05 17:10 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #24 from Nicola Lunghi (nick83ola@gmail.com) ---
Created attachment 310038
--> https://bugzilla.kernel.org/attachment.cgi?id=310038&action=edit
second patch to enable automatic link reestablishment
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (23 preceding siblings ...)
2026-05-05 17:10 ` bugzilla-daemon
@ 2026-05-05 17:13 ` bugzilla-daemon
2026-05-06 13:32 ` bugzilla-daemon
` (2 subsequent siblings)
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-05 17:13 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #25 from Nicola Lunghi (nick83ola@gmail.com) ---
Subject: [PATCH 0/2] ALSA: usb-audio: stream restart on isochronous gap for
MOTU AVB devices
---
MOTU AVB devices (USB ID 0x07fd:0x0005, including the 1248, 624, 8A, 8M,
16A and UltraLite AVB) cannot recover from a gap in the isochronous OUT
stream. Once a service interval is missed, the device's internal channel
demultiplexer loses synchronization and subsequent audio is routed to the
wrong output channels. The device remains in this state until the alternate
setting is toggled: set to 0, wait, then re-enable it.
This behaviour was confirmed by Dylan Robinson from MOTU in bugzilla
comment #20: "once a transfer is sent in a service interval (zero-length
or otherwise), there must be a transfer in every subsequent service
interval. If, for any reason, a service interval is missed, the alternate
setting must be toggled back to zero and then re-enabled, and the stream
restarted."
This series introduces QUIRK_FLAG_STREAM_RESTART_ON_GAP for devices with
this requirement. The snd-usb-audio driver already receives -EXDEV on
isochronous descriptors when xhci-hcd misses a service interval, providing
a reliable gap indication regardless of controller type. When the flag is
set, retire_playback_urb() checks for this condition and schedules a
workqueue item to perform the altsetting reset and signal an XRUN to
userspace. Normal ALSA recovery then calls snd_pcm_prepare(), which
executes the interface reset via snd_usb_endpoint_prepare() before
restarting URB submission.
Patch 1 adds a small helper in endpoint.c to encapsulate marking an
endpoint for a full interface reset, needed because struct snd_usb_iface_ref
is not visible outside that translation unit.
Patch 2 implements the gap detection and recovery mechanism, and registers
the quirk for USB ID 0x07fd:0x0005.
This series complements a separate xHCI patch series that addresses the
root causes of isochronous scheduling gaps on CFC-capable controllers.
That series reduces the frequency of gaps; this series handles recovery
when they do occur.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=220748
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (24 preceding siblings ...)
2026-05-05 17:13 ` bugzilla-daemon
@ 2026-05-06 13:32 ` bugzilla-daemon
2026-05-06 15:03 ` bugzilla-daemon
2026-05-11 19:15 ` bugzilla-daemon
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-06 13:32 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #26 from dylan_robinson@motu.com ---
(In reply to Michał Pecio from comment #21)
>> If, for any reason, a service interval is missed, the alternate setting must
>> be toggled back to zero and then re-enabled, and the stream restarted.
>
> Does the same apply to missed service on IN endpoints?
If the stream is interrupted, the alternate setting should be toggled for the
input streaming interface as well.
> Or IN endpoints with lost/corrupted packets?
This would only affect the ability to maintain implicit feedback
synchronization. As long as the endpoint is serviced during the service
interval, the device's buffer will not overrun. However, if a packet is lost or
corrupted, the driver cannot determine the payload size, which means it cannot
correctly derive the corresponding OUT transfer sizes.
> And for the record, are you saying there is no hope to predict what the HW
> expects to send and receive in the next interval and handle it right?
Correct, the hardware offers no mechanism to predict this. The device relies on
implicit feedback, so the correct OUT transfer size depends on the actual
length received on the corresponding IN endpoint.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (25 preceding siblings ...)
2026-05-06 13:32 ` bugzilla-daemon
@ 2026-05-06 15:03 ` bugzilla-daemon
2026-05-07 2:38 ` Alan Stern
2026-05-11 19:15 ` bugzilla-daemon
27 siblings, 1 reply; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-06 15:03 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
--- Comment #27 from dylan_robinson@motu.com ---
Regarding the start_frame, I want to advocate for treating urb->start_frame as
an input parameter when URB_ISO_ASAP is not set. In looking into porting our
drivers to Linux, we've found the current behavior challenging to accommodate.
As Alan mentioned, different host controllers have different representations of
frame numbers. Other platforms address this by only exposing a normalized,
frame (not microframe) index, while handling controller-specific conversions in
the host controller driver.
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-06 15:03 ` bugzilla-daemon
@ 2026-05-07 2:38 ` Alan Stern
2026-05-07 16:17 ` Dylan Robinson
0 siblings, 1 reply; 47+ messages in thread
From: Alan Stern @ 2026-05-07 2:38 UTC (permalink / raw)
To: dylan_robinson; +Cc: linux-usb
[Taking this side discussion off Bugzilla]
On Wed, May 06, 2026 at 03:03:44PM +0000, bugzilla-daemon@kernel.org wrote:
> https://bugzilla.kernel.org/show_bug.cgi?id=220748
>
> --- Comment #27 from dylan_robinson@motu.com ---
> Regarding the start_frame, I want to advocate for treating urb->start_frame as
> an input parameter when URB_ISO_ASAP is not set.
For one thing, that would be very impractical, as every driver using
isochronous transfers would then have to be modified.
For another, what's the point? In an ongoing stream, all this would
allow the driver to do would be to break the continuity of the stream.
At the start of a fresh stream, the driver could easily end up
requesting the HCD to put the first transaction in a (micro)frame that
the endpoint isn't scheduled to use or is beyond the end of the HCD's
scheduling window.
> In looking into porting our
> drivers to Linux, we've found the current behavior challenging to accommodate.
In what way? What is it you want to do that you find challenging?
> As Alan mentioned, different host controllers have different representations of
> frame numbers. Other platforms address this by only exposing a normalized,
> frame (not microframe) index, while handling controller-specific conversions in
> the host controller driver.
And thereby forcing every URB to contain an integral number of frames'
worth of transactions, at the risk of breaking the stream's continuity?
Alan Stern
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 2:38 ` Alan Stern
@ 2026-05-07 16:17 ` Dylan Robinson
2026-05-07 17:24 ` Alan Stern
2026-05-07 21:54 ` [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set Michal Pecio
0 siblings, 2 replies; 47+ messages in thread
From: Dylan Robinson @ 2026-05-07 16:17 UTC (permalink / raw)
To: Alan Stern; +Cc: linux-usb, mathias.nyman
On Wed, May 6, 2026 at 10:39 PM Alan Stern wrote:
> For one thing, that would be very impractical, as every driver using
> isochronous transfers would then have to be modified.
Maybe adding a new flag would make more sense then.
I was confused because the documentation regarding URB_ISO_ASAP
describes this behavior.
- For ISO there are two startup behaviors: Specified start_frame or ASAP.
- For ASAP set ``URB_ISO_ASAP`` in transfer_flags.
However, it looks like URB_ISO_ASAP actually means something
different: that the URB can be delayed. Is that correct?
> For another, what's the point?
The point would be to allow the driver to start a stream on a
particular frame relative to another stream, ensuring deterministic
latency between the two.
> In an ongoing stream, all this would
> allow the driver to do would be to break the continuity of the stream.
For an ongoing stream I would expect that submitting a start_frame
that is discontinuous and in the past would result in either a
submission failure or immediate completion with an error status. Such
an error would provide useful feedback to the driver because its
occurrence would mean the stream is no longer synchronized as the
driver expects.
> At the start of a fresh stream, the driver could easily end up
> requesting the HCD to put the first transaction in a (micro)frame that
> the endpoint isn't scheduled to use or is beyond the end of the HCD's
> scheduling window.
If the driver requests a specific start frame, it should be the
driver's responsibility to ensure it is a valid frame within the valid
scheduling window. If the requested start frame is invalid or outside
the host controller driver's valid scheduling window, the request
should fail. This would be more helpful than silently fixing it upon
submission.
> > In looking into porting our
> > drivers to Linux, we've found the current behavior challenging to accommodate.
> In what way? What is it you want to do that you find challenging?
In order to offer predictable round trip latency though the host, we
have to be able to correlate the frame time of transfers on the IN
pipe to transfers on the OUT pipe. Therefore, our drivers need to
determine the frame in which a transfer occurs.
Currently, with non-CFC host controllers, there is no way to determine
which frame a transfer occurs in. This is because without CFC, the
start_frame returned in the URB is only approximate.
> And thereby forcing every URB to contain an integral number of frames'
> worth of transactions, at the risk of breaking the stream's continuity?
AFAIK, xHCI doesn't allow scheduling transfers on arbitrary microframe
indexes, only on frame boundaries. URBs could still contain a
non-integral number of frames by putting the same start_frame in
multiple URBs (just like the TDs used by the hardware). Alternatively,
the initial frame-aligned URB could have a specific start_frame and
the subsequent ones could be submitted as start immediately after.
Ultimately, we care about the ability to start a stream on a
particular frame and maintain its continuity after starting.
Currently, there is no straightforward way to inform the xhci driver
whether a URB represents a stream start or continuation. This makes it
difficult to detect missed service intervals or other scheduling
discontinuities.
Dylan
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 16:17 ` Dylan Robinson
@ 2026-05-07 17:24 ` Alan Stern
2026-05-07 21:16 ` Dylan Robinson
2026-05-07 21:54 ` [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set Michal Pecio
1 sibling, 1 reply; 47+ messages in thread
From: Alan Stern @ 2026-05-07 17:24 UTC (permalink / raw)
To: Dylan Robinson; +Cc: linux-usb, mathias.nyman
On Thu, May 07, 2026 at 12:17:00PM -0400, Dylan Robinson wrote:
> On Wed, May 6, 2026 at 10:39 PM Alan Stern wrote:
> > For one thing, that would be very impractical, as every driver using
> > isochronous transfers would then have to be modified.
>
> Maybe adding a new flag would make more sense then.
>
> I was confused because the documentation regarding URB_ISO_ASAP
> describes this behavior.
>
> - For ISO there are two startup behaviors: Specified start_frame or ASAP.
> - For ASAP set ``URB_ISO_ASAP`` in transfer_flags.
>
> However, it looks like URB_ISO_ASAP actually means something
> different: that the URB can be delayed. Is that correct?
Sort of; a more accurate description would be that the URB is scheduled
for the next available unexpired time slot (as opposed to the next time
slot, regardless of whether it has expired or not).
Also, there is no "specified start_frame" behavior. The start_frame
field is output only. Some of the documentation is out of date. The
most up-to-date information about isochronous scheduling is in the
kerneldoc comments for usb_submit_urb() and usb_unlink_urb() in
drivers/usb/core/urb.c.
> > For another, what's the point?
>
> The point would be to allow the driver to start a stream on a
> particular frame relative to another stream, ensuring deterministic
> latency between the two.
I don't believe that's going to be possible. Or at least, not possible
until someone adds an API for getting an isochronous stream's current
schedule information. Until/unless that happens, the only way to know
what frames or microframes the stream will use is to submit an URB and
look at the start_frame and interval fields after it completes.
If you take that approach, submitting a few milliseconds worth of
innocuous data at the start (or ignoring it in the case of an input
stream), you should be able then to synchronize the data for your new
stream with the data in another one with little trouble. You won't be
able to change how they are scheduled, but you will know exactly how the
two streams' schedules are related.
> > In an ongoing stream, all this would
> > allow the driver to do would be to break the continuity of the stream.
>
> For an ongoing stream I would expect that submitting a start_frame
> that is discontinuous and in the past would result in either a
> submission failure or immediate completion with an error status. Such
> an error would provide useful feedback to the driver because its
> occurrence would mean the stream is no longer synchronized as the
> driver expects.
>
> > At the start of a fresh stream, the driver could easily end up
> > requesting the HCD to put the first transaction in a (micro)frame that
> > the endpoint isn't scheduled to use or is beyond the end of the HCD's
> > scheduling window.
>
> If the driver requests a specific start frame, it should be the
> driver's responsibility to ensure it is a valid frame within the valid
> scheduling window.
Not possible, because the driver doesn't know the scheduling window
or other parameters supported by the host controller driver.
> If the requested start frame is invalid or outside
> the host controller driver's valid scheduling window, the request
> should fail. This would be more helpful than silently fixing it upon
> submission.
Not if the driver doesn't know what the window is.
> > > In looking into porting our
> > > drivers to Linux, we've found the current behavior challenging to accommodate.
> > In what way? What is it you want to do that you find challenging?
>
> In order to offer predictable round trip latency though the host, we
> have to be able to correlate the frame time of transfers on the IN
> pipe to transfers on the OUT pipe. Therefore, our drivers need to
> determine the frame in which a transfer occurs.
>
> Currently, with non-CFC host controllers, there is no way to determine
> which frame a transfer occurs in. This is because without CFC, the
> start_frame returned in the URB is only approximate.
Is this a deficiency of xhci-hcd, or of some types of xHCI hardware?
Other kinds of host controllers don't have these problems.
> > And thereby forcing every URB to contain an integral number of frames'
> > worth of transactions, at the risk of breaking the stream's continuity?
>
> AFAIK, xHCI doesn't allow scheduling transfers on arbitrary microframe
> indexes, only on frame boundaries. URBs could still contain a
> non-integral number of frames by putting the same start_frame in
> multiple URBs (just like the TDs used by the hardware). Alternatively,
> the initial frame-aligned URB could have a specific start_frame and
> the subsequent ones could be submitted as start immediately after.
xhci-hcd is not the only host controller driver in the Linux USB stack.
What you're talking about would require changes to all of them (not to
mention the changes needed in the class drivers).
> Ultimately, we care about the ability to start a stream on a
> particular frame and maintain its continuity after starting.
>
> Currently, there is no straightforward way to inform the xhci driver
> whether a URB represents a stream start or continuation. This makes it
> difficult to detect missed service intervals or other scheduling
> discontinuities.
Informing xhci-hcd about this shouldn't be necessary; it should already
know. ehci-hcd does, for example. In short, if the submission occurs
before the last scheduled URB in a stream has completed, it is a
continuation. Otherwise it's a start.
What might be more difficult is distinguishing between a start and a
continuation after a gap. The boundary between those two is pretty
subjective. We deal with this by ignoring the possibility; it's always
treated as a new start.
Alan Stern
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 17:24 ` Alan Stern
@ 2026-05-07 21:16 ` Dylan Robinson
2026-05-08 3:02 ` Alan Stern
0 siblings, 1 reply; 47+ messages in thread
From: Dylan Robinson @ 2026-05-07 21:16 UTC (permalink / raw)
To: Alan Stern; +Cc: linux-usb, mathias.nyman
On Thu, May 7, 2026 at 1:24 PM Alan Stern wrote:
> On Thu, May 07, 2026 at 12:17:00PM -0400, Dylan Robinson wrote:
> > However, it looks like URB_ISO_ASAP actually means something
> > different: that the URB can be delayed. Is that correct?
>
> Sort of; a more accurate description would be that the URB is scheduled
> for the next available unexpired time slot (as opposed to the next time
> slot, regardless of whether it has expired or not).
Thanks for the clarification.
> Also, there is no "specified start_frame" behavior. The start_frame
> field is output only. Some of the documentation is out of date. The
> most up-to-date information about isochronous scheduling is in the
> kerneldoc comments for usb_submit_urb() and usb_unlink_urb() in
> drivers/usb/core/urb.c.
>
> > > For another, what's the point?
> >
> > The point would be to allow the driver to start a stream on a
> > particular frame relative to another stream, ensuring deterministic
> > latency between the two.
>
> I don't believe that's going to be possible. Or at least, not possible
> until someone adds an API for getting an isochronous stream's current
> schedule information. Until/unless that happens, the only way to know
> what frames or microframes the stream will use is to submit an URB and
> look at the start_frame and interval fields after it completes.
Adding "specified start_frame" behavior is exactly what I am
proposing. If it existed, the same driver submitting URBs for both
endpoints could track scheduling information internally, no additional
API needed. The device's endpoint descriptor determines the interval.
Is there additional scheduling information the HCD has that the driver
cannot observe?
FWIW, this is what our drivers for Windows and macOS do and they do
not require any such API. They just read the current frame and
schedule streams to start on a desirable future frame.
> If you take that approach, submitting a few milliseconds worth of
> innocuous data at the start (or ignoring it in the case of an input
> stream), you should be able then to synchronize the data for your new
> stream with the data in another one with little trouble. You won't be
> able to change how they are scheduled, but you will know exactly how the
> two streams' schedules are related.
I have tried this. It can work if the frame information returned after
submission is accurate. That said, considering endpoint feedback and
synchronization realities, an innocuous data transmission may not
always be available.
> > > In an ongoing stream, all this would
> > > allow the driver to do would be to break the continuity of the stream.
> >
> > For an ongoing stream I would expect that submitting a start_frame
> > that is discontinuous and in the past would result in either a
> > submission failure or immediate completion with an error status. Such
> > an error would provide useful feedback to the driver because its
> > occurrence would mean the stream is no longer synchronized as the
> > driver expects.
> >
> > > At the start of a fresh stream, the driver could easily end up
> > > requesting the HCD to put the first transaction in a (micro)frame that
> > > the endpoint isn't scheduled to use or is beyond the end of the HCD's
> > > scheduling window.
> >
> > If the driver requests a specific start frame, it should be the
> > driver's responsibility to ensure it is a valid frame within the valid
> > scheduling window.
>
> Not possible, because the driver doesn't know the scheduling window
> or other parameters supported by the host controller driver.
If a host controller cannot accommodate a requested start_frame, we
would consider that host controller incompatible with our device. If
we wanted to try working anyway, we could always fall back to
non-specified scheduling.
> > If the requested start frame is invalid or outside
> > the host controller driver's valid scheduling window, the request
> > should fail. This would be more helpful than silently fixing it upon
> > submission.
>
> Not if the driver doesn't know what the window is.
I don't think it matters if the driver knows what the window is,
because ultimately, the host controller can either support the
requested start_frame or not. All the driver needs to know is whether
its submission worked.
> > > > In looking into porting our
> > > > drivers to Linux, we've found the current behavior challenging to accommodate.
> > > In what way? What is it you want to do that you find challenging?
> >
> > In order to offer predictable round trip latency though the host, we
> > have to be able to correlate the frame time of transfers on the IN
> > pipe to transfers on the OUT pipe. Therefore, our drivers need to
> > determine the frame in which a transfer occurs.
> >
> > Currently, with non-CFC host controllers, there is no way to determine
> > which frame a transfer occurs in. This is because without CFC, the
> > start_frame returned in the URB is only approximate.
>
> Is this a deficiency of xhci-hcd, or of some types of xHCI hardware?
> Other kinds of host controllers don't have these problems.
I believe this is an xhci-hcd deficiency. The xhci-hcd computes a
start frame close to the IST, but only uses it if the controller
supports CFC and URB_ISO_ASAP is not set, otherwise it uses SIA. All
xHCI revisions support specifying a start_frame in the first TD, but
controllers without CFC require SIA = '1' for subsequent TDs.
> > > And thereby forcing every URB to contain an integral number of frames'
> > > worth of transactions, at the risk of breaking the stream's continuity?
> >
> > AFAIK, xHCI doesn't allow scheduling transfers on arbitrary microframe
> > indexes, only on frame boundaries. URBs could still contain a
> > non-integral number of frames by putting the same start_frame in
> > multiple URBs (just like the TDs used by the hardware). Alternatively,
> > the initial frame-aligned URB could have a specific start_frame and
> > the subsequent ones could be submitted as start immediately after.
>
> xhci-hcd is not the only host controller driver in the Linux USB stack.
> What you're talking about would require changes to all of them (not to
> mention the changes needed in the class drivers).
I understand this would touch more than xhci-hcd, and I'm not
proposing every HCD implement it. The minimal change would be for each
HCD to advertise whether it supports "specified start_frame," allowing
drivers to query that capability.
> > Ultimately, we care about the ability to start a stream on a
> > particular frame and maintain its continuity after starting.
> >
> > Currently, there is no straightforward way to inform the xhci driver
> > whether a URB represents a stream start or continuation. This makes it
> > difficult to detect missed service intervals or other scheduling
> > discontinuities.
>
> Informing xhci-hcd about this shouldn't be necessary; it should already
> know. ehci-hcd does, for example. In short, if the submission occurs
> before the last scheduled URB in a stream has completed, it is a
> continuation. Otherwise it's a start.
What ultimately matters is the continuity of the stream on the wire.
Submitting an URB before the completion of a previous one does not
guarantee it was submitted in time to prevent a discontinuity on the
wire, which we have observed.
> What might be more difficult is distinguishing between a start and a
> continuation after a gap. The boundary between those two is pretty
> subjective. We deal with this by ignoring the possibility; it's always
> treated as a new start.
Are you saying it's difficult to distinguish if the gap was intentional?
I was talking about having a way for a driver submitting URBs to tell
the host controller driver whether they are intended to be continuous,
so that an error can be observed if the submitter's indication
disagrees with the stream's current state.
Dylan
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 21:16 ` Dylan Robinson
@ 2026-05-08 3:02 ` Alan Stern
2026-05-08 17:20 ` Dylan Robinson
0 siblings, 1 reply; 47+ messages in thread
From: Alan Stern @ 2026-05-08 3:02 UTC (permalink / raw)
To: Dylan Robinson; +Cc: linux-usb, mathias.nyman
On Thu, May 07, 2026 at 05:16:31PM -0400, Dylan Robinson wrote:
> > > The point would be to allow the driver to start a stream on a
> > > particular frame relative to another stream, ensuring deterministic
> > > latency between the two.
> >
> > I don't believe that's going to be possible. Or at least, not possible
> > until someone adds an API for getting an isochronous stream's current
> > schedule information. Until/unless that happens, the only way to know
> > what frames or microframes the stream will use is to submit an URB and
> > look at the start_frame and interval fields after it completes.
>
> Adding "specified start_frame" behavior is exactly what I am
> proposing. If it existed, the same driver submitting URBs for both
> endpoints could track scheduling information internally, no additional
> API needed. The device's endpoint descriptor determines the interval.
> Is there additional scheduling information the HCD has that the driver
> cannot observe?
There certainly is!
Different HCDs handle periodic transfer scheduling in different ways.
In some cases the bandwidth (and corresponding (u)frames) may be
reserved when an alternate setting is installed; in others it doesn't
happen until the first URB of the new stream is submitted (in which
case the interval may be taken from the URB instead of from the
endpoint descriptor).
Either way, the HCD has to decide how to schedule the isochronous stream
based on the constraints of how much bandwidth is already dedicated to
other devices and endpoints, and what (u)frames are already in use. Of
course drivers have no way to know this, or to handle it if they did.
In addition, when the scheduling decision has been made, the HCD knows
not only the interval that has been allocated to the stream but also its
phase. That is, which (u)frames it will use, in cases where the
interval is larger than one (u)frame. This phase information is not
provided to the class driver other than implicitly through the
start_frame values of completed URBs.
In fact, IIRC the USB spec doesn't require isochronous transfers to
occur at uniformly spaced times. It only requires that there be one
transfer somewhere within each interval.
> FWIW, this is what our drivers for Windows and macOS do and they do
> not require any such API. They just read the current frame and
> schedule streams to start on a desirable future frame.
Let's say the interval is 8 frames, and the driver requests to start in
frame 71. If that isn't one of the frames in the allocated schedule,
presumably the URB submission will fail. What does the driver do then?
Try again with 72, then 73, 74, and so on, until it finds a value that
works? Make random choices?
What if the HCD only supports scheduling up to 256 ms in the future, but
the driver asks for a start frame that is 400 ms in the future?
What if the periodic schedule is already full and there is no bandwidth
remaining to schedule the new stream? How will the driver find out? By
getting a different error code from the URB submission?
> > If you take that approach, submitting a few milliseconds worth of
> > innocuous data at the start (or ignoring it in the case of an input
> > stream), you should be able then to synchronize the data for your new
> > stream with the data in another one with little trouble. You won't be
> > able to change how they are scheduled, but you will know exactly how the
> > two streams' schedules are related.
>
> I have tried this. It can work if the frame information returned after
> submission is accurate. That said, considering endpoint feedback and
> synchronization realities, an innocuous data transmission may not
> always be available.
All right. Something else will be needed for such situations. Right
now I don't know what the requirements are.
> If a host controller cannot accommodate a requested start_frame, we
> would consider that host controller incompatible with our device. If
> we wanted to try working anyway, we could always fall back to
> non-specified scheduling.
In view of my remarks above, doesn't this amount to saying that you
consider all host controllers incompatible with your device?
> I don't think it matters if the driver knows what the window is,
> because ultimately, the host controller can either support the
> requested start_frame or not. All the driver needs to know is whether
> its submission worked.
See above.
> > > Currently, with non-CFC host controllers, there is no way to determine
> > > which frame a transfer occurs in. This is because without CFC, the
> > > start_frame returned in the URB is only approximate.
> >
> > Is this a deficiency of xhci-hcd, or of some types of xHCI hardware?
> > Other kinds of host controllers don't have these problems.
>
> I believe this is an xhci-hcd deficiency. The xhci-hcd computes a
> start frame close to the IST, but only uses it if the controller
> supports CFC and URB_ISO_ASAP is not set, otherwise it uses SIA. All
> xHCI revisions support specifying a start_frame in the first TD, but
> controllers without CFC require SIA = '1' for subsequent TDs.
I'm not conversant with all the intricate details of xHCI or the
differences between various versions, so I'll take your word for it.
> I understand this would touch more than xhci-hcd, and I'm not
> proposing every HCD implement it. The minimal change would be for each
> HCD to advertise whether it supports "specified start_frame," allowing
> drivers to query that capability.
Then what will you do if your device lies behind a host controller that
doesn't support it? Fall back to unspecified scheduling? If that is
acceptable for such situations, why can't you use the same strategy with
xhci-hcd?
> > > Currently, there is no straightforward way to inform the xhci driver
> > > whether a URB represents a stream start or continuation. This makes it
> > > difficult to detect missed service intervals or other scheduling
> > > discontinuities.
> >
> > Informing xhci-hcd about this shouldn't be necessary; it should already
> > know. ehci-hcd does, for example. In short, if the submission occurs
> > before the last scheduled URB in a stream has completed, it is a
> > continuation. Otherwise it's a start.
>
> What ultimately matters is the continuity of the stream on the wire.
> Submitting an URB before the completion of a previous one does not
> guarantee it was submitted in time to prevent a discontinuity on the
> wire, which we have observed.
In fact, _nothing_ guarantees that URBs will be submitted in time to
prevent a gap in the schedule (except perhaps on some RT systems). The
CPU can get overcommitted at any time, and drivers have to be prepared
to handle unpredictable delays.
> > What might be more difficult is distinguishing between a start and a
> > continuation after a gap. The boundary between those two is pretty
> > subjective. We deal with this by ignoring the possibility; it's always
> > treated as a new start.
>
> Are you saying it's difficult to distinguish if the gap was intentional?
No, I'm saying that after a gap has occurred it's difficult to tell
whether the submitter of the next URB wants it to continue using the
same schedule as before and starting from the next upcoming time slot,
or to continue using the same schedule as before with some of the URB's
packets assigned to the expired time slots (so they never get sent), or
to start using a completely new schedule.
The current intention is to make this determination based on whether the
next URB is submitted before the last of the previous URBs completes and
on whether the next URB has the ISO_ASAP flag set.
> I was talking about having a way for a driver submitting URBs to tell
> the host controller driver whether they are intended to be continuous,
> so that an error can be observed if the submitter's indication
> disagrees with the stream's current state.
Aren't they almost always intended to be continuous? Particularly if
the new URB is submitted by an old URB's completion handler? About the
only unambiguous exception I can think of is the first URB submitted
after a Set-Interface request, where there's nothing for it to be
continuous with.
It's always possible to observe these errors after the fact (assuming
the start_frame values are reliable). I'm not sure what value there is
in observing them when the URB is submitted, however. What would the
driver do differently in these two cases?
Alan Stern
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-08 3:02 ` Alan Stern
@ 2026-05-08 17:20 ` Dylan Robinson
2026-05-09 1:25 ` Alan Stern
2026-05-09 22:12 ` Michal Pecio
0 siblings, 2 replies; 47+ messages in thread
From: Dylan Robinson @ 2026-05-08 17:20 UTC (permalink / raw)
To: Alan Stern; +Cc: linux-usb, mathias.nyman
On Thu, May 7, 2026 at 11:02 PM Alan Stern wrote:
> There certainly is!
>
> Different HCDs handle periodic transfer scheduling in different ways.
> In some cases the bandwidth (and corresponding (u)frames) may be
> reserved when an alternate setting is installed; in others it doesn't
> happen until the first URB of the new stream is submitted (in which
> case the interval may be taken from the URB instead of from the
> endpoint descriptor).
>
> Either way, the HCD has to decide how to schedule the isochronous stream
> based on the constraints of how much bandwidth is already dedicated to
> other devices and endpoints, and what (u)frames are already in use. Of
> course drivers have no way to know this, or to handle it if they did.
>
> In addition, when the scheduling decision has been made, the HCD knows
> not only the interval that has been allocated to the stream but also its
> phase. That is, which (u)frames it will use, in cases where the
> interval is larger than one (u)frame. This phase information is not
> provided to the class driver other than implicitly through the
> start_frame values of completed URBs.
>
> In fact, IIRC the USB spec doesn't require isochronous transfers to
> occur at uniformly spaced times. It only requires that there be one
> transfer somewhere within each interval.
Understood.
Our High Speed and SuperSpeed devices always use a bInterval of 1, so
they expect transfers in every microframe. I'm less familiar with the
behavior and expectations of endpoints that use larger polling
intervals.
Perhaps, rather than treating this as a "specified start frame", it
would be better to think of it as a "specified start service
interval".
If a driver requests a start frame that is not specifically available
within the periodic schedule, it seems reasonable for the HCD to place
the transfer at another valid position within that same service
interval. The actual start frame used could then still be reported
back through the completed URB, which can be inspected by the driver
if it needs to know the precise scheduling decision.
For endpoints with larger polling intervals, this "nearest valid start
frame" approach seems appropriate, since the device cannot expect
timing granularity finer than its polling interval anyway.
> > FWIW, this is what our drivers for Windows and macOS do and they do
> > not require any such API. They just read the current frame and
> > schedule streams to start on a desirable future frame.
>
> Let's say the interval is 8 frames, and the driver requests to start in
> frame 71. If that isn't one of the frames in the allocated schedule,
> presumably the URB submission will fail. What does the driver do then?
> Try again with 72, then 73, 74, and so on, until it finds a value that
> works? Make random choices?
See above.
> What if the HCD only supports scheduling up to 256 ms in the future, but
> the driver asks for a start frame that is 400 ms in the future?
On macOS and Windows the submission would fail with an error. That
seems appropriate.
For example, macOS has the following return status:
- kIOReturnIsoTooNew (Too far in the future)
- kIOReturnIsoTooOld (Too far in the past)
On Windows, the status of the URB is set to USBD_STATUS_BAD_START_FRAME.
Our drivers on those platforms request start frames that are about
20ms in the future. We haven't encountered controllers that reject
this.
> What if the periodic schedule is already full and there is no bandwidth
> remaining to schedule the new stream? How will the driver find out? By
> getting a different error code from the URB submission?
The driver needs to know if the stream cannot start. What happens currently?
> > > If you take that approach, submitting a few milliseconds worth of
> > > innocuous data at the start (or ignoring it in the case of an input
> > > stream), you should be able then to synchronize the data for your new
> > > stream with the data in another one with little trouble. You won't be
> > > able to change how they are scheduled, but you will know exactly how the
> > > two streams' schedules are related.
> >
> > I have tried this. It can work if the frame information returned after
> > submission is accurate. That said, considering endpoint feedback and
> > synchronization realities, an innocuous data transmission may not
> > always be available.
>
> All right. Something else will be needed for such situations. Right
> now I don't know what the requirements are.
The goal of our drivers is to provide low-latency, highly reliable
audio streaming. In general, the more visibility and control the
driver has over scheduling, the better the latency and reliability can
be.
The practical requirement is for the driver to be able to start a
stream at a predictable point in the future and to understand how that
stream is related to other active streams.
As long as the stream can be started reliably (without gaps), and the
resulting scheduling decision is observable to the driver, it is
generally possible to make the system work. However, reducing the
driver's visibility into or influence over scheduling typically means
trading away some combination of latency, synchronization accuracy, or
speed of recovery.
> > If a host controller cannot accommodate a requested start_frame, we
> > would consider that host controller incompatible with our device. If
> > we wanted to try working anyway, we could always fall back to
> > non-specified scheduling.
>
> In view of my remarks above, doesn't this amount to saying that you
> consider all host controllers incompatible with your device?
Our drivers already operate with "specified start_frame" behavior
across a variety of host controllers on both macOS and Windows, where
we consistently observe the requested start frame being honored in
practice.
That leads me to believe that our devices are compatible with the vast
majority of host controllers they are likely to encounter.
> Then what will you do if your device lies behind a host controller that
> doesn't support it? Fall back to unspecified scheduling? If that is
> acceptable for such situations, why can't you use the same strategy with
> xhci-hcd?
We can ultimately only provide functionality within the limits of what
the host platform and HCD are capable of supporting.
In general, the more precise and predictable the scheduling behavior
is, the lower latency and better overall performance the driver can
achieve. Falling back to unspecified scheduling may allow basic
functionality to work, but at the cost of increased buffering, reduced
synchronization accuracy and longer startup delay.
> > What ultimately matters is the continuity of the stream on the wire.
> > Submitting an URB before the completion of a previous one does not
> > guarantee it was submitted in time to prevent a discontinuity on the
> > wire, which we have observed.
>
> In fact, _nothing_ guarantees that URBs will be submitted in time to
> prevent a gap in the schedule (except perhaps on some RT systems). The
> CPU can get overcommitted at any time, and drivers have to be prepared
> to handle unpredictable delays.
Understood. We are well acquainted with that reality, and a
significant amount of our driver work goes into mitigating and
recovering from exactly those kinds of disruptions.
Ultimately, I am trying to understand how to achieve a similar level
of reliability on Linux to what we are currently able to achieve on
macOS and Windows platforms.
In the case of an unavoidable scheduling gap, what matters most is
that the driver has some way to detect that the discontinuity
occurred, so it can respond appropriately.
> Aren't they almost always intended to be continuous? Particularly if
> the new URB is submitted by an old URB's completion handler? About the
> only unambiguous exception I can think of is the first URB submitted
> after a Set-Interface request, where there's nothing for it to be
> continuous with.
Generally, yes. For audio streaming we always want the stream to be continuous.
That said, we do have some MIDI devices that intentionally use
discontinuous isochronous transfers in order to allow the driver to
schedule MIDI output with more precise timing.
> It's always possible to observe these errors after the fact (assuming
> the start_frame values are reliable). I'm not sure what value there is
> in observing them when the URB is submitted, however. What would the
> driver do differently in these two cases?
I agree the two cases are quite similar in terms of observability.
The main practical benefit of detecting this at submission time might
be the potential to reduce recovery latency. If the driver can
determine immediately that scheduling has failed, it may be able to
restart or resynchronize the stream sooner, which for an audio
interface, could translate to a smaller audible glitch.
Dylan
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-08 17:20 ` Dylan Robinson
@ 2026-05-09 1:25 ` Alan Stern
2026-05-09 22:12 ` Michal Pecio
1 sibling, 0 replies; 47+ messages in thread
From: Alan Stern @ 2026-05-09 1:25 UTC (permalink / raw)
To: Dylan Robinson; +Cc: linux-usb, mathias.nyman
On Fri, May 08, 2026 at 01:20:04PM -0400, Dylan Robinson wrote:
> Perhaps, rather than treating this as a "specified start frame", it
> would be better to think of it as a "specified start service
> interval".
>
> If a driver requests a start frame that is not specifically available
> within the periodic schedule, it seems reasonable for the HCD to place
> the transfer at another valid position within that same service
> interval. The actual start frame used could then still be reported
> back through the completed URB, which can be inspected by the driver
> if it needs to know the precise scheduling decision.
>
> For endpoints with larger polling intervals, this "nearest valid start
> frame" approach seems appropriate, since the device cannot expect
> timing granularity finer than its polling interval anyway.
That makes sense, and it should be doable. At least for ehci-hcd, and
likely for xhci-hcd as well.
> > What if the HCD only supports scheduling up to 256 ms in the future, but
> > the driver asks for a start frame that is 400 ms in the future?
>
> On macOS and Windows the submission would fail with an error. That
> seems appropriate.
>
> For example, macOS has the following return status:
> - kIOReturnIsoTooNew (Too far in the future)
> - kIOReturnIsoTooOld (Too far in the past)
>
> On Windows, the status of the URB is set to USBD_STATUS_BAD_START_FRAME.
We ought to be able to do something similar.
> > What if the periodic schedule is already full and there is no bandwidth
> > remaining to schedule the new stream? How will the driver find out? By
> > getting a different error code from the URB submission?
>
> The driver needs to know if the stream cannot start. What happens currently?
Submission fails with a -ENOSPC error code. On the other hand, if the
schedule is set up when the alternate setting is installed, and that
can't be done, then the usb_set_interface() call fails.
> We can ultimately only provide functionality within the limits of what
> the host platform and HCD are capable of supporting.
>
> In general, the more precise and predictable the scheduling behavior
> is, the lower latency and better overall performance the driver can
> achieve. Falling back to unspecified scheduling may allow basic
> functionality to work, but at the cost of increased buffering, reduced
> synchronization accuracy and longer startup delay.
All right, let's say what Mathias and Michal have to say about this.
Alan Stern
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-08 17:20 ` Dylan Robinson
2026-05-09 1:25 ` Alan Stern
@ 2026-05-09 22:12 ` Michal Pecio
2026-05-10 12:39 ` Dylan Robinson
1 sibling, 1 reply; 47+ messages in thread
From: Michal Pecio @ 2026-05-09 22:12 UTC (permalink / raw)
To: Dylan Robinson; +Cc: Alan Stern, linux-usb, mathias.nyman
On Fri, 8 May 2026 13:20:04 -0400, Dylan Robinson wrote:
> Our High Speed and SuperSpeed devices always use a bInterval of 1, so
> they expect transfers in every microframe. I'm less familiar with the
> behavior and expectations of endpoints that use larger polling
> intervals.
>
> Perhaps, rather than treating this as a "specified start frame", it
> would be better to think of it as a "specified start service
> interval".
>
> If a driver requests a start frame that is not specifically available
> within the periodic schedule, it seems reasonable for the HCD to place
> the transfer at another valid position within that same service
> interval. The actual start frame used could then still be reported
> back through the completed URB, which can be inspected by the driver
> if it needs to know the precise scheduling decision.
IDK if drivers ever need to know. They better not, because there is
no way to get "phase" information out of xHCI, besides timing IRQs.
There is also no way to set it. And no way to schedule a new stream
not on frame boundary. See xHCI 4.11.2.5.1, the rules are simple.
The urb->start_frame you get is the first (u)frame of target interval.
Requests from drivers would need to be rounded, ignored or rejected.
> > It's always possible to observe these errors after the fact
> > (assuming the start_frame values are reliable). I'm not sure what
> > value there is in observing them when the URB is submitted,
> > however. What would the driver do differently in these two cases?
>
> I agree the two cases are quite similar in terms of observability.
>
> The main practical benefit of detecting this at submission time might
> be the potential to reduce recovery latency. If the driver can
> determine immediately that scheduling has failed, it may be able to
> restart or resynchronize the stream sooner, which for an audio
> interface, could translate to a smaller audible glitch.
I believe gaps between URBs are not supposed to be happening at all
with proper resubmission, according to documented rules, so drivers
can detect gaps by watching for EXDEV status in frame_desc, if we
manage to bring xhci-hcd into compliance with the rules.
As for predicting EXDEV at submission, it's a gable. We can only be
100% sure whether it's too late or not when the interval completes
with Missed Service or Success.
Admittedly if it fails, the driver unfortunately only learns about
it after the end of the whole URB. Short URBs could be better here.
We can predict it by looking at the Isochronous Scheduling Threshold
reported by the HC, but it's a single number which may not accurately
reflect all cases (full-speed devices behind high-speed hubs are a
fascinating topic) and IME it's quite pessimistic on many chips - in
reality, they can do better than they claim.
So being conservative and failing submissions which missed the IST
might cause a driver to panic and reset the endpoint when no glitch
would actually occur. And of course delaying such URBs (as currently
done) has the known effects that this bug is about.
I wrote some experimental patches to debug and fix this back then,
but nobody was commenting on the bug so I dropped this topic because
I'm reluctant to submit significant changes without real testing.
I can rehash this stuff.
The most important question is if actual HW properly handles URBs
submitted too late. It would be nice to get testing on various xHCI
chips from varous vendors. If there are problems, it could prove
necessary to detect late submissions and complete such transfers as
EXDEV witout HW involvemnet. Yuck. Maybe it won't come to that.
Regards,
Michal
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-09 22:12 ` Michal Pecio
@ 2026-05-10 12:39 ` Dylan Robinson
2026-05-11 19:21 ` [RFT PATCH] xhci: fix frame id calculation for isoc transfer Mathias Nyman
0 siblings, 1 reply; 47+ messages in thread
From: Dylan Robinson @ 2026-05-10 12:39 UTC (permalink / raw)
To: Michal Pecio; +Cc: Alan Stern, linux-usb, mathias.nyman
On Sat, May 9, 2026 at 6:12 PM Michal Pecio wrote:
> IDK if drivers ever need to know. They better not, because there is
> no way to get "phase" information out of xHCI, besides timing IRQs.
> There is also no way to set it. And no way to schedule a new stream
> not on frame boundary. See xHCI 4.11.2.5.1, the rules are simple.
>
> The urb->start_frame you get is the first (u)frame of target interval.
This seems reasonable to me. Drivers should not expect timing
information with finer granularity than the endpoint service interval.
> Requests from drivers would need to be rounded, ignored or rejected.
For the first URB in a stream, I think rounding makes sense if the
requested start frame falls within the valid scheduling window, while
requests outside that window should be rejected.
For subsequent URBs submitted as continuations (and therefore able to
rely on -EXDEV to detect discontinuities), I think the requested start
frame can be ignored.
> I believe gaps between URBs are not supposed to be happening at all
> with proper resubmission, according to documented rules, so drivers
> can detect gaps by watching for EXDEV status in frame_desc, if we
> manage to bring xhci-hcd into compliance with the rules.
This sounds good.
> As for predicting EXDEV at submission, it's a gable. We can only be
> 100% sure whether it's too late or not when the interval completes
> with Missed Service or Success.
>
> Admittedly if it fails, the driver unfortunately only learns about
> it after the end of the whole URB. Short URBs could be better here.
>
> We can predict it by looking at the Isochronous Scheduling Threshold
> reported by the HC, but it's a single number which may not accurately
> reflect all cases (full-speed devices behind high-speed hubs are a
> fascinating topic) and IME it's quite pessimistic on many chips - in
> reality, they can do better than they claim.
>
> So being conservative and failing submissions which missed the IST
> might cause a driver to panic and reset the endpoint when no glitch
> would actually occur. And of course delaying such URBs (as currently
> done) has the known effects that this bug is about.
For an established stream, I agree with taking the optimistic approach
of attempting to append transfers that are only marginally past the
IST.
For a new stream with a requested start frame, however, I think the
HCD should, to the extent practical, validate that the request falls
within the valid scheduling window.
> I wrote some experimental patches to debug and fix this back then,
> but nobody was commenting on the bug so I dropped this topic because
> I'm reluctant to submit significant changes without real testing.
> I can rehash this stuff.
I am happy to help test in whatever way I can.
> The most important question is if actual HW properly handles URBs
> submitted too late. It would be nice to get testing on various xHCI
> chips from varous vendors. If there are problems, it could prove
> necessary to detect late submissions and complete such transfers as
> EXDEV witout HW involvemnet. Yuck. Maybe it won't come to that.
I would be interested to learn more about how this is handled in reality too.
I think drivers should, whenever possible, avoid submitting URBs too
close to the IST. A motivation for having a requested start frame is
that it allows transfers to be enqueued well in advance of when the
hardware will actually need them.
In our case, URBs are often submitted tens of milliseconds ahead of
time, while the transfer buffers themselves are serviced just before
the data is consumed by the host controller. For audio streaming, this
still allows low latency while avoiding the need to make submissions
close to the scheduling threshold.
In practice, we have found it less disruptive to occasionally miss
copying audio data in time than to lose continuity of the isochronous
stream itself and have to restart the endpoint.
Dylan
^ permalink raw reply [flat|nested] 47+ messages in thread
* [RFT PATCH] xhci: fix frame id calculation for isoc transfer
2026-05-10 12:39 ` Dylan Robinson
@ 2026-05-11 19:21 ` Mathias Nyman
2026-05-11 19:36 ` Mathias Nyman
0 siblings, 1 reply; 47+ messages in thread
From: Mathias Nyman @ 2026-05-11 19:21 UTC (permalink / raw)
To: dylan_robinson
Cc: linux-usb, mathias.nyman, michal.pecio, stern, Mathias Nyman
Always calculate estimated start frame, and set urb->start_frame
Fix valid frame window start and end calculation to match xhci
spec 4.11.2.5
Don't inclease frame id with 1 if a URB mod transfer is queued late.
Queue it with next expected frame ID but print a message if URB is
next expected frame id does not fir valid frame window range (URB mid
transfer is queued late)
This patch doesn't switch to URB_ISO_ASAP /SIA scheduling if URB is queued
late, not sure if that would help as it only moves the frame id glitch
problem forward, unless _every_ URB queued after a late URB is forced to
use SIA, which again would defeat the point of Frame ID use.
get rid of the annoying 'xep' and 'xdev' variables, xhci driver uses
ep and vdev naming everywhere else
Contains some FIXMEs, patch for initial testing purposes
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-mem.c | 1 +
drivers/usb/host/xhci-ring.c | 189 ++++++++++++++---------------------
drivers/usb/host/xhci.h | 7 +-
3 files changed, 80 insertions(+), 117 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 997fe90f54e5..58714e10773f 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1499,6 +1499,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
return -ENOMEM;
virt_dev->eps[ep_index].skip = false;
+ virt_dev->eps[ep_index].next_uframe = -1;
ep_ring = virt_dev->eps[ep_index].new_ring;
xhci_ring_init(xhci, ep_ring);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e47e644b296e..e63dc6920685 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3977,80 +3977,64 @@ static int xhci_ist_microframes(struct xhci_hcd *xhci)
}
/*
- * Calculates Frame ID field of the isochronous TRB identifies the
- * target frame that the Interval associated with this Isochronous
- * Transfer Descriptor will start on. Refer to 4.11.2.5 in 1.1 spec.
+ * Set the urb->start_frame of the URB.
*
- * Returns actual frame id on success, negative value on error.
+ * Returns microframe index of first TD
*/
-static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
- struct urb *urb, int index)
+static int xhci_get_isoc_start_frame(struct xhci_hcd *xhci, struct urb *urb,
+ struct xhci_virt_ep *ep)
{
- int start_frame, ist, ret = 0;
- int start_frame_id, end_frame_id, current_frame_id;
+ int curr_frame, start_uframe;
+ int win_start, win_end;
+ bool frame_unit;
+ bool in_range;
+ int uinterval;
+ u32 mfindex;
+ int ist;
- if (urb->dev->speed == USB_SPEED_LOW ||
- urb->dev->speed == USB_SPEED_FULL)
- start_frame = urb->start_frame + index * urb->interval;
- else
- start_frame = (urb->start_frame + index * urb->interval) >> 3;
+ /* check if urb uses frame units instead of microframes */
+ frame_unit = (urb->dev->speed == USB_SPEED_FULL ||
+ urb->dev->speed == USB_SPEED_LOW);
+
+ uinterval = urb->interval;
+ if (frame_unit)
+ uinterval *= 8;
+ /* get current microframe index and isoc sheduling threshold */
+ mfindex = readl(&xhci->run_regs->microframe_index);
ist = xhci_ist_microframes(xhci);
- /* Software shall not schedule an Isoch TD with a Frame ID value that
- * is less than the Start Frame ID or greater than the End Frame ID,
- * where:
- *
- * End Frame ID = (Current MFINDEX register value + 895 ms.) MOD 2048
- * Start Frame ID = (Current MFINDEX register value + IST + 1) MOD 2048
- *
- * Both the End Frame ID and Start Frame ID values are calculated
- * in microframes. When software determines the valid Frame ID value;
- * The End Frame ID value should be rounded down to the nearest Frame
- * boundary, and the Start Frame ID value should be rounded up to the
- * nearest Frame boundary.
- */
- current_frame_id = readl(&xhci->run_regs->microframe_index);
- start_frame_id = roundup(current_frame_id + ist + 1, 8);
- end_frame_id = rounddown(current_frame_id + 895 * 8, 8);
+ /* calculate valid frame window, in frame units, see xhci 4.11.2.5 */
+ curr_frame = MFINDEX_TO_FRAME(mfindex);
+ win_start = (curr_frame + DIV_ROUND_UP_POW2(ist, 8) + 1) % MAX_FRAMES;
+ win_end = (curr_frame + 895) % MAX_FRAMES;
- start_frame &= 0x7ff;
- start_frame_id = (start_frame_id >> 3) & 0x7ff;
- end_frame_id = (end_frame_id >> 3) & 0x7ff;
+ /* are we mid stream? is URB is queued inside the valid frame window */
+ // FIXME, used to check if !list_empty(&ep_ring->td_list)), is that reliable
+ if (ep->next_uframe >= 0) {
+ u32 frame = ep->next_uframe / 8;
- if (start_frame_id < end_frame_id) {
- if (start_frame > end_frame_id ||
- start_frame < start_frame_id)
- ret = -EINVAL;
- } else if (start_frame_id > end_frame_id) {
- if ((start_frame > end_frame_id &&
- start_frame < start_frame_id))
- ret = -EINVAL;
- } else {
- ret = -EINVAL;
- }
+ in_range = frame >= win_start && frame <= win_end;
+ /* valid frame window end wrapped around */
+ if (win_start > win_end)
+ in_range = frame >= win_start || frame <= win_end;
- if (index == 0) {
- if (ret == -EINVAL || start_frame == start_frame_id) {
- start_frame = start_frame_id + 1;
- if (urb->dev->speed == USB_SPEED_LOW ||
- urb->dev->speed == USB_SPEED_FULL)
- urb->start_frame = start_frame;
- else
- urb->start_frame = start_frame << 3;
- ret = 0;
- }
- }
+ start_uframe = ep->next_uframe;
- if (ret) {
- xhci_warn(xhci, "Frame ID %d (reg %d, index %d) beyond range (%d, %d)\n",
- start_frame, current_frame_id, index,
- start_frame_id, end_frame_id);
- xhci_warn(xhci, "Ignore frame ID field, use SIA bit instead\n");
- return ret;
+ if (!in_range)
+ xhci_warn(xhci, "Frame ID %d for Isoc URB %p is outside range %d-%d\n",
+ frame, urb, win_start, win_end);
+ } else {
+ /* align first URB to next interval boundary, or at last to full frame */
+ start_uframe = mfindex + ist + XHCI_CFC_DELAY;
+ start_uframe = roundup(start_uframe, 8);
+ start_uframe = roundup(start_uframe, uinterval);
}
- return start_frame;
+ /* set urb->start_frame */
+ urb->start_frame = frame_unit ? start_uframe / 8 : start_uframe;
+
+ return start_uframe;
}
/* Check if we should generate event interrupt for a TD in an isoc URB */
@@ -4089,10 +4073,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
u64 start_addr, addr;
int i, j;
bool more_trbs_coming;
- struct xhci_virt_ep *xep;
+ struct xhci_virt_ep *ep;
int frame_id;
+ int uinterval = urb->interval;
+ int start_uframe;
- xep = &xhci->devs[slot_id]->eps[ep_index];
+ ep = &xhci->devs[slot_id]->eps[ep_index];
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
ir = xhci->interrupters[0];
@@ -4106,12 +4092,17 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
start_cycle = ep_ring->cycle_state;
urb_priv = urb->hcpriv;
+
+ if (urb->dev->speed == USB_SPEED_FULL || urb->dev->speed == USB_SPEED_LOW)
+ uinterval = urb->interval * 8;
+
+ start_uframe = xhci_get_isoc_start_frame(xhci, urb, ep);
+
/* Queue the TRBs for each TD, even if they are zero-length */
for (i = 0; i < num_tds; i++) {
unsigned int total_pkt_count, max_pkt;
unsigned int burst_count, last_burst_pkt_count;
u32 sia_frame_id;
-
first_trb = true;
running_total = 0;
addr = start_addr + urb->iso_frame_desc[i].offset;
@@ -4137,14 +4128,14 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
goto cleanup;
}
td = &urb_priv->td[i];
- /* use SIA as default, if frame id is used overwrite it */
+
sia_frame_id = TRB_SIA;
- if (!(urb->transfer_flags & URB_ISO_ASAP) &&
- (xhci->hcc_params & HCC_CFC)) {
- frame_id = xhci_get_isoc_frame_id(xhci, urb, i);
- if (frame_id >= 0)
- sia_frame_id = TRB_FRAME_ID(frame_id);
+ if (!(urb->transfer_flags & URB_ISO_ASAP) && (xhci->hcc_params & HCC_CFC)) {
+ frame_id = (start_uframe + i * uinterval) / 8;
+ frame_id %= MAX_FRAMES;
+ sia_frame_id = TRB_FRAME_ID(frame_id);
}
+
/*
* Set isoc specific data for the first TRB in a TD.
* Prevent HW from getting the TRBs by keeping the cycle state
@@ -4156,7 +4147,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
(i ? ep_ring->cycle_state : !start_cycle);
/* xhci 1.1 with ETE uses TD_Size field for TBC, old is Rsvdz */
- if (!xep->use_extended_tbc)
+ if (!ep->use_extended_tbc)
field |= TRB_TBC(burst_count);
/* fill the rest of the TRB fields, and remaining normal TRBs */
@@ -4198,7 +4189,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
TRB_INTR_TARGET(0);
/* xhci 1.1 with ETE uses TD Size field for TBC */
- if (first_trb && xep->use_extended_tbc)
+ if (first_trb && ep->use_extended_tbc)
length_field |= TRB_TD_SIZE_TBC(burst_count);
else
length_field |= TRB_TD_SIZE(remainder);
@@ -4223,9 +4214,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
}
}
- /* store the next frame id */
- if (xhci->hcc_params & HCC_CFC)
- xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
+ ep->next_uframe = (start_uframe + num_tds * uinterval) % MAX_UFRAMES;
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
if (xhci->quirks & XHCI_AMD_PLL_FIX)
@@ -4249,7 +4238,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
*/
urb_priv->td[0].end_trb = ep_ring->enqueue;
/* Every TRB except the first & last will have its cycle bit flipped. */
- td_to_noop(xhci, xep, &urb_priv->td[0], true);
+ td_to_noop(xhci, ep, &urb_priv->td[0], true);
/* Reset the ring enqueue back to the first TRB and its cycle bit. */
ep_ring->enqueue = urb_priv->td[0].start_trb;
@@ -4269,19 +4258,17 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
{
- struct xhci_virt_device *xdev;
+ struct xhci_virt_device *vdev;
struct xhci_ring *ep_ring;
struct xhci_ep_ctx *ep_ctx;
- int start_frame;
+ struct xhci_virt_ep *ep;
int num_tds, num_trbs, i;
int ret;
- struct xhci_virt_ep *xep;
- int ist;
- xdev = xhci->devs[slot_id];
- xep = &xhci->devs[slot_id]->eps[ep_index];
- ep_ring = xdev->eps[ep_index].ring;
- ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ vdev = xhci->devs[slot_id];
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ ep_ring = vdev->eps[ep_index].ring;
+ ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
num_trbs = 0;
num_tds = urb->number_of_packets;
@@ -4302,38 +4289,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
*/
check_interval(urb, ep_ctx);
- /* Calculate the start frame and put it in urb->start_frame. */
- if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) {
- if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
- urb->start_frame = xep->next_frame_id;
- goto skip_start_over;
- }
- }
-
- start_frame = readl(&xhci->run_regs->microframe_index);
- start_frame &= 0x3fff;
- /*
- * Round up to the next frame and consider the time before trb really
- * gets scheduled by hardare.
- */
- ist = xhci_ist_microframes(xhci);
- start_frame += ist + XHCI_CFC_DELAY;
- start_frame = roundup(start_frame, 8);
-
- /*
- * Round up to the next ESIT (Endpoint Service Interval Time) if ESIT
- * is greate than 8 microframes.
- */
- if (urb->dev->speed == USB_SPEED_LOW ||
- urb->dev->speed == USB_SPEED_FULL) {
- start_frame = roundup(start_frame, urb->interval << 3);
- urb->start_frame = start_frame >> 3;
- } else {
- start_frame = roundup(start_frame, urb->interval);
- urb->start_frame = start_frame;
- }
-
-skip_start_over:
+ if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING)
+ ep->next_uframe = -1;
return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index aeecd301f207..bcb7b8c877db 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -287,6 +287,11 @@ struct xhci_run_regs {
struct xhci_intr_reg ir_set[1024];
};
+/* Bits [13:3] of the microframe index equals the 1ms frame index */
+#define MFINDEX_TO_FRAME(p) (((p) >> 3) & 0x7ff)
+#define MAX_FRAMES 2048
+#define MAX_UFRAMES (MAX_FRAMES * 8)
+
/**
* struct doorbell_array
*
@@ -696,7 +701,7 @@ struct xhci_virt_ep {
struct list_head bw_endpoint_list;
unsigned long stop_time;
/* Isoch Frame ID checking storage */
- int next_frame_id;
+ int next_uframe;
/* Use new Isoch TRB layout needed for extended TBC support */
bool use_extended_tbc;
/* set if this endpoint is controlled via sideband access*/
--
2.43.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* Re: [RFT PATCH] xhci: fix frame id calculation for isoc transfer
2026-05-11 19:21 ` [RFT PATCH] xhci: fix frame id calculation for isoc transfer Mathias Nyman
@ 2026-05-11 19:36 ` Mathias Nyman
0 siblings, 0 replies; 47+ messages in thread
From: Mathias Nyman @ 2026-05-11 19:36 UTC (permalink / raw)
To: dylan_robinson; +Cc: linux-usb, mathias.nyman, michal.pecio, stern
On 5/11/26 22:21, Mathias Nyman wrote:
> Always calculate estimated start frame, and set urb->start_frame
>
> Fix valid frame window start and end calculation to match xhci
> spec 4.11.2.5
>
> Don't inclease frame id with 1 if a URB mod transfer is queued late.
> Queue it with next expected frame ID but print a message if URB is
> next expected frame id does not fir valid frame window range (URB mid
> transfer is queued late)
>
> This patch doesn't switch to URB_ISO_ASAP /SIA scheduling if URB is queued
> late, not sure if that would help as it only moves the frame id glitch
> problem forward, unless _every_ URB queued after a late URB is forced to
> use SIA, which again would defeat the point of Frame ID use.
>
> get rid of the annoying 'xep' and 'xdev' variables, xhci driver uses
> ep and vdev naming everywhere else
>
> Contains some FIXMEs, patch for initial testing purposes
>
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
> ---
Butchered the commit message but code should be testable
First URB is aligned to full frame, or to interval start if interval is longer
than a frame
For urbs queued mid isoch transfer we check if the next expected frame id
is withinh the accepted frame window, but only print a message if not.
It doesn't alter the expected start frame id, or switch to URB_ISO_ASAP/SIA
scheduling if the check fails.
Always calculate and set urb->start_frame.
URBs with intrval less than a frame do not need to be aligned to entire frame,
next urb can continue mid frame.
-Mathias
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 16:17 ` Dylan Robinson
2026-05-07 17:24 ` Alan Stern
@ 2026-05-07 21:54 ` Michal Pecio
2026-05-08 3:09 ` Alan Stern
2026-05-08 21:39 ` Dylan Robinson
1 sibling, 2 replies; 47+ messages in thread
From: Michal Pecio @ 2026-05-07 21:54 UTC (permalink / raw)
To: Dylan Robinson; +Cc: Alan Stern, linux-usb, mathias.nyman
On Thu, 7 May 2026 12:17:00 -0400, Dylan Robinson wrote:
> On Wed, May 6, 2026 at 10:39 PM Alan Stern wrote:
> > For one thing, that would be very impractical, as every driver using
> > isochronous transfers would then have to be modified.
>
> Maybe adding a new flag would make more sense then.
It would surely need to be optional on per-URB basis, to avoid updating
all drivers and to enable sub-1ms URBs without gaps on HW like xHCI,
where we can't request starting frame with microframe granularity.
I suppose Windows/OSX have to be doing it this way too?
> I was confused because the documentation regarding URB_ISO_ASAP
> describes this behavior.
>
> - For ISO there are two startup behaviors: Specified start_frame or ASAP.
> - For ASAP set ``URB_ISO_ASAP`` in transfer_flags.
>
> However, it looks like URB_ISO_ASAP actually means something
> different: that the URB can be delayed. Is that correct?
Yes, there are many errors in Documentation/driver-api/usb. I thought
about fixing this when the bug was opened, but maybe it's better to
wait till the dust settles around xhci-hcd.
Alan pointed out that kerneldocs are more up to date, but there is
still a crucial error there: on ehci-hcd, an URB submitted too late
completes with -EXDEV instead of the submission failing with -EXDEV.
And it seems we should strive for the same in xhci-hcd, instead of
silently rescheduling such URBs to run later.
> For an ongoing stream I would expect that submitting a start_frame
> that is discontinuous and in the past would result in either a
> submission failure or immediate completion with an error status. Such
> an error would provide useful feedback to the driver because its
> occurrence would mean the stream is no longer synchronized as the
> driver expects.
The idea is that in absence of bugs (which xhci-hcd has plenty), the
stream is always synchronized as long as all URBs are completing
successfully and resubmitting before returning from their complete()
callback. Drivers don't need to "start_frame++" for this to work.
If an IRQ delay causes resubmission to be late, some or all "frames"
of the URB will complete with EXDEV.
Also, I believe such completion should occur in submission order, i.e.
only after all URBs submitted earlier have completed in some manner.
> If the driver requests a specific start frame, it should be the
> driver's responsibility to ensure it is a valid frame within the valid
> scheduling window. If the requested start frame is invalid or outside
> the host controller driver's valid scheduling window, the request
> should fail. This would be more helpful than silently fixing it upon
> submission.
There would need to be some convention how to deal with bInterval > 1.
Such endpoint doesn't use every (micro)frame and can't be scheduled
into any arbitrary one.
In such cases, xHCI spec only permits the first frame of every ESIT to
be specified, but the actual transfer will take place anywhere within
the ESIT, and we don't know when. See 4.11.2.5.
Drivers would need to be aware of such low level details.
> Currently, with non-CFC host controllers, there is no way to determine
> which frame a transfer occurs in. This is because without CFC, the
> start_frame returned in the URB is only approximate.
That's partly a bug.
Filling start_frame is only implemented for CFC HCs. On non-CFC we
could still set start_frame accurately on the first URB and later
increment it blindly. What we cannot do is ensure that the HW will
stay in sync after Missed Service. All bets are off after EXDEV.
On non-CFC we would also be forced to ignore any driver-specified
start_frame except at the beginning of a new stream.
> Currently, there is no straightforward way to inform the xhci driver
> whether a URB represents a stream start or continuation. This makes it
> difficult to detect missed service intervals or other scheduling
> discontinuities.
It's supposed to be implied that submission from a complete() callback
is a continuation. I believe one of the submitted patches fixes that.
Regards,
Michal
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 21:54 ` [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set Michal Pecio
@ 2026-05-08 3:09 ` Alan Stern
2026-05-08 9:41 ` Michal Pecio
2026-05-08 21:39 ` Dylan Robinson
1 sibling, 1 reply; 47+ messages in thread
From: Alan Stern @ 2026-05-08 3:09 UTC (permalink / raw)
To: Michal Pecio; +Cc: Dylan Robinson, linux-usb, mathias.nyman
On Thu, May 07, 2026 at 11:54:32PM +0200, Michal Pecio wrote:
> Alan pointed out that kerneldocs are more up to date, but there is
> still a crucial error there: on ehci-hcd, an URB submitted too late
> completes with -EXDEV instead of the submission failing with -EXDEV.
I don't remember the details exactly, but are you perhaps not taking
care to distinguish between URBs that are partially too late (some of
their packets' time slots have expired but some have not) and URBs that
are entirely too late (all of their packets' time slots are expired)?
As I recall, the first case is supposed to complete with some of the
packet statuses set to -EXDEV, whereas in the second case the entire
submission is supposed to fail with -EXDEV.
But my memory could easily be wrong. And I'm too tired to check the
source code now...
Alan Stern
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-08 3:09 ` Alan Stern
@ 2026-05-08 9:41 ` Michal Pecio
2026-05-08 14:54 ` Alan Stern
0 siblings, 1 reply; 47+ messages in thread
From: Michal Pecio @ 2026-05-08 9:41 UTC (permalink / raw)
To: Alan Stern; +Cc: Dylan Robinson, linux-usb, mathias.nyman
On Thu, 7 May 2026 23:09:18 -0400, Alan Stern wrote:
> On Thu, May 07, 2026 at 11:54:32PM +0200, Michal Pecio wrote:
> > Alan pointed out that kerneldocs are more up to date, but there is
> > still a crucial error there: on ehci-hcd, an URB submitted too late
> > completes with -EXDEV instead of the submission failing with -EXDEV.
>
> I don't remember the details exactly, but are you perhaps not taking
> care to distinguish between URBs that are partially too late (some of
> their packets' time slots have expired but some have not) and URBs that
> are entirely too late (all of their packets' time slots are expired)?
>
> As I recall, the first case is supposed to complete with some of the
> packet statuses set to -EXDEV, whereas in the second case the entire
> submission is supposed to fail with -EXDEV.
... which was an artificial distinction requiring drivers to handle
substantially the same problem in two ways. They didn't, therefore
24f531371de1 USB: EHCI: accept very late isochronous URBs
changed it to EXDEV completion in all cases. AFAIK, this code still
stands. I can't find the string "EXDEV" anywhere in ehci-hcd source.
Note: every frame start with this status so in case of missed transfer
the driver simply leaves it unchanged.
Regards,
Michal
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-08 9:41 ` Michal Pecio
@ 2026-05-08 14:54 ` Alan Stern
0 siblings, 0 replies; 47+ messages in thread
From: Alan Stern @ 2026-05-08 14:54 UTC (permalink / raw)
To: Michal Pecio; +Cc: Dylan Robinson, linux-usb, mathias.nyman
On Fri, May 08, 2026 at 11:41:26AM +0200, Michal Pecio wrote:
> On Thu, 7 May 2026 23:09:18 -0400, Alan Stern wrote:
> > On Thu, May 07, 2026 at 11:54:32PM +0200, Michal Pecio wrote:
> > > Alan pointed out that kerneldocs are more up to date, but there is
> > > still a crucial error there: on ehci-hcd, an URB submitted too late
> > > completes with -EXDEV instead of the submission failing with -EXDEV.
> >
> > I don't remember the details exactly, but are you perhaps not taking
> > care to distinguish between URBs that are partially too late (some of
> > their packets' time slots have expired but some have not) and URBs that
> > are entirely too late (all of their packets' time slots are expired)?
> >
> > As I recall, the first case is supposed to complete with some of the
> > packet statuses set to -EXDEV, whereas in the second case the entire
> > submission is supposed to fail with -EXDEV.
>
> ... which was an artificial distinction requiring drivers to handle
> substantially the same problem in two ways. They didn't, therefore
>
> 24f531371de1 USB: EHCI: accept very late isochronous URBs
>
> changed it to EXDEV completion in all cases. AFAIK, this code still
> stands. I can't find the string "EXDEV" anywhere in ehci-hcd source.
> Note: every frame start with this status so in case of missed transfer
> the driver simply leaves it unchanged.
I had completely forgotten about that commit. And you're right; the
kerneldoc for usb_submit_urb() needs to be updated. I guess that can be
done after the xHCI changes have settled down, when we'll have a better
idea of what to say.
Alan Stern
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-07 21:54 ` [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set Michal Pecio
2026-05-08 3:09 ` Alan Stern
@ 2026-05-08 21:39 ` Dylan Robinson
2026-05-09 11:10 ` Michal Pecio
1 sibling, 1 reply; 47+ messages in thread
From: Dylan Robinson @ 2026-05-08 21:39 UTC (permalink / raw)
To: Michal Pecio; +Cc: Alan Stern, linux-usb, mathias.nyman
On Thu, May 7, 2026 at 5:54 PM Michal Pecio wrote:
> It would surely need to be optional on per-URB basis, to avoid updating
> all drivers and to enable sub-1ms URBs without gaps on HW like xHCI,
> where we can't request starting frame with microframe granularity.
>
> I suppose Windows/OSX have to be doing it this way too?
Windows isochronous transfers behave similarly to the outdated
documentation in Documentation/driver-api/usb.
Setting USBD_START_ISO_TRANSFER_ASAP in the URB instructs the USB
driver stack to schedule the transfer in the next appropriate frame.
For the first URB on a pipe, transfers with this flag are scheduled as
soon as possible. For subsequent URBs, the stack automatically tracks
stream continuity.
Otherwise, the UrbIsochronousTransfer.StartFrame member specifies the
starting USB frame number for the transfer.
Source: https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/transfer-data-to-isochronous-endpoints
The USBDriverKit API on macOS for submitting isochronous transfers is:
virtual kern_return_t IsochIO(
IOMemoryDescriptor *dataBuffer,
IOMemoryDescriptor *frameList,
uint64_t firstFrameNumber,
OSAction *completion);
firstFrameNumber: The starting frame number for the request. You can
get the current frame number from the GetFrameNumber method of
IOUSBHostDevice or IOUSBHostInterface. Specify 0 to begin the transfer
on the next available frame (XHCI only).
Source: https://developer.apple.com/documentation/usbdriverkit/iousbhostpipe/isochio
> It's supposed to be implied that submission from a complete() callback
> is a continuation. I believe one of the submitted patches fixes that.
Because our devices use the implicit feedback synchronization model,
our drivers typically submit OUT transfers from the IN pipe's
completion callbacks. Would those submissions also be considered
continuations of the existing OUT stream, or is continuity only
inferred when a URB is submitted from the completion handler
associated with the same pipe?
Dylan
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-08 21:39 ` Dylan Robinson
@ 2026-05-09 11:10 ` Michal Pecio
2026-05-09 20:18 ` Dylan Robinson
0 siblings, 1 reply; 47+ messages in thread
From: Michal Pecio @ 2026-05-09 11:10 UTC (permalink / raw)
To: Dylan Robinson; +Cc: Alan Stern, linux-usb, mathias.nyman
On Fri, 8 May 2026 17:39:20 -0400, Dylan Robinson wrote:
> On Thu, May 7, 2026 at 5:54 PM Michal Pecio wrote:
> > It would surely need to be optional on per-URB basis, to avoid
> > updating all drivers and to enable sub-1ms URBs without gaps on HW
> > like xHCI, where we can't request starting frame with microframe
> > granularity.
> >
> > I suppose Windows/OSX have to be doing it this way too?
>
> Windows isochronous transfers behave similarly to the outdated
> documentation in Documentation/driver-api/usb.
>
> Setting USBD_START_ISO_TRANSFER_ASAP in the URB instructs the USB
> driver stack to schedule the transfer in the next appropriate frame.
> For the first URB on a pipe, transfers with this flag are scheduled as
> soon as possible. For subsequent URBs, the stack automatically tracks
> stream continuity.
>
> Otherwise, the UrbIsochronousTransfer.StartFrame member specifies the
> starting USB frame number for the transfer.
>
> Source:
> https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/transfer-data-to-isochronous-endpoints
Looks like Windows ASAP is similar to Linux non-ASAP behavior, except
that they consider new URBs to continue the stream for 1024 USB frames
after the last completed URB. In Linux that number is 0 instead, as
there is no way to indicate a new stream by other means.
Windows requires isoc URBs to be frame-aligned. Linux doesn't have this
limitation. I can run a high-speed UAC dongle with two 0.5ms URBs just
fine, though ALSA won't let me go shorter for some reason.
> The USBDriverKit API on macOS for submitting isochronous transfers is:
>
> virtual kern_return_t IsochIO(
> IOMemoryDescriptor *dataBuffer,
> IOMemoryDescriptor *frameList,
> uint64_t firstFrameNumber,
> OSAction *completion);
>
> firstFrameNumber: The starting frame number for the request. You can
> get the current frame number from the GetFrameNumber method of
> IOUSBHostDevice or IOUSBHostInterface. Specify 0 to begin the transfer
> on the next available frame (XHCI only).
>
> Source:
> https://developer.apple.com/documentation/usbdriverkit/iousbhostpipe/isochio
Not sure how Apple and MS deal with the "phase" issue, i.e. does a
bInterval=6 endpoint run in frames 0,4,8,... or 3,7,11,... and how
are drivers supposed to set firstFrameNumber then.
Looking at the MS article, I suspect they want drivers to pretend it's
always 0,4,8, which is what the xHCI spec requires too.
> > It's supposed to be implied that submission from a complete()
> > callback is a continuation. I believe one of the submitted patches
> > fixes that.
>
> Because our devices use the implicit feedback synchronization model,
> our drivers typically submit OUT transfers from the IN pipe's
> completion callbacks. Would those submissions also be considered
> continuations of the existing OUT stream, or is continuity only
> inferred when a URB is submitted from the completion handler
> associated with the same pipe?
I meant completion at the same endpoint because it's a common case,
but submission while any URBs are pending and not completed yet also
is supposed to continues the stream, regardless of who submits.
Or at least that's the intent stated by Alan and what it was before
"BH giveback". I think today there is a short window after completion
by HW and before complete() runs, where even ehci-hcd may fail to
detect that the last remaining URB is still "in flight". In this case
it's likely too late to avoid a gap on OUT, but the gap should produce
EXDEV completions rather than delay the next URB by a few uframes.
Perhaps this should be fixed for all HCDs. The alternative is to ask
drivers to make the IN completion give the OUT completion any required
information to resubmit, which increases latency and chance of EXDEV,
or they risk potential hard to detect gaps due to SW latency. (The gaps
could be detected by looking at frame_desc->start_frame, at least when
xhci-hcd is fixed to fill it properly on non-CFC chips).
Regards,
Michal
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2026-05-09 11:10 ` Michal Pecio
@ 2026-05-09 20:18 ` Dylan Robinson
0 siblings, 0 replies; 47+ messages in thread
From: Dylan Robinson @ 2026-05-09 20:18 UTC (permalink / raw)
To: Michal Pecio; +Cc: Alan Stern, linux-usb, mathias.nyman
On Sat, May 9, 2026 at 7:10 AM Michal Pecio wrote:
> Windows requires isoc URBs to be frame-aligned. Linux doesn't have this
> limitation. I can run a high-speed UAC dongle with two 0.5ms URBs just
> fine, though ALSA won't let me go shorter for some reason.
This appears to be true. After reading the documentation, it was not
clear to me whether it might be possible to do something similar on
Windows by submitting two URBs with the same StartFrame value, each
describing four microframes.
So, I tried an experiment against a bInterval = 1 high-speed device,
and any submission describing less than a full frame's worth of
transfers was not scheduled and was immediately completed with
USBD_STATUS_BUFFER_UNDERRUN.
> Not sure how Apple and MS deal with the "phase" issue, i.e. does a
> bInterval=6 endpoint run in frames 0,4,8,... or 3,7,11,... and how
> are drivers supposed to set firstFrameNumber then.
>
> Looking at the MS article, I suspect they want drivers to pretend it's
> always 0,4,8, which is what the xHCI spec requires too.
The Windows documentation suggests that high-speed isochronous
endpoint descriptors are only supported up to bInterval = 4.
Source: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/usbspec/ns-usbspec-_usb_endpoint_descriptor
It also may be the case that Microsoft only supports bInterval = 1 for
full-speed isochronous endpoints. That descriptor documentation
suggests that other values are valid, but the isochronous transfer
article includes the following comment in the example code:
// Microsoft USB stack only supports bInterval value of 1 for
// full-speed isochronous endpoints.
Source: https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/transfer-data-to-isochronous-endpoints
For Apple, I have not been able to find documentation describing how
start frame alignment is handled for isochronous endpoints with
polling intervals greater than 1 ms.
I noticed you weren't CC'd on my last e-mail exchange with Alan.
On Fri, May 8, 2026 at 9:25 PM Alan Stern wrote:
> On Fri, May 08, 2026 at 01:20:04PM -0400, Dylan Robinson wrote:
> > Perhaps, rather than treating this as a "specified start frame", it
> > would be better to think of it as a "specified start service
> > interval".
> >
> > If a driver requests a start frame that is not specifically available
> > within the periodic schedule, it seems reasonable for the HCD to place
> > the transfer at another valid position within that same service
> > interval. The actual start frame used could then still be reported
> > back through the completed URB, which can be inspected by the driver
> > if it needs to know the precise scheduling decision.
> >
> > For endpoints with larger polling intervals, this "nearest valid start
> > frame" approach seems appropriate, since the device cannot expect
> > timing granularity finer than its polling interval anyway.
>
> That makes sense, and it should be doable. At least for ehci-hcd, and
> likely for xhci-hcd as well.
I wonder what you think about adopting this approach for Linux.
Alternatively, the requested start frame could be interpreted as a
"not-before" constraint (i.e. the HCD would be allowed to schedule it
on the first valid frame that is equal to or greater than the
requested value).
Another option would be to require that the submitted start frame be
aligned to the polling interval, but that seems unnecessarily
restrictive.
Dylan
^ permalink raw reply [flat|nested] 47+ messages in thread
* [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
` (26 preceding siblings ...)
2026-05-06 15:03 ` bugzilla-daemon
@ 2026-05-11 19:15 ` bugzilla-daemon
27 siblings, 0 replies; 47+ messages in thread
From: bugzilla-daemon @ 2026-05-11 19:15 UTC (permalink / raw)
To: linux-usb
https://bugzilla.kernel.org/show_bug.cgi?id=220748
Mathias Nyman (mathias.nyman@linux.intel.com) changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |mathias.nyman@linux.intel.c
| |om
--- Comment #28 from Mathias Nyman (mathias.nyman@linux.intel.com) ---
Created attachment 310090
--> https://bugzilla.kernel.org/attachment.cgi?id=310090&action=edit
testpatch set and check xhci frame id correcly
-Aligns frame id to full frame or esit on first urb,
-Always sets urb->frame_id
-Calculates valid frame window as in spec
-checks if next frame id mid stream is within frame window, print message if
not. Does not alter frame id even if outside frame window.
- allows next URB to be queued mid transfer continueing mid frame (endpoint
interval must be shorter than a full frame as well)
Commit message and some comments needs cleaning up, but code should be
testable.
xhci tracing shows the frame_id of each queued urb, enable with:
echo 1 > /sys/kernel/tracing/events/xhci-hcd/xhci_urb_enqueue/enable
echo 1 > /sys/kernel/tracing/events/xhci-hcd/xhci_queue_trb/enable
<transfer some non SIA isoc transfers >
cat /sys/kernel/tracing/trace
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply [flat|nested] 47+ messages in thread