sound/usb/endpoint.c | 3 +++ sound/usb/quirks.c | 8 ++++++++ 2 files changed, 11 insertions(+)
The Generic USB Audio device (0x1e0b:d01e) produces crackling noise during
system boot when the boot music plays. The issue disappears once the system
has fully started.
Kernel logs show xhci-ring warnings when the device initializes:
[ 2.836118] usb 1-3: New USB device found, idVendor=1e0b, idProduct=d01e
[ 9.654586] xhci_hcd 0000:03:00.3: Frame ID 644 (reg 5154, index 13) beyond range (645, 1539)
[ 9.654589] xhci_hcd 0000:03:00.3: Ignore frame ID field, use SIA bit instead
[ 9.655053] xhci_hcd 0000:03:00.3: Frame ID 644 (reg 5158, index 14) beyond range (645, 1539)
[ 9.655055] xhci_hcd 0000:03:00.3: Ignore frame ID field, use SIA bit instead
These warnings are generated by xhci_get_isoc_frame_id() in xhci-ring.c.
Data endpoints have multiple TDs per URB (number_of_packets > 1). For TD
index=0, the function adjusts start_frame to a valid range when validation
fails. However, for TD index>0, the calculated Frame IDs become stale
before validation completes, causing xhci_get_isoc_frame_id() to return
-EINVAL and trigger a fallback to SIA (Schedule Information Address) mode
for those TDs. This inconsistent scheduling between index=0 TD (direct
Frame ID) and index>0 TDs (SIA mode) within the same data endpoint causes
audio data misalignment and crackling noise during boot.
Sync endpoints have only one TD per URB (number_of_packets = 1), so they
don't suffer from this mixed scheduling issue.
Fix by setting URB_ISO_ASAP flag for the data endpoint of this specific
device. This flag causes the xHCI driver to skip Frame ID calculation
entirely and let hardware schedule via SIA bit for all TDs, ensuring
consistent scheduling within the data endpoint. Also add a quirk to skip
the first 4 packets on the sync endpoint.
Signed-off-by: Zhang Heng <zhangheng@kylinos.cn>
---
sound/usb/endpoint.c | 3 +++
sound/usb/quirks.c | 8 ++++++++
2 files changed, 11 insertions(+)
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 24cd7692bd01..6a6a6797ac4b 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -1256,6 +1256,9 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
goto out_of_memory;
u->urb->pipe = ep->pipe;
u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */
+ if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e))
+ u->urb->transfer_flags |= URB_ISO_ASAP;
u->urb->interval = 1 << ep->datainterval;
u->urb->context = u;
u->urb->complete = snd_complete_urb;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2949a0d2d961..52a4fd352ea2 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1938,6 +1938,14 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
ep->skip_packets = 4;
+ /*
+ * Generic USB Audio (0x1e0b:d01e) - skip initial sync packets
+ * to avoid crackling noise during system boot
+ */
+ if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) &&
+ ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
+ ep->skip_packets = 4;
+
/*
* M-Audio Fast Track C400/C600 - when packets are not skipped, real
* world latency varies by approx. +/- 50 frames (at 96kHz) each time
--
2.25.1
On Mon, Jul 13, 2026 at 04:10:48PM +0800, Zhang Heng wrote: > + /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */ > + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e)) > + u->urb->transfer_flags |= URB_ISO_ASAP; Not a maintainer, just a bystander who was Cc'd -- two small notes on the form of the patch, plus one question I can't answer myself. A per-device usb_id comparison in the endpoint.c fast path seems like the kind of thing the quirk_flags_table in quirks.c exists to avoid. Would a QUIRK_FLAG_ISO_ASAP (set in the table, tested here as chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) work for you? It keeps endpoint.c device-agnostic, and the next device with the same symptom becomes a one-line table entry rather than another if. > + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) && > + ep->type == SND_USB_ENDPOINT_TYPE_SYNC) > + ep->skip_packets = 4; The block immediately above this one already does exactly "type == SND_USB_ENDPOINT_TYPE_SYNC -> skip_packets = 4"; adding the ID to that condition would avoid the duplicate if. Also, the changelog doesn't say what this hunk contributes on its own -- is URB_ISO_ASAP alone insufficient, and if so, what does skipping the first 4 sync packets fix that ASAP doesn't? Right now the two changes are indistinguishable in the commit message, and skip_packets = 4 reads as belt-and-braces. And the question. If xhci_get_isoc_frame_id() really does compute a stale Frame ID for TDs at index > 0 -- because the frame has advanced by the time validation runs -- then isn't that generic to any isoc URB with number_of_packets > 1, rather than specific to 1e0b:d01e? The index=0 TD gets clamped back into range, later TDs don't, and the endpoint ends up mixing explicit Frame IDs with SIA. Nothing in that description looks device-specific to me. If it is in fact generic, a per-device quirk in sound/usb papers over it for one device while every other multi-TD isoc consumer keeps hitting it -- and the fix would belong in xhci-ring.c. I'm not familiar enough with the xHCI scheduling side to say whether that's right, so it may be worth Cc'ing Mathias Nyman <mathias.nyman@linux.intel.com> to get a verdict before this is settled as a usb-audio quirk. If your device is the only one that turns the mixed scheduling into audible corruption, then the quirk is arguably the right scope after all -- but that reasoning should be in the changelog. One data point that cuts against my own question above: on an AMD xHCI here, streaming to a class-compliant USB audio device, I see no "beyond range" / "Ignore frame ID field" messages at all. If the index > 0 staleness were unconditional I would expect to hit it too. So something about your device's packet layout or your host's timing is presumably what actually trips it -- which would make the device-specific scope defensible, but that is exactly the reasoning I would want to see spelled out in the changelog. Thanks, Gordon Chen
> On Mon, Jul 13, 2026 at 04:10:48PM +0800, Zhang Heng wrote: >> + /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */ >> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e)) >> + u->urb->transfer_flags |= URB_ISO_ASAP; > Not a maintainer, just a bystander who was Cc'd -- two small notes on the > form of the patch, plus one question I can't answer myself. > > A per-device usb_id comparison in the endpoint.c fast path seems like the > kind of thing the quirk_flags_table in quirks.c exists to avoid. Would a > QUIRK_FLAG_ISO_ASAP (set in the table, tested here as > chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) work for you? It keeps endpoint.c > device-agnostic, and the next device with the same symptom becomes a > one-line table entry rather than another if. First of all, let me clarify: this issue occurs when there is startup music, but it plays normally after entering the system. There is a heavy creaking sound when the system is turned on here. Based on the information from dmesg and syslog, there are a large number of xhci hcd frame synchronization failures. I tried adding URB_ISO_ASAP here, and it will be much better, with only a little noise. > >> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) && >> + ep->type == SND_USB_ENDPOINT_TYPE_SYNC) >> + ep->skip_packets = 4; > The block immediately above this one already does exactly > "type == SND_USB_ENDPOINT_TYPE_SYNC -> skip_packets = 4"; adding the ID to > that condition would avoid the duplicate if. Also, the changelog doesn't > say what this hunk contributes on its own -- is URB_ISO_ASAP alone > insufficient, and if so, what does skipping the first 4 sync packets fix > that ASAP doesn't? Right now the two changes are indistinguishable in the > commit message, and skip_packets = 4 reads as belt-and-braces. As mentioned above, there is still a bit of noise when only adding URB_ISO_ASAP, but skip-packets=4 can solve this problem. > > And the question. If xhci_get_isoc_frame_id() really does compute a > stale Frame ID for TDs at index > 0 -- because the frame has advanced by > the time validation runs -- then isn't that generic to any isoc URB with > number_of_packets > 1, rather than specific to 1e0b:d01e? The index=0 TD > gets clamped back into range, later TDs don't, and the endpoint ends up > mixing explicit Frame IDs with SIA. Nothing in that description looks > device-specific to me. If it is in fact generic, a per-device quirk in > sound/usb papers over it for one device while every other multi-TD isoc > consumer keeps hitting it -- and the fix would belong in xhci-ring.c. > > I'm not familiar enough with the xHCI scheduling side to say whether > that's right, so it may be worth Cc'ing Mathias Nyman > <mathias.nyman@linux.intel.com> to get a verdict before this is settled as > a usb-audio quirk. If your device is the only one that turns the mixed > scheduling into audible corruption, then the quirk is arguably the right > scope after all -- but that reasoning should be in the changelog. > > One data point that cuts against my own question above: on an AMD xHCI > here, streaming to a class-compliant USB audio device, I see no "beyond > range" / "Ignore frame ID field" messages at all. If the index > 0 > staleness were unconditional I would expect to hit it too. So something > about your device's packet layout or your host's timing is presumably > what actually trips it -- which would make the device-specific scope > defensible, but that is exactly the reasoning I would want to see spelled > out in the changelog. > > Thanks, > Gordon Chen This repair is currently effective and easy for my sound card and system. Perhaps the general operating system does not have startup music, so similar issues may not have been found. Of course, not all USB sound cards have such problems. test.log is a log file.
On Tue, 14 Jul 2026 03:58:08 +0200,
Zhang Heng wrote:
>
> > On Mon, Jul 13, 2026 at 04:10:48PM +0800, Zhang Heng wrote:
> >> + /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */
> >> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e))
> >> + u->urb->transfer_flags |= URB_ISO_ASAP;
> > Not a maintainer, just a bystander who was Cc'd -- two small notes on the
> > form of the patch, plus one question I can't answer myself.
> >
> > A per-device usb_id comparison in the endpoint.c fast path seems like the
> > kind of thing the quirk_flags_table in quirks.c exists to avoid. Would a
> > QUIRK_FLAG_ISO_ASAP (set in the table, tested here as
> > chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) work for you? It keeps endpoint.c
> > device-agnostic, and the next device with the same symptom becomes a
> > one-line table entry rather than another if.
> First of all, let me clarify: this issue occurs when there is startup music,
> but it plays normally after entering the system. There is a heavy creaking
> sound when the system is turned on here. Based on the information from dmesg
> and syslog, there are a large number of xhci hcd frame synchronization
> failures. I tried adding URB_ISO_ASAP here, and it will be much better,
> with only a little noise.
OK, but it still makes sense to deal URB_ISO_ASAP workaround more
generically.
> >> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) &&
> >> + ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
> >> + ep->skip_packets = 4;
> > The block immediately above this one already does exactly
> > "type == SND_USB_ENDPOINT_TYPE_SYNC -> skip_packets = 4"; adding the ID to
> > that condition would avoid the duplicate if. Also, the changelog doesn't
> > say what this hunk contributes on its own -- is URB_ISO_ASAP alone
> > insufficient, and if so, what does skipping the first 4 sync packets fix
> > that ASAP doesn't? Right now the two changes are indistinguishable in the
> > commit message, and skip_packets = 4 reads as belt-and-braces.
> As mentioned above, there is still a bit of noise when only adding
> URB_ISO_ASAP,
>
> but skip-packets=4 can solve this problem.
Well, one missing thing is to understand why this fixes.
Originally, the skip_packets=4 for Playback Design devices was
introduced for bogus feedback packets at the start of the stream long
time ago. But that's the only known device that needs it. Does your
device send also 4 bogus packets?
The skip_packets=16 for M-Audio devices are rather for avoiding the
latency. And, speaking of latency, I have a patch for the lowlatency
support of implicit fb mode, but never had a test environment.
Could you check the patch below and see the patch below has any
positive/negative influence? Just to be sure.
thanks,
Takashi
-- 8< --
index 682b6c1fe76b..6d144e39a849 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -265,6 +265,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip,
return 0;
}
+/* stop both data and sync endpoints */
static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
{
bool stopped = 0;
@@ -280,6 +281,24 @@ static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
return stopped;
}
+/* only start sync endpoint */
+static int start_sync_endpoint(struct snd_usb_substream *subs)
+{
+ int err;
+
+ if (subs->sync_endpoint &&
+ !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+ err = snd_usb_endpoint_start(subs->sync_endpoint);
+ if (err < 0) {
+ clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* start both data and sync endpoints */
static int start_endpoints(struct snd_usb_substream *subs)
{
int err;
@@ -295,14 +314,9 @@ static int start_endpoints(struct snd_usb_substream *subs)
}
}
- if (subs->sync_endpoint &&
- !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
- err = snd_usb_endpoint_start(subs->sync_endpoint);
- if (err < 0) {
- clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
- goto error;
- }
- }
+ err = start_sync_endpoint(subs);
+ if (err < 0)
+ goto error;
return 0;
@@ -656,12 +670,16 @@ static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
return false;
if (in_free_wheeling_mode(runtime))
return false;
- /* implicit feedback mode has own operation mode */
- if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
- return false;
return true;
}
+/* return true if it's a normal playback (not in implicit fb) */
+static bool is_normal_lowlatency_playback(struct snd_usb_substream *subs)
+{
+ return subs->lowlatency_playback &&
+ !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint);
+}
+
/*
* prepare callback
*
@@ -709,9 +727,11 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
runtime->delay = 0;
subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- !subs->lowlatency_playback) {
- ret = start_endpoints(subs);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!subs->lowlatency_playback)
+ ret = start_endpoints(subs);
+ else if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
+ ret = start_sync_endpoint(subs);
/* if XRUN happens at starting streams (possibly with implicit
* fb case), restart again, but only try once.
*/
@@ -1539,7 +1559,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
frame_limit = subs->frame_limit + ep->max_urb_frames;
transfer_done = subs->transfer_done;
- if (subs->lowlatency_playback &&
+ if (is_normal_lowlatency_playback(subs) &&
runtime->state != SNDRV_PCM_STATE_DRAINING) {
unsigned int hwptr = subs->hwptr_done / stride;
@@ -1625,7 +1645,8 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
subs->trigger_tstamp_pending_update = false;
}
- if (period_elapsed && !subs->running && subs->lowlatency_playback) {
+ if (period_elapsed && !subs->running &&
+ is_normal_lowlatency_playback(subs)) {
subs->period_elapsed_pending = 1;
period_elapsed = 0;
}
@@ -1677,7 +1698,7 @@ static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
struct snd_usb_substream *subs = substream->runtime->private_data;
struct snd_usb_endpoint *ep;
- if (!subs->lowlatency_playback || !subs->running)
+ if (!is_normal_lowlatency_playback(subs) || !subs->running)
return 0;
ep = subs->data_endpoint;
if (!ep)
@@ -1705,6 +1726,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
prepare_playback_urb,
retire_playback_urb,
subs);
+ /* start EPs for both normal and implicit-fb modes */
if (subs->lowlatency_playback &&
cmd == SNDRV_PCM_TRIGGER_START) {
if (in_free_wheeling_mode(substream->runtime))
> On Tue, 14 Jul 2026 03:58:08 +0200,
> Zhang Heng wrote:
>>> On Mon, Jul 13, 2026 at 04:10:48PM +0800, Zhang Heng wrote:
>>>> + /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */
>>>> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e))
>>>> + u->urb->transfer_flags |= URB_ISO_ASAP;
>>> Not a maintainer, just a bystander who was Cc'd -- two small notes on the
>>> form of the patch, plus one question I can't answer myself.
>>>
>>> A per-device usb_id comparison in the endpoint.c fast path seems like the
>>> kind of thing the quirk_flags_table in quirks.c exists to avoid. Would a
>>> QUIRK_FLAG_ISO_ASAP (set in the table, tested here as
>>> chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) work for you? It keeps endpoint.c
>>> device-agnostic, and the next device with the same symptom becomes a
>>> one-line table entry rather than another if.
>> First of all, let me clarify: this issue occurs when there is startup music,
>> but it plays normally after entering the system. There is a heavy creaking
>> sound when the system is turned on here. Based on the information from dmesg
>> and syslog, there are a large number of xhci hcd frame synchronization
>> failures. I tried adding URB_ISO_ASAP here, and it will be much better,
>> with only a little noise.
> OK, but it still makes sense to deal URB_ISO_ASAP workaround more
> generically.
>
>>>> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) &&
>>>> + ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
>>>> + ep->skip_packets = 4;
>>> The block immediately above this one already does exactly
>>> "type == SND_USB_ENDPOINT_TYPE_SYNC -> skip_packets = 4"; adding the ID to
>>> that condition would avoid the duplicate if. Also, the changelog doesn't
>>> say what this hunk contributes on its own -- is URB_ISO_ASAP alone
>>> insufficient, and if so, what does skipping the first 4 sync packets fix
>>> that ASAP doesn't? Right now the two changes are indistinguishable in the
>>> commit message, and skip_packets = 4 reads as belt-and-braces.
>> As mentioned above, there is still a bit of noise when only adding
>> URB_ISO_ASAP,
>>
>> but skip-packets=4 can solve this problem.
> Well, one missing thing is to understand why this fixes.
> Originally, the skip_packets=4 for Playback Design devices was
> introduced for bogus feedback packets at the start of the stream long
> time ago. But that's the only known device that needs it. Does your
> device send also 4 bogus packets?
>
> The skip_packets=16 for M-Audio devices are rather for avoiding the
> latency. And, speaking of latency, I have a patch for the lowlatency
> support of implicit fb mode, but never had a test environment.
> Could you check the patch below and see the patch below has any
> positive/negative influence? Just to be sure.
>
>
> thanks,
>
> Takashi
>
> -- 8< --
> index 682b6c1fe76b..6d144e39a849 100644
> --- a/sound/usb/pcm.c
> +++ b/sound/usb/pcm.c
> @@ -265,6 +265,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip,
> return 0;
> }
>
> +/* stop both data and sync endpoints */
> static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
> {
> bool stopped = 0;
> @@ -280,6 +281,24 @@ static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
> return stopped;
> }
>
> +/* only start sync endpoint */
> +static int start_sync_endpoint(struct snd_usb_substream *subs)
> +{
> + int err;
> +
> + if (subs->sync_endpoint &&
> + !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
> + err = snd_usb_endpoint_start(subs->sync_endpoint);
> + if (err < 0) {
> + clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
> + return err;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/* start both data and sync endpoints */
> static int start_endpoints(struct snd_usb_substream *subs)
> {
> int err;
> @@ -295,14 +314,9 @@ static int start_endpoints(struct snd_usb_substream *subs)
> }
> }
>
> - if (subs->sync_endpoint &&
> - !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
> - err = snd_usb_endpoint_start(subs->sync_endpoint);
> - if (err < 0) {
> - clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
> - goto error;
> - }
> - }
> + err = start_sync_endpoint(subs);
> + if (err < 0)
> + goto error;
>
> return 0;
>
> @@ -656,12 +670,16 @@ static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
> return false;
> if (in_free_wheeling_mode(runtime))
> return false;
> - /* implicit feedback mode has own operation mode */
> - if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
> - return false;
> return true;
> }
>
> +/* return true if it's a normal playback (not in implicit fb) */
> +static bool is_normal_lowlatency_playback(struct snd_usb_substream *subs)
> +{
> + return subs->lowlatency_playback &&
> + !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint);
> +}
> +
> /*
> * prepare callback
> *
> @@ -709,9 +727,11 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
> runtime->delay = 0;
>
> subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
> - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
> - !subs->lowlatency_playback) {
> - ret = start_endpoints(subs);
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + if (!subs->lowlatency_playback)
> + ret = start_endpoints(subs);
> + else if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
> + ret = start_sync_endpoint(subs);
> /* if XRUN happens at starting streams (possibly with implicit
> * fb case), restart again, but only try once.
> */
> @@ -1539,7 +1559,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
> frame_limit = subs->frame_limit + ep->max_urb_frames;
> transfer_done = subs->transfer_done;
>
> - if (subs->lowlatency_playback &&
> + if (is_normal_lowlatency_playback(subs) &&
> runtime->state != SNDRV_PCM_STATE_DRAINING) {
> unsigned int hwptr = subs->hwptr_done / stride;
>
> @@ -1625,7 +1645,8 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
> subs->trigger_tstamp_pending_update = false;
> }
>
> - if (period_elapsed && !subs->running && subs->lowlatency_playback) {
> + if (period_elapsed && !subs->running &&
> + is_normal_lowlatency_playback(subs)) {
> subs->period_elapsed_pending = 1;
> period_elapsed = 0;
> }
> @@ -1677,7 +1698,7 @@ static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
> struct snd_usb_substream *subs = substream->runtime->private_data;
> struct snd_usb_endpoint *ep;
>
> - if (!subs->lowlatency_playback || !subs->running)
> + if (!is_normal_lowlatency_playback(subs) || !subs->running)
> return 0;
> ep = subs->data_endpoint;
> if (!ep)
> @@ -1705,6 +1726,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
> prepare_playback_urb,
> retire_playback_urb,
> subs);
> + /* start EPs for both normal and implicit-fb modes */
> if (subs->lowlatency_playback &&
> cmd == SNDRV_PCM_TRIGGER_START) {
> if (in_free_wheeling_mode(substream->runtime))
Regarding your patch, I am unable to test it because my current
kernel is based on a certain stable version of 5.4. The
differences in pcm.c are too significant, so the patch cannot
be applied directly.
On Tue, 14 Jul 2026 15:02:09 +0200,
Zhang Heng wrote:
>
> > On Tue, 14 Jul 2026 03:58:08 +0200,
> > Zhang Heng wrote:
> >>> On Mon, Jul 13, 2026 at 04:10:48PM +0800, Zhang Heng wrote:
> >>>> + /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */
> >>>> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e))
> >>>> + u->urb->transfer_flags |= URB_ISO_ASAP;
> >>> Not a maintainer, just a bystander who was Cc'd -- two small notes on the
> >>> form of the patch, plus one question I can't answer myself.
> >>>
> >>> A per-device usb_id comparison in the endpoint.c fast path seems like the
> >>> kind of thing the quirk_flags_table in quirks.c exists to avoid. Would a
> >>> QUIRK_FLAG_ISO_ASAP (set in the table, tested here as
> >>> chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) work for you? It keeps endpoint.c
> >>> device-agnostic, and the next device with the same symptom becomes a
> >>> one-line table entry rather than another if.
> >> First of all, let me clarify: this issue occurs when there is startup music,
> >> but it plays normally after entering the system. There is a heavy creaking
> >> sound when the system is turned on here. Based on the information from dmesg
> >> and syslog, there are a large number of xhci hcd frame synchronization
> >> failures. I tried adding URB_ISO_ASAP here, and it will be much better,
> >> with only a little noise.
> > OK, but it still makes sense to deal URB_ISO_ASAP workaround more
> > generically.
> >
> >>>> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) &&
> >>>> + ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
> >>>> + ep->skip_packets = 4;
> >>> The block immediately above this one already does exactly
> >>> "type == SND_USB_ENDPOINT_TYPE_SYNC -> skip_packets = 4"; adding the ID to
> >>> that condition would avoid the duplicate if. Also, the changelog doesn't
> >>> say what this hunk contributes on its own -- is URB_ISO_ASAP alone
> >>> insufficient, and if so, what does skipping the first 4 sync packets fix
> >>> that ASAP doesn't? Right now the two changes are indistinguishable in the
> >>> commit message, and skip_packets = 4 reads as belt-and-braces.
> >> As mentioned above, there is still a bit of noise when only adding
> >> URB_ISO_ASAP,
> >>
> >> but skip-packets=4 can solve this problem.
> > Well, one missing thing is to understand why this fixes.
> > Originally, the skip_packets=4 for Playback Design devices was
> > introduced for bogus feedback packets at the start of the stream long
> > time ago. But that's the only known device that needs it. Does your
> > device send also 4 bogus packets?
> >
> > The skip_packets=16 for M-Audio devices are rather for avoiding the
> > latency. And, speaking of latency, I have a patch for the lowlatency
> > support of implicit fb mode, but never had a test environment.
> > Could you check the patch below and see the patch below has any
> > positive/negative influence? Just to be sure.
> >
> >
> > thanks,
> >
> > Takashi
> >
> > -- 8< --
> > index 682b6c1fe76b..6d144e39a849 100644
> > --- a/sound/usb/pcm.c
> > +++ b/sound/usb/pcm.c
> > @@ -265,6 +265,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip,
> > return 0;
> > }
> > +/* stop both data and sync endpoints */
> > static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
> > {
> > bool stopped = 0;
> > @@ -280,6 +281,24 @@ static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
> > return stopped;
> > }
> > +/* only start sync endpoint */
> > +static int start_sync_endpoint(struct snd_usb_substream *subs)
> > +{
> > + int err;
> > +
> > + if (subs->sync_endpoint &&
> > + !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
> > + err = snd_usb_endpoint_start(subs->sync_endpoint);
> > + if (err < 0) {
> > + clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
> > + return err;
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/* start both data and sync endpoints */
> > static int start_endpoints(struct snd_usb_substream *subs)
> > {
> > int err;
> > @@ -295,14 +314,9 @@ static int start_endpoints(struct snd_usb_substream *subs)
> > }
> > }
> > - if (subs->sync_endpoint &&
> > - !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
> > - err = snd_usb_endpoint_start(subs->sync_endpoint);
> > - if (err < 0) {
> > - clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
> > - goto error;
> > - }
> > - }
> > + err = start_sync_endpoint(subs);
> > + if (err < 0)
> > + goto error;
> > return 0;
> > @@ -656,12 +670,16 @@ static int
> > lowlatency_playback_available(struct snd_pcm_runtime *runtime,
> > return false;
> > if (in_free_wheeling_mode(runtime))
> > return false;
> > - /* implicit feedback mode has own operation mode */
> > - if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
> > - return false;
> > return true;
> > }
> > +/* return true if it's a normal playback (not in implicit fb) */
> > +static bool is_normal_lowlatency_playback(struct snd_usb_substream *subs)
> > +{
> > + return subs->lowlatency_playback &&
> > + !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint);
> > +}
> > +
> > /*
> > * prepare callback
> > *
> > @@ -709,9 +727,11 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
> > runtime->delay = 0;
> > subs->lowlatency_playback =
> > lowlatency_playback_available(runtime, subs);
> > - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
> > - !subs->lowlatency_playback) {
> > - ret = start_endpoints(subs);
> > + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> > + if (!subs->lowlatency_playback)
> > + ret = start_endpoints(subs);
> > + else if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
> > + ret = start_sync_endpoint(subs);
> > /* if XRUN happens at starting streams (possibly with implicit
> > * fb case), restart again, but only try once.
> > */
> > @@ -1539,7 +1559,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
> > frame_limit = subs->frame_limit + ep->max_urb_frames;
> > transfer_done = subs->transfer_done;
> > - if (subs->lowlatency_playback &&
> > + if (is_normal_lowlatency_playback(subs) &&
> > runtime->state != SNDRV_PCM_STATE_DRAINING) {
> > unsigned int hwptr = subs->hwptr_done / stride;
> > @@ -1625,7 +1645,8 @@ static int prepare_playback_urb(struct
> > snd_usb_substream *subs,
> > subs->trigger_tstamp_pending_update = false;
> > }
> > - if (period_elapsed && !subs->running &&
> > subs->lowlatency_playback) {
> > + if (period_elapsed && !subs->running &&
> > + is_normal_lowlatency_playback(subs)) {
> > subs->period_elapsed_pending = 1;
> > period_elapsed = 0;
> > }
> > @@ -1677,7 +1698,7 @@ static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
> > struct snd_usb_substream *subs = substream->runtime->private_data;
> > struct snd_usb_endpoint *ep;
> > - if (!subs->lowlatency_playback || !subs->running)
> > + if (!is_normal_lowlatency_playback(subs) || !subs->running)
> > return 0;
> > ep = subs->data_endpoint;
> > if (!ep)
> > @@ -1705,6 +1726,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
> > prepare_playback_urb,
> > retire_playback_urb,
> > subs);
> > + /* start EPs for both normal and implicit-fb modes */
> > if (subs->lowlatency_playback &&
> > cmd == SNDRV_PCM_TRIGGER_START) {
> > if (in_free_wheeling_mode(substream->runtime))
> Regarding your patch, I am unable to test it because my current
> kernel is based on a certain stable version of 5.4. The
> differences in pcm.c are too significant, so the patch cannot
> be applied directly.
Please verify whether the problem really exists with the latest
kernel at first. At least we have to be sure about the workaround
with URB_ISO_ASAP for the latest kernel before taking to the upstream.
thanks,
Takashi
On Mon, 13 Jul 2026 15:20:29 +0200, Gordon Chen wrote: > > On Mon, Jul 13, 2026 at 04:10:48PM +0800, Zhang Heng wrote: > > + /* Generic USB Audio (0x1e0b:d01e): use SIA for consistent scheduling */ > > + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e)) > > + u->urb->transfer_flags |= URB_ISO_ASAP; > > Not a maintainer, just a bystander who was Cc'd -- two small notes on the > form of the patch, plus one question I can't answer myself. > > A per-device usb_id comparison in the endpoint.c fast path seems like the > kind of thing the quirk_flags_table in quirks.c exists to avoid. Would a > QUIRK_FLAG_ISO_ASAP (set in the table, tested here as > chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) work for you? It keeps endpoint.c > device-agnostic, and the next device with the same symptom becomes a > one-line table entry rather than another if. > > > + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) && > > + ep->type == SND_USB_ENDPOINT_TYPE_SYNC) > > + ep->skip_packets = 4; > > The block immediately above this one already does exactly > "type == SND_USB_ENDPOINT_TYPE_SYNC -> skip_packets = 4"; adding the ID to > that condition would avoid the duplicate if. Also, the changelog doesn't > say what this hunk contributes on its own -- is URB_ISO_ASAP alone > insufficient, and if so, what does skipping the first 4 sync packets fix > that ASAP doesn't? Right now the two changes are indistinguishable in the > commit message, and skip_packets = 4 reads as belt-and-braces. > > And the question. If xhci_get_isoc_frame_id() really does compute a > stale Frame ID for TDs at index > 0 -- because the frame has advanced by > the time validation runs -- then isn't that generic to any isoc URB with > number_of_packets > 1, rather than specific to 1e0b:d01e? The index=0 TD > gets clamped back into range, later TDs don't, and the endpoint ends up > mixing explicit Frame IDs with SIA. Nothing in that description looks > device-specific to me. If it is in fact generic, a per-device quirk in > sound/usb papers over it for one device while every other multi-TD isoc > consumer keeps hitting it -- and the fix would belong in xhci-ring.c. > > I'm not familiar enough with the xHCI scheduling side to say whether > that's right, so it may be worth Cc'ing Mathias Nyman > <mathias.nyman@linux.intel.com> to get a verdict before this is settled as > a usb-audio quirk. If your device is the only one that turns the mixed > scheduling into audible corruption, then the quirk is arguably the right > scope after all -- but that reasoning should be in the changelog. > > One data point that cuts against my own question above: on an AMD xHCI > here, streaming to a class-compliant USB audio device, I see no "beyond > range" / "Ignore frame ID field" messages at all. If the index > 0 > staleness were unconditional I would expect to hit it too. So something > about your device's packet layout or your host's timing is presumably > what actually trips it -- which would make the device-specific scope > defensible, but that is exactly the reasoning I would want to see spelled > out in the changelog. Agreed on all points! Especially the workaround with URB_ISO_ASAP should be applied in a more generic way. thanks, Takashi
On Tue, 14 Jul 2026 15:09:46 +0200, Zhang Heng wrote: > > > From 11c25cb49a0a233fda643b2eb6c0bca7bc726d37 Mon Sep 17 00:00:00 2001 > From: Zhang Heng <zhangheng@kylinos.cn> > Date: Tue, 14 Jul 2026 17:55:54 +0800 > Subject: [PATCH] ALSA: usb-audio: Fix boot-time crackling for Generic USB > Audio device > > The Generic USB Audio device (0x1e0b:d01e) produces crackling noise during > system boot when the boot music plays. The issue disappears once the system > has fully started. > > Kernel logs show xhci-ring warnings when the device initializes: > [ 9.654586] xhci_hcd 0000:03:00.3: Frame ID 644 (reg 5154, index 13) beyond range (645, 1539) > [ 9.654589] xhci_hcd 0000:03:00.3: Ignore frame ID field, use SIA bit instead > [ 9.655053] xhci_hcd 0000:03:00.3: Frame ID 644 (reg 5158, index 14) beyond range (645, 1539) > [ 9.655055] xhci_hcd 0000:03:00.3: Ignore frame ID field, use SIA bit instead > > These warnings indicate that xhci_get_isoc_frame_id() returns -EINVAL for > TDs at index > 0 because their calculated Frame IDs become stale before > validation completes. This causes a fallback to SIA mode for those TDs, > while index=0 TD uses direct Frame ID. The resulting mixed scheduling within > the same data endpoint causes audio data misalignment and crackling. > > Fix by adding QUIRK_FLAG_ISO_ASAP and setting URB_ISO_ASAP flag for the > data endpoint. This ensures all TDs use consistent SIA scheduling. Also add > a quirk to skip the first 4 sync packets, as some devices send incorrect > feedback data during stream startup. > > Using the quirk mechanism keeps endpoint.c device-agnostic and allows easy > addition of new devices with the same symptom. > > Signed-off-by: Zhang Heng <zhangheng@kylinos.cn> > --- > v1->v2: > > - Replace hardcoded USB ID check in endpoint.c with QUIRK_FLAG_ISO_ASAP > - Add QUIRK_TYPE_ISO_ASAP = 31 enum and QUIRK_FLAG_ISO_ASAP macro in usbaudio.h > - Add device entry to quirk_flags_table in quirks.c for Generic USB Audio (0x1e0b:d01e) > - Add ISO_ASAP string entry to snd_usb_audio_quirk_flag_names > - Add documentation comment for QUIRK_FLAG_ISO_ASAP in usbaudio.h > - Keep endpoint.c device-agnostic, allowing easy addition of new devices > > sound/usb/endpoint.c | 2 ++ > sound/usb/quirks.c | 8 ++++++++ > sound/usb/usbaudio.h | 8 ++++++++ > 3 files changed, 18 insertions(+) > > diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c > index 24cd7692bd01..155b3858dac9 100644 > --- a/sound/usb/endpoint.c > +++ b/sound/usb/endpoint.c > @@ -1256,6 +1256,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) > goto out_of_memory; > u->urb->pipe = ep->pipe; > u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; > + if (ep->chip->quirk_flags & QUIRK_FLAG_ISO_ASAP) > + u->urb->transfer_flags |= URB_ISO_ASAP; > u->urb->interval = 1 << ep->datainterval; > u->urb->context = u; > u->urb->complete = snd_complete_urb; > diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c > index 2949a0d2d961..f5e5b662bfd3 100644 > --- a/sound/usb/quirks.c > +++ b/sound/usb/quirks.c > @@ -1938,6 +1938,10 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) > ep->type == SND_USB_ENDPOINT_TYPE_SYNC) > ep->skip_packets = 4; > > + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) && > + ep->type == SND_USB_ENDPOINT_TYPE_SYNC) > + ep->skip_packets = 4; > + Please add some comment why the device needs this workaround. IIUC, the reason is different from the others in your case. Also, please verify that the problem is reproduced in the latest upstream kernel, and the workaround works as expected there, too. thanks, Takashi
> Please add some comment why the device needs this workaround. > IIUC, the reason is different from the others in your case. > > Also, please verify that the problem is reproduced in the latest > upstream kernel, and the workaround works as expected there, too. > > > thanks, > > Takashi >> --- a/sound/usb/quirks.c >> +++ b/sound/usb/quirks.c >> @@ -1938,6 +1938,10 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) >> ep->type == SND_USB_ENDPOINT_TYPE_SYNC) >> ep->skip_packets = 4; >> >> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) && >> + ep->type == SND_USB_ENDPOINT_TYPE_SYNC) >> + ep->skip_packets = 4; >> + Let me update the current status: that piece of code can be omitted. I have applied your test patch, but there is no change. It might be that your patch has no effect on this device. I tested with version v7.2-rc1. To be honest, without any modification, the problem becomes a reproducible stutter plus crackling noise. After applying my fix, it becomes a probabilistic stutter (still with a relatively high probability of recurrence), but when there is no stutter, there is also no crackling noise. So I am sure that this patch is at least useful, but it does not perfectly solve the problem. I am still trying to resolve the stutter issue, and I wonder if it is due to resource contention during boot? This problem only occurs at boot time; after entering the system, it disappears.
On Mon, 20 Jul 2026 04:23:25 +0200, Zhang Heng wrote: > > > > Please add some comment why the device needs this workaround. > > IIUC, the reason is different from the others in your case. > > > > Also, please verify that the problem is reproduced in the latest > > upstream kernel, and the workaround works as expected there, too. > > > > > > thanks, > > > > Takashi > > >> --- a/sound/usb/quirks.c > >> +++ b/sound/usb/quirks.c > >> @@ -1938,6 +1938,10 @@ void snd_usb_endpoint_start_quirk(struct > snd_usb_endpoint *ep) > >> ep->type == SND_USB_ENDPOINT_TYPE_SYNC) > >> ep->skip_packets = 4; > >> > >> + if (ep->chip->usb_id == USB_ID(0x1e0b, 0xd01e) && > >> + ep->type == SND_USB_ENDPOINT_TYPE_SYNC) > >> + ep->skip_packets = 4; > >> + > > Let me update the current status: that piece of code can be > omitted. I have applied your test patch, but there is no > change. It might be that your patch has no effect on this > device. I tested with version v7.2-rc1. To be honest, > without any modification, the problem becomes a reproducible > stutter plus crackling noise. After applying my fix, it > becomes a probabilistic stutter (still with a relatively > high probability of recurrence), but when there is no > stutter, there is also no crackling noise. So I am sure > that this patch is at least useful, but it does not > perfectly solve the problem. I am still trying to resolve > the stutter issue, and I wonder if it is due to resource > contention during boot? This problem only occurs at boot > time; after entering the system, it disappears. OK, if the workaround is no solid solution, it's not worth to take. Have you tried lowlatency=0 option? If it's an implicit fb and about the playback, it might stabilize. thanks, Takashi
> OK, if the workaround is no solid solution, it's not worth to take. > > Have you tried lowlatency=0 option? If it's an implicit fb and about > the playback, it might stabilize. > > > thanks, > > Takashi I tried lowlatency=0 but it didn't help. I am waiting for the customer to send a machine, and I will test again once it arrives.
> OK, if the workaround is no solid solution, it's not worth to take. > > Have you tried lowlatency=0 option? If it's an implicit fb and about > the playback, it might stabilize. > > > thanks, > > Takashi I tried lowlatency=0 but it didn't help. I am waiting for the customer to send a machine, and I will test again once it arrives.
> OK, if the workaround is no solid solution, it's not worth to take. > > Have you tried lowlatency=0 option? If it's an implicit fb and about > the playback, it might stabilize. I tried lowlatency=0 but it didn't help. I am waiting for the customer to send a machine, and I will test again once it arrives.
© 2016 - 2026 Red Hat, Inc.