From 3213bf9885e3356fff12ac18b12d4c2fb4687734 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Fri, 19 Jun 2026 01:00:26 +0800 Subject: [PATCH] ALSA: usb-audio: Release components on probe errors usb_audio_probe() can create USB-audio component resources before the first interface is recorded in chip->num_interfaces. If a later probe step fails, the error path drops chip->active and frees the card directly when no interfaces have been registered. Normal disconnect first releases USB-audio components in a fixed order before the card is freed. The first-interface probe error path skipped that sequence, so partially initialized PCM, endpoint, MIDI, media, or mixer resources could be left for the card private_free path without their disconnect handling having run. Move the existing normal-disconnect component release sequence into a helper and call it from the zero-interface probe error path before snd_card_free(). Keep the normal disconnect ordering unchanged: PCM streams, endpoint resources, MIDI 1.0 resources, MIDI 2.0 resources, media device cleanup, then mixer resources. Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260618170026.192212-1-zzzccc427@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/card.c | 70 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 6a3b576fb067..b36f513dccb9 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -905,6 +905,40 @@ static int try_to_register_card(struct snd_usb_audio *chip, int ifnum) return 0; } +static void usb_audio_disconnect_components(struct snd_usb_audio *chip) +{ + struct snd_usb_stream *as; + struct snd_usb_endpoint *ep; + struct usb_mixer_interface *mixer; + struct list_head *p; + + /* release the pcm resources */ + list_for_each_entry(as, &chip->pcm_list, list) { + snd_usb_stream_disconnect(as); + } + /* release the endpoint resources */ + list_for_each_entry(ep, &chip->ep_list, list) { + snd_usb_endpoint_release(ep); + } + /* release the midi resources */ + list_for_each(p, &chip->midi_list) { + snd_usbmidi_disconnect(p); + } + snd_usb_midi_v2_disconnect_all(chip); + /* + * Nice to check quirk && quirk->shares_media_device and + * then call the snd_media_device_delete(). Don't have + * access to the quirk here. snd_media_device_delete() + * accesses mixer_list + */ + snd_media_device_delete(chip); + + /* release mixer resources */ + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_usb_mixer_disconnect(mixer); + } +} + /* * probe the active usb device * @@ -1077,8 +1111,10 @@ static int usb_audio_probe(struct usb_interface *intf, * decrement before memory is possibly returned. */ atomic_dec(&chip->active); - if (!chip->num_interfaces) + if (!chip->num_interfaces) { + usb_audio_disconnect_components(chip); snd_card_free(chip->card); + } } return err; } @@ -1091,48 +1127,18 @@ static bool __usb_audio_disconnect(struct usb_interface *intf, struct snd_usb_audio *chip, struct snd_card *card) { - struct list_head *p; - guard(mutex)(®ister_mutex); if (platform_ops && platform_ops->disconnect_cb) platform_ops->disconnect_cb(chip); if (atomic_inc_return(&chip->shutdown) == 1) { - struct snd_usb_stream *as; - struct snd_usb_endpoint *ep; - struct usb_mixer_interface *mixer; - /* wait until all pending tasks done; * they are protected by snd_usb_lock_shutdown() */ snd_refcount_sync(&chip->usage_count); snd_card_disconnect(card); - /* release the pcm resources */ - list_for_each_entry(as, &chip->pcm_list, list) { - snd_usb_stream_disconnect(as); - } - /* release the endpoint resources */ - list_for_each_entry(ep, &chip->ep_list, list) { - snd_usb_endpoint_release(ep); - } - /* release the midi resources */ - list_for_each(p, &chip->midi_list) { - snd_usbmidi_disconnect(p); - } - snd_usb_midi_v2_disconnect_all(chip); - /* - * Nice to check quirk && quirk->shares_media_device and - * then call the snd_media_device_delete(). Don't have - * access to the quirk here. snd_media_device_delete() - * accesses mixer_list - */ - snd_media_device_delete(chip); - - /* release mixer resources */ - list_for_each_entry(mixer, &chip->mixer_list, list) { - snd_usb_mixer_disconnect(mixer); - } + usb_audio_disconnect_components(chip); } if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND)