From cd17d6ff7b7d2b1dd9bcc80ae7b4a83773f918c6 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Mon, 10 Aug 2026 03:36:11 +0930 Subject: [PATCH] ALSA: scarlett2: Use a private URB for the notification endpoint scarlett2_init_notify() used mixer->urb, which snd_usb_mixer_status_create() allocates for the UAC2 status interrupt endpoint and mixer.c manages. On a device with that endpoint, the "already in use" check fires on the status URB and returns 0 for success without doing anything. No notification URB is submitted, and cmd_done is left zeroed because it is initialised past that check and nowhere else. scarlett2_usb_init() then issues SCARLETT2_USB_INIT_1 and wait_for_completion_timeout() would crash adding to the zeroed wait.head. Use a separate URB in scarlett2_data, as done for FCP, and initialise cmd_done in scarlett2_init_private(). mixer.c was also freeing the URB in snd_usb_mixer_free() and resubmitting it in snd_usb_mixer_activate(), so scarlett2 must now do both: add scarlett2_cleanup_urb(), called from private_free and private_suspend, and a private_resume callback to re-establish the URB after resume. scarlett2_init_notify() is reached from there, and the URB kill path in scarlett2_notify() completes cmd_done, leaving a stale count that would satisfy the next command's wait before the device ACKs. Use reinit_completion() to clear it. Also free the URB if the transfer buffer allocation fails, and both if usb_submit_urb() fails. Move scarlett2_init_notify() up next to scarlett2_cleanup_urb() so scarlett2_init_private() can reference it without a forward declaration. Fixes: 1b65088958ca ("ALSA: scarlett2: Implement handling of the ACK notification") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Geoffrey D. Bennett Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/ffb8ba37d5d605dfdfd8576949d67098651f9349.1786290885.git.g@b4.vu --- sound/usb/mixer.c | 6 +++ sound/usb/mixer.h | 2 + sound/usb/mixer_scarlett2.c | 98 ++++++++++++++++++++++++------------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 703c118f9d4e..5de182181ede 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3935,6 +3935,12 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer) struct usb_mixer_elem_list *list; int id, err; + if (mixer->private_resume) { + err = mixer->private_resume(mixer); + if (err < 0) + return err; + } + /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { for_each_mixer_elem(list, mixer, id) { diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 3fa1bd96f858..037b446d8b6f 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -18,6 +18,7 @@ struct usb_mixer_interface { struct usb_host_interface *hostif; struct list_head list; unsigned int ignore_ctl_error; + /* UAC2 status interrupt endpoint; owned by mixer.c */ struct urb *urb; /* array[MAX_ID_ELEMS], indexed by unit id */ struct usb_mixer_elem_list **id_elems; @@ -42,6 +43,7 @@ struct usb_mixer_interface { void *private_data; void (*private_free)(struct usb_mixer_interface *mixer); void (*private_suspend)(struct usb_mixer_interface *mixer); + int (*private_resume)(struct usb_mixer_interface *mixer); }; #define MAX_CHANNELS 64 /* max logical channels */ diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 78fb72e626ca..502854cc9f9f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -1403,6 +1403,7 @@ struct scarlett2_data { struct usb_mixer_interface *mixer; struct mutex usb_mutex; /* prevent sending concurrent USB requests */ struct completion cmd_done; + struct urb *urb; /* notification endpoint */ struct mutex data_mutex; /* lock access to this data */ u8 running; u8 hwdep_in_use; @@ -8565,13 +8566,70 @@ static void scarlett2_notify(struct urb *urb) } } -/*** Cleanup/Suspend Callbacks ***/ +/*** Notification URB and Cleanup/Suspend Callbacks ***/ + +/* Submit a URB to receive notifications from the device */ +static int scarlett2_init_notify(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + struct scarlett2_data *private = mixer->private_data; + unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); + void *transfer_buffer; + int err; + + /* Already set up */ + if (private->urb) + return 0; + + if (usb_pipe_type_check(dev, pipe)) + return -EINVAL; + + private->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!private->urb) + return -ENOMEM; + + transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); + if (!transfer_buffer) { + usb_free_urb(private->urb); + private->urb = NULL; + return -ENOMEM; + } + + usb_fill_int_urb(private->urb, dev, pipe, + transfer_buffer, private->wMaxPacketSize, + scarlett2_notify, mixer, private->bInterval); + + reinit_completion(&private->cmd_done); + + err = usb_submit_urb(private->urb, GFP_KERNEL); + if (err) { + kfree(transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; + } + + return err; +} + +static void scarlett2_cleanup_urb(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (!private->urb) + return; + + usb_kill_urb(private->urb); + kfree(private->urb->transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; +} static void scarlett2_private_free(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; cancel_delayed_work_sync(&private->work); + scarlett2_cleanup_urb(mixer); kfree(private); mixer->private_data = NULL; } @@ -8582,6 +8640,8 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) if (cancel_delayed_work_sync(&private->work)) scarlett2_config_save(private->mixer); + + scarlett2_cleanup_urb(mixer); } /*** Initialisation ***/ @@ -8701,11 +8761,13 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, mutex_init(&private->usb_mutex); mutex_init(&private->data_mutex); + init_completion(&private->cmd_done); INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); mixer->private_data = private; mixer->private_free = scarlett2_private_free; mixer->private_suspend = scarlett2_private_suspend; + mixer->private_resume = scarlett2_init_notify; private->info = entry->info; @@ -8722,40 +8784,6 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, return scarlett2_find_fc_interface(mixer->chip->dev, private); } -/* Submit a URB to receive notifications from the device */ -static int scarlett2_init_notify(struct usb_mixer_interface *mixer) -{ - struct usb_device *dev = mixer->chip->dev; - struct scarlett2_data *private = mixer->private_data; - unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); - void *transfer_buffer; - - if (mixer->urb) { - usb_audio_err(mixer->chip, - "%s: mixer urb already in use!\n", __func__); - return 0; - } - - if (usb_pipe_type_check(dev, pipe)) - return -EINVAL; - - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) - return -ENOMEM; - - transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); - if (!transfer_buffer) - return -ENOMEM; - - usb_fill_int_urb(mixer->urb, dev, pipe, - transfer_buffer, private->wMaxPacketSize, - scarlett2_notify, mixer, private->bInterval); - - init_completion(&private->cmd_done); - - return usb_submit_urb(mixer->urb, GFP_KERNEL); -} - /* Cargo cult proprietary initialisation sequence */ static int scarlett2_usb_init(struct usb_mixer_interface *mixer) {