From 75bed736620f70f7a719da5637191f9d7b745b99 Mon Sep 17 00:00:00 2001 From: Volodymyr Lurin Date: Wed, 21 Oct 2026 22:38:39 -0400 Subject: ALSA: hda/realtek: Add mute LEDs quirk for HP Pavilion Plus 14-EW1XXX. The HP Pavilion Plus 14-ew1xxx with subsystem ID 103c:0x8c31 needs the ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT bit LED quirk to control speaker mute LED and ALC245_FIXUP_HP_GPIO_LED quirk to control mic mute LED. Define new ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS fixup that combine existing COEF(ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT) and GPIO (ALC245_FIXUP_HP_GPIO_LED) fixups. Add the missing quirk entry. Tested on the affected hardware: both the mute and mic-mute key LEDs respond correctly to the keyboard hotkeys after this change. Based on tests, to control mute LED, it is sufficient to flip coef bit #3. While ALC245_FIXUP_HP_X360_MUTE_LEDS works too (combines ALC245_FIXUP_HP_MUTE_LED_COEFBIT and ALC245_FIXUP_HP_GPIO_LED), it also flip coef bit #2, and side effect of this "flip" is not clear. So separate chained quirk introduced to be of safe side. Signed-off-by: Volodymyr Lurin Link: https://patch.msgid.link/20261022023839.504841-1-volodymyr.iurin@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 23aa175faedd..2f0645482108 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4492,6 +4492,7 @@ enum { ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, + ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS, ALC287_FIXUP_THINKPAD_I2S_SPK, ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC287_FIXUP_CS35L41_I2C_4_THINKPAD_ACPI, @@ -7113,6 +7114,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC245_FIXUP_HP_GPIO_LED }, + [ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_v2_coefbit, + .chained = true, + .chain_id = ALC245_FIXUP_HP_GPIO_LED + }, [ALC287_FIXUP_THINKPAD_I2S_SPK] = { .type = HDA_FIXUP_FUNC, .v.func = alc287_fixup_bind_dacs, @@ -7951,6 +7958,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c31, "HP Pavilion Plus Laptop 14-ew1xxx", ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS), SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), -- cgit v1.2.3 From d2a1fb09414a79de340215e03fb7f85e17cf0a2b Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 22 Oct 2026 10:59:49 +0800 Subject: ALSA: hda/realtek: Fix internal mic on ASUS VivoBook X509JB On the ASUS VivoBook X509JB (SSID 1043:1ec1) with ALC256, the BIOS marks pin 0x13 as not connected and pin 0x1b as the internal mic, but the generic parser still defaults Capture Source to 0x13, which records only white noise. Manually selecting 0x1b works, but PipeWire/WirePlumber resets it on every stream open. Reuse ALC2XX_FIXUP_HEADSET_MIC for this SSID. It limits capture sources to 0x19 and 0x1b, removing the bogus 0x13 source so the internal mic records normally without manual mixer changes. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=222152 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20261022025950.150390-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 2f0645482108..5a2f93c787a9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8234,6 +8234,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), + SND_PCI_QUIRK(0x1043, 0x1ec1, "ASUS X509JB", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), -- cgit v1.2.3 From fd47de58575a27869357a5ca2435f9b58c01b643 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 22 Oct 2026 11:11:13 +0800 Subject: ALSA: hda/realtek: Add quirk for HP Pavilion AiO 24 (ALC225) The HP Pavilion AIO 24-xa0xxx (codec SSID 103c:84ee) uses the ALC225 codec. The internal speakers produce no sound without a machine-specific fixup. Add a HDA_CODEC_QUIRK for this codec SSID using ALC225_FIXUP_HP_AIO_SPK so the speaker output works correctly. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221616 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20261022031113.163632-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 5a2f93c787a9..e7b4543171fd 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8068,6 +8068,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + HDA_CODEC_QUIRK(0x103c, 0x84ee, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK), SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), -- cgit v1.2.3 From b4b7f5f8beabe3cb92028b5b3b6d1b3dac5363fc Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Thu, 22 Oct 2026 15:14:56 +0800 Subject: ALSA: usb-audio: Add iface reset and delay quirk for RYDZ Technology USB Headphone Setting up the interface when suspended/resuming fails on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: New USB device found, idVendor=0124, idProduct=0c36 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: USB Headphone usb 1-1: Manufacturer: RYDZ Technology usb 1-1: SerialNumber: 202601131108 Signed-off-by: Lianqin Hu Link: https://patch.msgid.link/20261022071456.14256-1-hulianqin@vivo.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 43eea724779a..6aa212c2cab6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2496,6 +2496,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x0124, 0x0c36, /* RYDZ Technology USB Headphone */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x03f0, 0x0c56, /* HP Elite x3 Lap Dock */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ -- cgit v1.2.3 From 3a003faf38273b8c6b566a0c437914d8d1f82fb3 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 22 Oct 2026 10:14:36 +0200 Subject: ALSA: ac97: Use snprintf() in snd_ac97_get_name() To prepare for removing strlcat() from the kernel [1], use snprintf() to append the codec name and the optional revision. Also use snprintf() for the unknown vendor fallback. Fix some formatting while at it. No functional change. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20261022081437.1029-2-blum@kernel.org Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_codec.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 207ce083593f..f0ebe6d45836 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1832,36 +1832,39 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, size_t maxlen, int modem) { const struct ac97_codec_id *pid; + size_t len; - sprintf(name, "0x%x %c%c%c", id, - printable(id >> 24), - printable(id >> 16), - printable(id >> 8)); pid = look_for_codec_id(snd_ac97_codec_id_vendors, id); - if (! pid) + if (!pid) { + snprintf(name, maxlen, "0x%x %c%c%c", id, + printable(id >> 24), + printable(id >> 16), + printable(id >> 8)); return; + } strscpy(name, pid->name, maxlen); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || - (! modem && ! (pid->flags & AC97_MODEM_PATCH))) + (!modem && !(pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); - } + } + len = strnlen(name, maxlen); pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { - strlcat(name, " ", maxlen); - strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff) - sprintf(name + strlen(name), " rev %u", id & ~pid->mask); + snprintf(name + len, maxlen - len, " %s rev %u", + pid->name, id & ~pid->mask); + else + snprintf(name + len, maxlen - len, " %s", pid->name); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || - (! modem && ! (pid->flags & AC97_MODEM_PATCH))) + (!modem && !(pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } } else { - int l = strlen(name); - snprintf(name + l, maxlen - l, " id %x", id & 0xff); + snprintf(name + len, maxlen - len, " id %x", id & 0xff); } } -- cgit v1.2.3 From a44a446d24180a7d3d8e712a39a5d8e6243ab688 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 22 Oct 2026 10:22:47 +0200 Subject: ALSA: ump: Use safe_append_string() in snd_ump_update_group_attrs() To prepare for removing strlcat() from the kernel [1], use the existing safe_append_string() helper to append the group name separator. No functional change. Link: https://github.com/KSPP/linux/issues/370 [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20261022082247.1682-2-blum@kernel.org Signed-off-by: Takashi Iwai --- sound/core/ump.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/core/ump.c b/sound/core/ump.c index f6482a9bfe61..04ef66298943 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -559,6 +559,7 @@ static void snd_ump_proc_read(struct snd_info_entry *entry, /* update dir_bits and active flag for all groups in the client */ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) { + static const char separator[] = ", "; struct snd_ump_block *fb; struct snd_ump_group *group; int i; @@ -598,7 +599,8 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) if (!*fb->info.name) continue; if (*group->name) - strlcat(group->name, ", ", sizeof(group->name)); + safe_append_string(group->name, sizeof(group->name), + separator, sizeof(separator)); safe_append_string(group->name, sizeof(group->name), fb->info.name, sizeof(fb->info.name)); } -- cgit v1.2.3 From 677dc50304129465dc4626da01a3c0991dc89537 Mon Sep 17 00:00:00 2001 From: renpanpan Date: Thu, 22 Oct 2026 16:54:38 +0800 Subject: ALSA: hda: Force MSI for Hygon H34M51A board The Hygon HDA controller (PCI 1d94:14a9) has AZX_DCAPS_NO_MSI set in the PCI ID table to use legacy INTx, as MSI may lose interrupts under heavy load on some Hygon platforms (commit 41212ff3e4ed ("ALSA: hda: Add support for Hygon family 18h model 5h HD-Audio")). However, on the Kunlun H34M51A board (PCI subsystem 1d94:c950) the INTx interrupt line is not wired. /proc/interrupts shows zero count for the HDA IRQ from boot, so the RIRB interrupt handler is never called. Without RIRB processing, azx_rirb_get_response() times out after 1 s, switches to polling mode, and the resulting slow codec communication delays card registration by ~20 s and breaks unsolicited jack-detection event delivery. Force MSI on for subsystem 1d94:c950 by adding it to msi_deny_list with value 1. The list is consulted in check_msi() before the AZX_DCAPS_NO_MSI check, so H34M51A gets MSI enabled while other Hygon platforms retain INTx. Signed-off-by: renpanpan Link: https://patch.msgid.link/20261022085438.1315679-1-rppxj178543@163.com Signed-off-by: Takashi Iwai --- sound/hda/controllers/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 6b80b59b8743..af01b2669c49 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -1668,6 +1668,7 @@ static const struct snd_pci_quirk msi_deny_list[] = { SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ + SND_PCI_QUIRK(0x1d94, 0xc950, "Hygon H34M51A", 1), /* INTx broken */ {} }; -- cgit v1.2.3 From 9a8b6b459e3a0e677cec7c0cab18602b6f3bba45 Mon Sep 17 00:00:00 2001 From: Shahab Ghodsi Date: Thu, 22 Oct 2026 12:31:49 +0330 Subject: ALSA: hda/realtek: Add quirk for ASUS ExpertBook B3402FVA The ASUS ExpertBook B3402FVA (PCI SSID 1043:1e03) uses an ALC256 codec. Headphone output is extremely quiet and distorted, with only the loudest parts audible. The BIOS does not enable the headphone depop circuit. Reuse ALC256_FIXUP_ASUS_HPE, which sets COEF 0x0f to 0x7778 to enable the headphone depop circuit. This fixup is already used for the ASUS TM420 and B1400CEPE. The COEF write was verified manually with hda-verb on this machine; headphones become loud and clear. The chained pin change on 0x19 was also applied manually; the headset microphone worked before and after, and audio playback was unaffected. Closes: https://github.com/thesofproject/linux/issues/5996 Signed-off-by: Shahab Ghodsi Link: https://patch.msgid.link/20261022090149.304576-1-shahab16774@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e7b4543171fd..c499165b80e9 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8223,6 +8223,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1e03, "ASUS ExpertBook B3402FVA", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e3e, "ASUS VivoBook M515DA/X515DAP", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), -- cgit v1.2.3 From 0d01f90b07e877ef31f9959e9b0bc6bcca9b99df Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Thu, 22 Oct 2026 18:32:41 +0200 Subject: ALSA: sh: aica: Fix spu_memset() stride spu_memset() writes one 32-bit word per iteration, but advances the destination offset by only one byte. spu_reset() calls it with 0x200000 / 4 words to clear the 2 MiB of sound RAM, so: - three out of four writel() calls target an unaligned address. The SH4 can't do that. Each one traps and is emulated by the unaligned access fixup. This call to spu_memset() causes 393216 unaligned accesses, visible in /proc/cpu/alignment. - the clear covered only about 512 KiB of sound RAM, not 2 MiB. Advance the offset by the size of the written word, to clear the full sound RAM using aligned writes. Fixes: 198de43d758c ("[ALSA] Add ALSA support for the SEGA Dreamcast PCM device") Signed-off-by: Florian Fuchs Acked-by: Adrian McMenamin Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20261022163241.2504922-1-fuchsfl@gmail.com --- sound/sh/aica.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 078fc7fc08f9..04f80c8c89c0 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -94,7 +94,7 @@ static void spu_memset(u32 toi, u32 what, int length) local_irq_save(flags); writel(what, toi + SPU_MEMORY_BASE); local_irq_restore(flags); - toi++; + toi += sizeof(what); } } -- cgit v1.2.3 From e4a21e95c5577dda3a4049b8eb7f2df7d24b3134 Mon Sep 17 00:00:00 2001 From: Andrey Zakharov Date: Fri, 23 Oct 2026 13:50:27 +0300 Subject: ALSA: emu10k1: Pass destination size to copy_string() copy_string() copies the bus and input/output names into fixed-size arrays of struct snd_emu10k1_fx8010_info using strcpy() and sprintf(), and it has no information about the size of the destination buffer. All source strings are static and shorter than the 32-byte destination arrays (the longest one is 20 characters), so this is safe today, but the safety should not depend on the contents of the name tables. The earlier strcpy() to strscpy() conversions skipped this function because dst is a plain pointer. Pass the destination size to copy_string() and use strscpy() and snprintf() instead. No functional changes intended. Testing: compile-tested only with clang (W=1) and sparse (C=1). The function is reached only through the SNDRV_EMU10K1_IOCTL_INFO ioctl of the EMU10K1 hardware, which is not available under QEMU. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Andrey Zakharov Link: https://patch.msgid.link/20261023105027.53135-1-dedemorow@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emufx.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index a33817aba17f..d49866d9961e 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -2423,12 +2423,12 @@ static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file) return 0; } -static void copy_string(char *dst, const char *src, const char *null, int idx) +static void copy_string(char *dst, size_t size, const char *src, const char *null, int idx) { if (src == NULL) - sprintf(dst, "%s %02X", null, idx); + snprintf(dst, size, "%s %02X", null, idx); else - strcpy(dst, src); + strscpy(dst, src, size); } static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, @@ -2446,12 +2446,16 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask; extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask; for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { - copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res); - copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); - copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + copy_string(info->fxbus_names[res], sizeof(info->fxbus_names[res]), + *fxbus, "FXBUS", res); + copy_string(info->extin_names[res], sizeof(info->extin_names[res]), + extin_mask & (1 << res) ? *extin : NULL, "Unused", res); + copy_string(info->extout_names[res], sizeof(info->extout_names[res]), + extout_mask & (1 << res) ? *extout : NULL, "Unused", res); } for (res = 16; res < 32; res++, extout++) - copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); + copy_string(info->extout_names[res], sizeof(info->extout_names[res]), + extout_mask & (1 << res) ? *extout : NULL, "Unused", res); info->gpr_controls = emu->fx8010.gpr_count; } -- cgit v1.2.3 From d74eb09af56beda9cbe310e9f6554ff1f35f280f Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 22 Oct 2026 18:58:20 +0800 Subject: ALSA: pcm: Drain proc callbacks at device disconnect Disconnecting an individual PCM leaves its proc files accessible until snd_pcm_free_stream(). HDA codec-only sysfs unbind can take this path while the controller and card remain registered. An open proc file pins the card module, but does not hold a codec PCM reference. With CONFIG_SND_VERBOSE_PROCFS, a prealloc write is buffered until file release. Without an attached PCM runtime, its release can replace the DMA buffer after final PCM cleanup has already freed the preallocation: HDA codec unbind prealloc file release ---------------- --------------------- hda_codec_driver_remove() snd_hda_codec_disconnect_pcms() snd_device_disconnect() snd_pcm_dev_disconnect() /* proc nodes remain */ snd_hda_codec_cleanup_for_unbind() codec_release_pcms() snd_device_free() snd_pcm_free() free preallocated buffer snd_info_text_entry_release() snd_pcm_lib_preallocate_proc_write() replace DMA buffer remove stream proc nodes free substream The later proc rundown can itself invoke this buffered write and leave the replacement allocation without a final free. A concurrent release can also overlap the earlier buffer cleanup. Extract snd_info_disconnect_entry() from snd_info_free_entry() to remove proc nodes and drain callbacks without freeing the info entry objects. Use it for both stream directions at PCM disconnect. Finish the existing PCM state and device disconnection under open_mutex, then drop that lock before proc rundown: even an otherwise idle proc file can run its buffered release callback, which takes open_mutex, during proc_remove(). Keep entry ownership and final cleanup unchanged, including the saved OSS child entries. As with existing info entry teardown, callers must serialize registration with disconnection and release. Suggested-by: Takashi Iwai Assisted-by: LLM Signed-off-by: Cen Zhang Link: https://patch.msgid.link/pm-pmb-2439c851f82c443c3600-v7-2774a482b7f03e1fef60@gmail.com Signed-off-by: Takashi Iwai --- include/sound/info.h | 2 ++ sound/core/info.c | 23 ++++++++++++++++----- sound/core/pcm.c | 56 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/include/sound/info.h b/include/sound/info.h index adbc506860d6..f216291e5c41 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -117,6 +117,7 @@ struct snd_info_entry *snd_info_create_module_entry(struct module *module, struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent); +void snd_info_disconnect_entry(struct snd_info_entry *entry); void snd_info_free_entry(struct snd_info_entry *entry); int snd_info_card_create(struct snd_card *card); @@ -164,6 +165,7 @@ static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line, static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; } static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; } static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; } +static inline void snd_info_disconnect_entry(struct snd_info_entry *entry) { } static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; } static inline int snd_info_card_create(struct snd_card *card) { return 0; } diff --git a/sound/core/info.c b/sound/core/info.c index c4d57d0809ad..023b4736aa6d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -737,6 +737,23 @@ static void snd_info_clear_entries(struct snd_info_entry *entry) entry->p = NULL; } +/** + * snd_info_disconnect_entry - remove proc nodes and drain their callbacks + * @entry: root of the info entry subtree + * + * Retains the info entries for snd_info_free_entry(). Callers must serialize + * registration and destruction, and must not hold locks needed by callbacks. + */ +void snd_info_disconnect_entry(struct snd_info_entry *entry) +{ + if (entry && entry->p) { + proc_remove(entry->p); + guard(mutex)(&info_mutex); + snd_info_clear_entries(entry); + } +} +EXPORT_SYMBOL(snd_info_disconnect_entry); + /** * snd_info_free_entry - release the info entry * @entry: the info entry @@ -749,11 +766,7 @@ void snd_info_free_entry(struct snd_info_entry * entry) if (!entry) return; - if (entry->p) { - proc_remove(entry->p); - guard(mutex)(&info_mutex); - snd_info_clear_entries(entry); - } + snd_info_disconnect_entry(entry); /* free all children at first */ list_for_each_entry_safe(p, n, &entry->children, list) diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 1e4e0ecb3dbd..064df4cd3b50 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -520,6 +520,11 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) return 0; } +static void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) +{ + snd_info_disconnect_entry(pstr->proc_root); +} + static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { snd_info_free_entry(pstr->proc_root); @@ -580,6 +585,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) #else /* !CONFIG_SND_VERBOSE_PROCFS */ static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } +static inline void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) { } static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } #endif /* CONFIG_SND_VERBOSE_PROCFS */ @@ -1094,33 +1100,37 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) int cidx; guard(mutex)(®ister_mutex); - guard(mutex)(&pcm->open_mutex); - wake_up(&pcm->open_wait); - list_del_init(&pcm->list); - - for_each_pcm_substream(pcm, cidx, substream) { - snd_pcm_stream_lock_irq(substream); - if (substream->runtime) { - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - /* to be sure, set the state unconditionally */ - __snd_pcm_set_state(substream->runtime, - SNDRV_PCM_STATE_DISCONNECTED); - wake_up(&substream->runtime->sleep); - wake_up(&substream->runtime->tsleep); + scoped_guard(mutex, &pcm->open_mutex) { + wake_up(&pcm->open_wait); + list_del_init(&pcm->list); + + for_each_pcm_substream(pcm, cidx, substream) { + snd_pcm_stream_lock_irq(substream); + if (substream->runtime) { + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + /* to be sure, set the state unconditionally */ + __snd_pcm_set_state(substream->runtime, + SNDRV_PCM_STATE_DISCONNECTED); + wake_up(&substream->runtime->sleep); + wake_up(&substream->runtime->tsleep); + } + snd_pcm_stream_unlock_irq(substream); } - snd_pcm_stream_unlock_irq(substream); - } - for_each_pcm_substream(pcm, cidx, substream) - snd_pcm_sync_stop(substream, false); + for_each_pcm_substream(pcm, cidx, substream) + snd_pcm_sync_stop(substream, false); - pcm_call_notify(pcm, n_disconnect); - for (cidx = 0; cidx < 2; cidx++) { - if (pcm->streams[cidx].dev) - snd_unregister_device(pcm->streams[cidx].dev); - free_chmap(&pcm->streams[cidx]); + pcm_call_notify(pcm, n_disconnect); + for (cidx = 0; cidx < 2; cidx++) { + if (pcm->streams[cidx].dev) + snd_unregister_device(pcm->streams[cidx].dev); + free_chmap(&pcm->streams[cidx]); + } } + /* Proc rundown can run release callbacks which take open_mutex. */ + for (cidx = 0; cidx < 2; cidx++) + snd_pcm_stream_proc_disconnect(&pcm->streams[cidx]); return 0; } -- cgit v1.2.3 From 73e38d178bbd28fe2c30443e6c0cf74e3f584bdb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 24 Oct 2026 10:00:10 +0200 Subject: ALSA: usb-audio: Correct a typo in UMC404HD model comment The recently added quirk code has a comment for 1397:0509 as if it were UMC204HD, but it's UMC404HD. Correct it. Fixes: e88f4749c02f ("ALSA: usb-audio: Avoid applying boot quirk for Behringer UMC204HD") Suggested-by: Gergo Koteles Link: https://patch.msgid.link/20261024080011.2258391-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 6aa212c2cab6..b6be3a80d9c9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1794,7 +1794,7 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, /* the quirk seems breaking on some Behringer devices */ switch (id) { case USB_ID(0x1397, 0x0508): /* UMC204HD */ - case USB_ID(0x1397, 0x0509): /* UMC204HD */ + case USB_ID(0x1397, 0x0509): /* UMC404HD */ return 0; } return snd_usb_cm1a_boot_quirk(dev); -- cgit v1.2.3 From 9b30046de03acb3b3f2135d45af6be7ee0b565b6 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 24 Oct 2026 16:02:47 +0800 Subject: ALSA: ump: Drain sequencer callbacks before freeing their client seq_ump_client_free() cancels group_notify_work while the endpoint still publishes seq_ops and seq_client. A received Function Block update can enter the sequencer callback after cancellation and queue the work again. Even disabling the work cannot protect a callback that already loaded seq_client and then resumes after the client has been freed. A USB UMP endpoint can continue receiving while its sequencer binding is removed. The following order uses the same endpoint and client: Sequencer binding removal UMP input completion ------------------------- -------------------- cancel_work_sync() load seq_ops and seq_client delete sequencer client clear endpoint binding kfree(client) schedule group_notify_work read client->seq_client Cover all calls through seq_ops with SRCU in the UMP core. Withdraw the operations pointer and wait for those callbacks before canceling work and deleting the client. No producer can then queue work after the final cancellation, and both the client and callback code remain available until the last in-flight callback returns. Publish seq_ops with release/acquire ordering so the separately stored seq_client is initialized before any callback uses it. Use SRCU because sequencer notification delivery can sleep; no callback needs to change its existing execution context or allocation behavior. Fixes: 4a16a3af0571 ("ALSA: seq: ump: Handle FB info update") Assisted-by: LLM Signed-off-by: Cen Zhang Link: https://patch.msgid.link/pm-pmb-00625126b81966d1bf1d-v2-915f6bbabe1fd458c3ab@gmail.com Signed-off-by: Takashi Iwai --- include/sound/ump.h | 5 ++++ sound/core/seq/seq_ump_client.c | 5 ++-- sound/core/ump.c | 59 +++++++++++++++++++++++++++++++++++------ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/include/sound/ump.h b/include/sound/ump.h index 73f97f88e2ed..387143783a9b 100644 --- a/include/sound/ump.h +++ b/include/sound/ump.h @@ -66,6 +66,7 @@ struct snd_ump_endpoint { #if IS_ENABLED(CONFIG_SND_SEQUENCER) struct snd_seq_device *seq_dev; + /* Published after seq_client; calls are covered by UMP core SRCU. */ const struct snd_seq_ump_ops *seq_ops; void *seq_client; #endif @@ -126,6 +127,10 @@ int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val); int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol); void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump); +#if IS_ENABLED(CONFIG_SND_SEQUENCER) +void snd_ump_detach_seq(struct snd_ump_endpoint *ump); +#endif + /* * Some definitions for UMP */ diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 4c1e81db376f..ca9dcec19883 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -353,12 +353,12 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) /* release the client resources */ static void seq_ump_client_free(struct seq_ump_client *client) { + snd_ump_detach_seq(client->ump); cancel_work_sync(&client->group_notify_work); if (client->seq_client >= 0) snd_seq_delete_kernel_client(client->seq_client); - client->ump->seq_ops = NULL; client->ump->seq_client = NULL; kfree(client); @@ -523,7 +523,8 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) snd_seq_kernel_client_put(cptr); ump->seq_client = client; - ump->seq_ops = &seq_ump_ops; + /* Publish the initialized client before callbacks can observe seq_ops. */ + smp_store_release(&ump->seq_ops, &seq_ump_ops); return 0; error: diff --git a/sound/core/ump.c b/sound/core/ump.c index 04ef66298943..d5a5322082f1 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,31 @@ #define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args) #define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) +DEFINE_STATIC_SRCU(ump_seq_srcu); + +static const struct snd_seq_ump_ops *seq_ops_load(struct snd_ump_endpoint *ump) +{ + /* Pair with publication after seq_client has been initialized. */ + return smp_load_acquire(&ump->seq_ops); +} + +/** + * snd_ump_detach_seq - stop callbacks to a sequencer binding + * @ump: UMP endpoint + * + * Withdraw the binding and wait for all callbacks already in progress. + * The caller can then drain binding work and free the sequencer client. + * Must not be called from one of the binding callbacks. + */ +void snd_ump_detach_seq(struct snd_ump_endpoint *ump) +{ + WRITE_ONCE(ump->seq_ops, NULL); + synchronize_srcu(&ump_seq_srcu); +} +EXPORT_SYMBOL_GPL(snd_ump_detach_seq); +#endif + static int snd_ump_dev_register(struct snd_rawmidi *rmidi); static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi); static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd, @@ -348,8 +374,13 @@ int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count) continue; ump_handle_stream_msg(ump, ump->input_buf, n); #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops) - ump->seq_ops->input_receive(ump, ump->input_buf, n); + scoped_guard(srcu, &ump_seq_srcu) { + const struct snd_seq_ump_ops *ops; + + ops = seq_ops_load(ump); + if (ops) + ops->input_receive(ump, ump->input_buf, n); + } #endif process_legacy_input(ump, ump->input_buf, n); } @@ -701,8 +732,12 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump) static void seq_notify_ep_change(struct snd_ump_endpoint *ump) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change) - ump->seq_ops->notify_ep_change(ump); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ump->parsed && ops && ops->notify_ep_change) + ops->notify_ep_change(ump); #endif } @@ -799,8 +834,12 @@ static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump, static void seq_notify_protocol(struct snd_ump_endpoint *ump) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops && ump->seq_ops->switch_protocol) - ump->seq_ops->switch_protocol(ump); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ops && ops->switch_protocol) + ops->switch_protocol(ump); #endif /* CONFIG_SND_SEQUENCER */ } @@ -895,8 +934,12 @@ static void seq_notify_fb_change(struct snd_ump_endpoint *ump, struct snd_ump_block *fb) { #if IS_ENABLED(CONFIG_SND_SEQUENCER) - if (ump->seq_ops && ump->seq_ops->notify_fb_change) - ump->seq_ops->notify_fb_change(ump, fb); + const struct snd_seq_ump_ops *ops; + + guard(srcu)(&ump_seq_srcu); + ops = seq_ops_load(ump); + if (ops && ops->notify_fb_change) + ops->notify_fb_change(ump, fb); #endif } -- cgit v1.2.3 From a71cef6f641152c7c7bbc17d86f33a3b45cd10a9 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:49 +0800 Subject: ALSA: opl3: Initialize the timer before publishing a sequencer port snd_opl3_seq_probe() initializes the delayed-note timer and its lock after snd_opl3_synth_create_port() has made the native port reachable. A direct NOTEON does not require a subscription, and a fixed-duration instrument makes it enter snd_opl3_start_timer(). On sequencer-driver rebind, the card-owned instrument bank remains available. An event delivered as soon as the port appears can race the late initialization: Sequencer driver probe Direct NOTEON ---------------------- ------------- snd_opl3_synth_create_port() snd_seq_event_port_attach() publish port snd_opl3_synth_event_input() snd_opl3_note_on() snd_opl3_start_timer() use tlist and sys_timer_lock timer_setup(tlist) spin_lock_init(sys_timer_lock) sys_timer_status = 0 Initialize the timer, its lock and its status before creating the client and attaching either port. No event can then reach partially initialized timer state or have its newly armed timer reinitialized by the probe. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-1@gmail.com --- sound/drivers/opl3/opl3_seq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index b77d8e8f12fb..2bacefc3201d 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -214,6 +214,11 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev) spin_lock_init(&opl3->voice_lock); + /* Initialize the timer before an attached port can receive events. */ + timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); + spin_lock_init(&opl3->sys_timer_lock); + opl3->sys_timer_status = 0; + opl3->seq_client = -1; /* allocate new client */ @@ -232,11 +237,6 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev) return err; } - /* setup system timer */ - timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); - spin_lock_init(&opl3->sys_timer_lock); - opl3->sys_timer_status = 0; - #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_opl3_init_seq_oss(opl3, name); #endif -- cgit v1.2.3 From 02b1d5127f37683448ef35798b3a6310d1d38761 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:50 +0800 Subject: ALSA: opl3: Clear voice channels before freeing their sets The card-owned OPL3 voice array survives sequencer-driver unbind, but its chan pointers refer to the native or OSS port's channel set. Neither port's private_free callback clears those pointers before freeing its set. A direct NOTEON in the initial SYNTH mode can leave a voice allocated without a subscription. Even with fix_dur zero and no timer, unbind and rebind followed by direct PITCHBEND can read the old channel: Sequencer driver unbind/rebind Direct PITCHBEND ----------------------------- ---------------- snd_seq_delete_kernel_client() port_free() free channel set snd_opl3_seq_probe() allocate new channel set snd_opl3_pitch_ctrl() snd_opl3_update_pitch() old voice->chan is non-NULL snd_opl3_calc_pitch() read freed channel Clearing only note_off_check or the voice state does not protect this path: snd_opl3_update_pitch() tests the channel pointer itself. Key off active voices before clearing their channel pointers, delayed- note flags and state under voice_lock. Direct events need no subscription, so port deletion need not call synth_unuse() to reset the hardware. Merely marking a keyed-on voice off would retain hardware KEYON and cause a later note to miss the key-on transition. Reuse snd_opl3_kill_voice() before either channel set is freed. Visit higher-numbered voices first. In initial SYNTH mode, direct NOTEON events map channels to fixed voices without reserving them through the allocator: a 4-op primary can coexist with an independently keyed-on 2-op partner at voice + 3. Killing the primary clears that partner's software state without keying off its register. Ascending cleanup would then skip the partner. Retiring the partner first guarantees its key-off before the primary can change its state, on both register banks. The lock also excludes a timer or pitch update already using a voice. Both private_free callbacks must do the cleanup because an in-flight OSS event can refill the shared voice array after the native port has been drained and freed. That event can only reference its own still-live set; the OSS callback's final cleanup retires that reference before freeing its set as well. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-2@gmail.com --- sound/drivers/opl3/opl3_midi.c | 15 +++++++++++++++ sound/drivers/opl3/opl3_oss.c | 1 + sound/drivers/opl3/opl3_seq.c | 1 + sound/drivers/opl3/opl3_voice.h | 1 + 4 files changed, 18 insertions(+) diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 6d3c5b5a35ff..de611214cdac 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -632,6 +632,21 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice) } +void snd_opl3_clear_voices(struct snd_opl3 *opl3) +{ + int i; + + guard(spinlock_irqsave)(&opl3->voice_lock); + /* A 4-op primary clears its higher-numbered partner's state. */ + for (i = MAX_OPL3_VOICES - 1; i >= 0; i--) { + if (opl3->voices[i].state > 0) + snd_opl3_kill_voice(opl3, i); + opl3->voices[i].chan = NULL; + opl3->voices[i].note_off_check = 0; + opl3->voices[i].state = SNDRV_OPL3_ST_OFF; + } +} + /* * Release a note in response to a midi note off. */ diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index 6d39b2b77b80..1f01d27955fe 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -41,6 +41,7 @@ static void snd_opl3_oss_free_port(void *private_data) { struct snd_opl3 *opl3 = private_data; + snd_opl3_clear_voices(opl3); snd_midi_channel_free_set(opl3->oss_chset); } diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index 2bacefc3201d..e03c88377bb6 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -152,6 +152,7 @@ static void snd_opl3_synth_free_port(void *private_data) { struct snd_opl3 *opl3 = private_data; + snd_opl3_clear_voices(opl3); snd_midi_channel_free_set(opl3->chset); } diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h index be9ccca2d952..aa81512d23c9 100644 --- a/sound/drivers/opl3/opl3_voice.h +++ b/sound/drivers/opl3/opl3_voice.h @@ -13,6 +13,7 @@ int snd_opl3_synth_use_inc(struct snd_opl3 * opl3); void snd_opl3_synth_use_dec(struct snd_opl3 * opl3); int snd_opl3_synth_setup(struct snd_opl3 * opl3); void snd_opl3_synth_cleanup(struct snd_opl3 * opl3); +void snd_opl3_clear_voices(struct snd_opl3 *opl3); /* Prototypes for opl3_midi.c */ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); -- cgit v1.2.3 From 9194d8dd4196fa3fb457de2909df468d501c01c2 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 25 Oct 2026 00:49:51 +0800 Subject: ALSA: opl3: Shut down the timer after draining sequencer ports The delayed-note timer is independent of the sequencer ports' use-locks and runs code in snd-opl3-synth. Deleting the client drains the event callbacks but does not cancel this timer. A direct fixed-duration NOTEON without a subscription can arm it without any unuse callback to stop it. A shutdown in only the native port's private_free callback is too early to finish shared voice cleanup when the OSS port is enabled: Sequencer client deletion In-flight OSS NOTEON ------------------------- ------------------- drain native port clear native voice aliases snd_opl3_oss_event_input() refill shared voice array drain OSS port clear voice aliases again free OSS channel set client deletion returns The per-port voice cleanup prevents the timer from dereferencing a freed channel during that sequence, but a queued or running timer can still outlive client deletion and the sequencer driver itself. Shut the timer down synchronously after snd_seq_delete_kernel_client() has drained both ports. No event can then start it again, and shutdown waits for a callback already running and prevents callback self-rearming. Do this outside voice_lock and sys_timer_lock, which the callback takes. The probe initializes timer state before publishing ports on rebind. Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/pm-pmb-4552c4e605507d8d9b16-v9-70247b8436e10e26d324-3@gmail.com --- sound/drivers/opl3/opl3_seq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index e03c88377bb6..1004a7df1b8c 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -259,6 +259,8 @@ static void snd_opl3_seq_remove(struct snd_seq_device *dev) snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } + /* Stop the shared timer after all event callbacks have been drained. */ + timer_shutdown_sync(&opl3->tlist); } static struct snd_seq_driver opl3_seq_driver = { -- cgit v1.2.3 From 6b186281a11194be8349d6b14c3d605efcfaf037 Mon Sep 17 00:00:00 2001 From: Denis Kulikov Date: Mon, 26 Oct 2026 02:18:49 +0300 Subject: ALSA: usb-audio: Rate-limit URB submission error at stream start When two USB audio devices share a USB 2.0 hub (here a headset and a microphone), there may not be enough isochronous bandwidth to start the second stream, and usb_submit_urb() fails with -ENOSPC. PipeWire keeps retrying to start the stream every ~20 ms, and every attempt logs usb 2-1.1: cannot submit urb 0, error -28: not enough bandwidth On my desktop this was printed up to 94 times per second and flooded the kernel log. Use usb_audio_err_ratelimited() for this message, as commit 2db2be5607c4 ("ALSA: usb-audio: Rate limit usb_set_interface error reporting") already did for a neighbouring message in the same file. Tested on that desktop (X79, Linux 7.3) with the headset and the microphone on one hub, starting playback and capture on both for 20 seconds: the stock snd-usb-audio module printed the message 580 times, the patched one printed it 50 times and suppressed the rest. Signed-off-by: Denis Kulikov Link: https://patch.msgid.link/20261025231849.148149-1-dar1knessx@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 9c844c55ad3f..8dd0b3525e4b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1642,9 +1642,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) err = -ENODEV; if (err < 0) { if (!atomic_read(&ep->chip->shutdown)) - usb_audio_err(ep->chip, - "cannot submit urb %d, error %d: %s\n", - i, err, usb_error_string(err)); + usb_audio_err_ratelimited(ep->chip, + "cannot submit urb %d, error %d: %s\n", + i, err, usb_error_string(err)); goto __error; } set_bit(i, &ep->active_mask); -- cgit v1.2.3 From 5deb9c57ba93d28a52871a96f3d8794d2cbed035 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Mon, 26 Oct 2026 12:16:55 +0900 Subject: ALSA: pcmtest: Reject invalid fill_mode values The fill_block() function handles only fill_mode 0 (random) and 1 (pattern). With any other value it writes nothing and does not move the buffer position. A capture stream then gets no data, and a read fails with -EIO after the timeout. Nothing in the kernel log says why. Check the value in a set callback, so a bad value is rejected both when the module is loaded and when the parameter is written. Fixes: 315a3d57c64c ("ALSA: Implement the new Virtual PCM Test Driver") Assisted-by: Claude:claude-opus-5-5 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20261026031655.2699561-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/drivers/pcmtest.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index d1b0dff56cd9..422fdb10f237 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -72,13 +72,32 @@ static u8 playback_capture_test; static u8 ioctl_reset_test; static struct dentry *driver_debug_dir; +static int fill_mode_set(const char *val, const struct kernel_param *kp) +{ + short mode; + int err; + + err = kstrtos16(val, 0, &mode); + if (err) + return err; + if (mode != FILL_MODE_RAND && mode != FILL_MODE_PAT) + return -EINVAL; + fill_mode = mode; + return 0; +} + +static const struct kernel_param_ops fill_mode_ops = { + .set = fill_mode_set, + .get = param_get_short, +}; + module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for pcmtest soundcard"); module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for pcmtest soundcard"); module_param(enable, bool, 0444); MODULE_PARM_DESC(enable, "Enable pcmtest soundcard."); -module_param(fill_mode, short, 0600); +module_param_cb(fill_mode, &fill_mode_ops, &fill_mode, 0600); MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)"); module_param(inject_delay, int, 0600); MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)"); -- cgit v1.2.3 From 06be918062adc1a5a76dc346803b136b20bdb5ae Mon Sep 17 00:00:00 2001 From: Sinan Turunc Date: Mon, 26 Oct 2026 22:36:39 +0000 Subject: ALSA: usb-audio: Bound packet copies by the PCM buffer size retire_capture_urb() and prepare_playback_urb() assume that one USB packet never carries more data than the whole ALSA ring buffer. Nothing enforces that: the packet size comes from the device (wMaxPacketSize, and curpacksize == maxpacksize when the class-specific endpoint descriptor sets MaxPacketsOnly), while the PCM hardware constraints only require 64-byte periods, two periods and a period time of one data interval. An 8 kHz mono U8 stream on a 128-byte buffer is legal, and a full-speed device can send 1023-byte packets into it. On the first oversized packet hwptr_done is wrapped once and stays beyond buffer_bytes (895 for 1023 bytes into 128). On the next packet the wrap arithmetic in the copy path computes bytes1 = subs->buffer_bytes - oldptr with oldptr > buffer_bytes, so bytes1 wraps to about 4 GB and memcpy() runs off the end of the URB buffer and of the vmalloc'd DMA buffer: BUG: KASAN: slab-out-of-bounds in retire_capture_urb+0x251/0x870 Read of size 4294966529 at addr ffff88800c1e2800 by task vhci_rx/82 and for playback, where copy_to_urb() has the same shape: BUG: KASAN: vmalloc-out-of-bounds in copy_to_urb.isra.0+0x188/0x430 Read of size 4294966529 at addr ffffc9000004537f by task vhci_rx/79 prepare_playback_urb+0x98a/0x1fc0 Truncate an oversized capture packet to the buffer size, and stop filling a playback URB once it holds a whole buffer. With bytes <= buffer_bytes the single wrap of hwptr_done and the two-part memcpy() in both copy paths stay inside the buffer. A device whose packets do not fit the buffer at all was never going to play correctly anyway. Reproduced on 7.3-rc7 with a USB/IP device (dummy_hcd cannot carry isochronous data) advertising a UAC1 endpoint with MaxPacketsOnly and wMaxPacketSize 1023, and a PCM client using period_size 64, periods 2. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sinan Turunc Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20261026223543.620-2-enscrpt@pm.me --- sound/usb/pcm.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 682b6c1fe76b..c2f7b08607ac 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1343,6 +1343,18 @@ static void retire_capture_urb(struct snd_usb_substream *subs, bytes -= adj; subs->stream_offset_adj -= adj; } + /* + * A packet can be as large as the endpoint's maxpacksize, which + * the device chooses, while the ring buffer can be as small as + * two 64-byte periods. The hwptr_done wrap below handles one + * wrap only, so never take more than one buffer from a packet. + */ + if (unlikely(bytes > subs->buffer_bytes)) { + dev_warn_ratelimited(&subs->dev->dev, + "%u-byte packet exceeds %u-byte buffer, truncating\n", + bytes, subs->buffer_bytes); + bytes = subs->buffer_bytes; + } frames = bytes / stride; if (!subs->txfr_quirk) bytes = frames * stride; @@ -1553,8 +1565,14 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, for (i = 0; i < ctx->packets; i++) { counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail); + /* + * Bound by the ring buffer as well as by the URB: + * copy_to_urb() wraps hwptr_done once, so one URB must + * never carry more than the whole buffer. + */ if (counts < 0 || - (frames + counts) * stride > ctx->buffer_size) + (frames + counts) * stride > ctx->buffer_size || + (frames + counts) * stride > subs->buffer_bytes) break; /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; -- cgit v1.2.3 From 493a77a9f20fca07ca5654f61c1b03bef035df25 Mon Sep 17 00:00:00 2001 From: Sinan Turunc Date: Mon, 26 Oct 2026 22:36:47 +0000 Subject: ALSA: ua101: Bound packet copies by the PCM buffer size copy_capture_data() and copy_playback_data() wrap the ring buffer position once and split the memcpy() in two at the buffer end, which is only correct when one packet holds at most buffer_size frames. The capture packet size is the device's (actual_length up to wMaxPacketSize, divided by 3 * bNrChannels from its own descriptor), the playback packet size is taken from the capture packets, and set_stream_hw() lets the PCM client pick period_bytes_min 1, periods_min 2 and a period time of 1.5 packet intervals: 9 frames at 44.1 kHz high speed, an 18-frame buffer. A device sending 672-byte mono 24-bit packets delivers 224 frames per packet. After the first packet buffer_pos is 206 and stays beyond buffer_size. On the next packet frames1 = buffer_size - buffer_pos wraps and memcpy() runs away: snd-ua101 2-1:1.0: invalid position: , pos = 206, buffer size = 18, period size = 9 BUG: KASAN: slab-out-of-bounds in capture_urb_complete+0x9eb/0xcb0 Read of size 4294966732 at addr ffff88800d73e7e0 by task vhci_rx/81 and for playback: BUG: KASAN: vmalloc-out-of-bounds in playback_work+0x789/0xac0 Read of size 4294966732 at addr ffffc9000002626a by task kworker/0:1H/80 Clamp the capture copy to buffer_size. On playback, copy at most one buffer and fill the rest of the packet with silence, so the URB length, the delay accounting and the data sent stay consistent. With that bound the single wrap and both memcpy() halves stay inside the buffer. A device whose packets do not fit the buffer never played correctly anyway. Reproduced on 7.3-rc7 with a USB/IP device presenting the UA-101 interface layout and a 672-byte isochronous endpoint. Fixes: 63978ab3e3e9 ("sound: add Edirol UA-101 support") Cc: stable@vger.kernel.org Signed-off-by: Sinan Turunc Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20261026223543.620-3-enscrpt@pm.me --- sound/usb/misc/ua101.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 58d8a3cdc8b9..bd8e0ed0ba5d 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -285,13 +285,29 @@ static void playback_work(struct work_struct *work) /* fill packet with data or silence */ urb->iso_frame_desc[0].length = frames * ua->playback.frame_bytes; - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) { + /* + * The packet size comes from the capture stream, + * i.e. from the device, while the ring buffer can + * be as small as two tiny periods. The single wrap + * in copy_playback_data() is only valid for + * frames <= buffer_size, so copy at most one + * buffer and send silence for the rest. + */ + unsigned int copy_frames = min(frames, + ua->playback.substream->runtime->buffer_size); + do_period_elapsed |= copy_playback_data(&ua->playback, urb, - frames); - else + copy_frames); + memset(urb->transfer_buffer + + copy_frames * ua->playback.frame_bytes, 0, + (frames - copy_frames) * + ua->playback.frame_bytes); + } else { memset(urb->transfer_buffer, 0, urb->iso_frame_desc[0].length); + } /* and off you go ... */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -320,6 +336,13 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, runtime = stream->substream->runtime; frame_bytes = stream->frame_bytes; + /* + * A packet can carry up to max_packet_bytes, the device's + * wMaxPacketSize, which may exceed the whole ring buffer; keep what + * fits so the single wrap below stays valid. + */ + if (unlikely(frames > runtime->buffer_size)) + frames = runtime->buffer_size; dest = runtime->dma_area + stream->buffer_pos * frame_bytes; if (stream->buffer_pos + frames <= runtime->buffer_size) { memcpy(dest, urb->transfer_buffer, frames * frame_bytes); -- cgit v1.2.3 From fe3b45a0875a8b0944d2adee3ca62283d876edc0 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 27 Oct 2026 10:22:27 +0900 Subject: ALSA: pcmtest: Honor the enable parameter The enable module parameter is declared and documented as "Enable pcmtest soundcard.", but nothing reads it. Loading the module with enable=0 still creates the card. Return -ENODEV from mod_init() when enable is false, as the pcsp driver does. Fixes: 315a3d57c64c ("ALSA: Implement the new Virtual PCM Test Driver") Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20261027012228.3797565-1-sammiee5311@gmail.com Signed-off-by: Takashi Iwai --- sound/drivers/pcmtest.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 422fdb10f237..66bee8a2ce34 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -712,6 +712,9 @@ static int __init mod_init(void) { int err = 0; + if (!enable) + return -ENODEV; + buf_allocated = setup_patt_bufs(); if (!buf_allocated) return -ENOMEM; -- cgit v1.2.3 From 8d61d55bedb7e1ee553ec3f43a9dc0feecb9da3c Mon Sep 17 00:00:00 2001 From: Ernst Persson Date: Tue, 27 Oct 2026 07:13:44 +0100 Subject: ALSA: hda/realtek: Extend quirk for HP Pavilion All-in-One 27-ca1xxx A quirk was added in commit 704de5697008 ("ALSA: hda/realtek: Add quirk for HP Pavilion All-in-One 27-ca1xxx") Also enable the quirk for the actual 89e9 model named there. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216139 Signed-off-by: Ernst Persson Link: https://patch.msgid.link/20261027-realtek-alc274-v1-1-c0eae15d0c55@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 1 + sound/hda/controllers/intel.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c499165b80e9..23329d5887a5 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7876,6 +7876,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), + SND_PCI_QUIRK(0x103c, 0x89e9, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index af01b2669c49..29679fe7f401 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2330,6 +2330,7 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */ SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0), + SND_PCI_QUIRK(0x103c, 0x89e9, "HP 89E9", 0), {} }; -- cgit v1.2.3 From 379ba557f555245eb9cb6c2a9d7a1eef704c786f Mon Sep 17 00:00:00 2001 From: Zak Brighton Knight Date: Mon, 26 Oct 2026 14:09:34 +0000 Subject: ALSA: usb-audio: Skip probe-time interface setup for MSI MAG X570S ALC4080 The Realtek ALC4080 USB audio on the MSI MAG X570S Tomahawk Max WiFi (USB ID 0db0:a073, the same ID as the MAG X570S Torpedo Max mixer map) has six streaming interfaces with 68 altsettings in total. For each of them, __snd_usb_parse_audio_interface() switches the interface to alt 0, initialises pitch and the sample rate and switches back, which costs about 64 ms per altsetting here, mostly spent inside usb_set_interface(). Probing the card therefore takes about 6.4 seconds, on every boot. That setup is redundant with snd_usb_endpoint_prepare() at stream-open time, so set QUIRK_FLAG_SKIP_IFACE_SETUP for this device. Tested on 7.2.11 with the equivalent quirk_flags=0db0:a073:skip_iface_setup module option, re-probing the device via its sysfs "authorized" attribute: probe time went from 6.45 s to 0.57 s, all 68 altsettings are still listed in /proc/asound/cardX/stream*, and playback and capture work. Assisted-by: LLM Signed-off-by: Zak Brighton Knight Link: https://patch.msgid.link/20261026140934.3575940-1-zakbrightonknight@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index b6be3a80d9c9..4ec1f2a0b359 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2661,6 +2661,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0d8c, 0x0102, /* C-Media CM6206 */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x0db0, 0xa073, /* MSI MAG X570S Tomahawk/Torpedo Max (ALC4080) */ + QUIRK_FLAG_SKIP_IFACE_SETUP), DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ -- cgit v1.2.3 From a09c2f3b267e47767b8937f65930567edbb88d44 Mon Sep 17 00:00:00 2001 From: Sally Marcher Date: Mon, 26 Oct 2026 14:29:34 +0100 Subject: ALSA: usb-audio: Add correct datainterval in the quirks-table for Microbook II This patch fixes playback issues on the Microbook II. The correct value was in the older driver for this device (https://github.com/manuel-rhdt/snd-microbookii/blob/dd304c097bb6a9d52276ac29c6969fa02bc7674a/audio.c#L914). Datainterval needs to be set explicitly. Fixes: a634090a0f24 ("ALSA: usb-audio: Add quirk for MOTU MicroBook II") Signed-off-by: Sally Marcher Link: https://lore.kernel.org/d5dfd044-69f6-4fc3-9884-6599d88afb55@student.tugraz.at Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 6343a50fb273..18a3e66faeec 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3005,6 +3005,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .datainterval = 1, .rate_min = 96000, .rate_max = 96000, .nr_rates = 1, @@ -3027,6 +3028,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .rates = SNDRV_PCM_RATE_96000, .ep_attr = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .datainterval = 1, .rate_min = 96000, .rate_max = 96000, .nr_rates = 1, -- cgit v1.2.3 From 31ce8c24dfc8210e80ff610b35e5465cf2066323 Mon Sep 17 00:00:00 2001 From: Shawn Rainey Date: Fri, 23 Oct 2026 08:25:11 -0400 Subject: ALSA: usb-audio: add quirk for Novation UltraNova (1235:0011) The device reports vendor-specific class on all interfaces, so the standard parser rejects it and no card is created. Add a composite quirk with fixed audio and raw-MIDI layouts. The id table entry matches interface 0 only so that the vendor control interfaces (2/3) are left unbound for userspace access. The endpoints advertise no synchronization and the device provides no feedback; both streams are locked to the USB SOF. The playback entry is marked USB_ENDPOINT_SYNC_SYNC so that generic sync-EP detection does not pair the capture data endpoint (0x82) as implicit feedback for the output stream; the capture entry needs no override, as its .ep_idx = 1 already keeps it out of sync-EP detection. Verified on hardware: audio duplex at both supported rates, raw MIDI in/out, and complete patch dumps as SysEx on the MIDI endpoints. Signed-off-by: Shawn Rainey Link: https://patch.msgid.link/20261023122513.1025684-1-rainey.shawn+kaudio@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 18a3e66faeec..4793fd1f85fe 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2359,6 +2359,69 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* + * Novation UltraNova: vendor-specific class descriptors, fixed + * audio and MIDI layouts. Only the audio and MIDI interfaces + * (0 and 1) are grabbed; the vendor control interfaces (2/3) + * are left unbound for userspace access. + */ + USB_DEVICE_INTERFACE_NUMBER(0x1235, 0x0011, 0), + QUIRK_DRIVER_INFO { + .vendor_name = "Novation", + .product_name = "UltraNova", + QUIRK_DATA_COMPOSITE { + { + QUIRK_DATA_AUDIOFORMAT(0) { + /* playback */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_SYNC, + .maxpacksize = 392, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { + QUIRK_DATA_AUDIOFORMAT(0) { + /* capture */ + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 2, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x82, + .ep_idx = 1, + .ep_attr = USB_ENDPOINT_XFER_ISOC, + .maxpacksize = 294, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { QUIRK_DATA_RAW_BYTES(1) }, + QUIRK_COMPOSITE_END + } + } +}, { USB_DEVICE(0x1235, 0x001e), QUIRK_DRIVER_INFO { -- cgit v1.2.3 From a4311e9447bbb219044db38897f95be112f46a8a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 09:28:35 +0200 Subject: ALSA: hda: Reject codec reset on ASoC (ext_ops) buses Writing to the reconfig or clear sysfs nodes of a codec handled by ASoC (SOF via hdac_hda, AVS via codecs/hda) hangs the writer. snd_hda_codec_reset() sets card->shutdown and calls device_release_driver(). With ext_ops the driver unbind unregisters the ASoC component, which unbinds and frees the whole ASoC card: - snd_card_disconnect() returns early as card->shutdown is already set, so the card's child devices are not removed and snd_card_free() waits for the card release forever. - Even without that, freeing the card removes the codec's hwdep device, which owns the sysfs node the reset was issued from, deadlocking in kernfs while waiting for the active write to finish. The legacy HDA driver keeps the card across a driver rebind, ASoC does not, so the reset cannot be supported there. Return -EOPNOTSUPP and skip the misleading "codec is being used" messages in the callers. Fixes: 6bae5ea94989 ("ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers") Cc: stable@vger.kernel.org Closes: https://bugzilla.kernel.org/show_bug.cgi?id=222182 Signed-off-by: Peter Ujfalusi Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20261027072835.4796-1-peter.ujfalusi@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/common/codec.c | 6 ++++++ sound/hda/common/sysfs.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 31e5fad78b0f..e367cc1c6e78 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1807,6 +1807,12 @@ int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; + /* ASoC: driver unbind frees the card, incl. the hwdep issuing the reset */ + if (bus->core.ext_ops) { + codec_err(codec, "Codec reset is not supported with ASoC\n"); + return -EOPNOTSUPP; + } + if (snd_hda_lock_devices(bus) < 0) return -EBUSY; diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c index bedf10b30885..bf65223fde10 100644 --- a/sound/hda/common/sysfs.c +++ b/sound/hda/common/sysfs.c @@ -117,6 +117,8 @@ static int clear_codec(struct hda_codec *codec) int err; err = snd_hda_codec_reset(codec); + if (err == -EOPNOTSUPP) + return err; if (err < 0) { codec_err(codec, "The codec is being used, can't free.\n"); return err; @@ -132,6 +134,8 @@ static int reconfig_codec(struct hda_codec *codec) CLASS(snd_hda_power, pm)(codec); codec_info(codec, "hda-codec: reconfiguring\n"); err = snd_hda_codec_reset(codec); + if (err == -EOPNOTSUPP) + return err; if (err < 0) { codec_err(codec, "The codec is being used, can't reconfigure.\n"); -- cgit v1.2.3