* [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 notification endpoint
@ 2026-08-09 18:05 Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 1/2] ALSA: FCP: Use a private URB for the " Geoffrey D. Bennett
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Geoffrey D. Bennett @ 2026-08-09 18:05 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound
Hi Takashi,
Both the FCP and scarlett2 drivers used mixer->urb for their
notification endpoint URB, but that field belongs to
snd_usb_mixer_status_create() and is managed by mixer.c. Sharing it
means the "already set up" check in the driver's init_notify() can be
satisfied by the status endpoint's URB, so the driver's own URB is
never submitted and the first command would crash in
wait_for_completion_timeout() on the still-zeroed cmd_done. mixer.c
can also kill, resubmit or free the URB from under the driver.
Give each driver its own URB in its private data. For scarlett2 this
also means taking over the free/suspend/resume handling that mixer.c
was doing for it.
Tested on an ISA C8X and a Scarlett 18i20 Gen 4 (FCP), and a Scarlett
4i4 Gen 3 and 4i4 Gen 4 (scarlett2) on 7.1.7, including suspend/resume
and configuration save on suspend.
Thanks,
Geoffrey
Geoffrey D. Bennett (2):
ALSA: FCP: Use a private URB for the notification endpoint
ALSA: scarlett2: Use a private URB for the notification endpoint
sound/usb/fcp.c | 38 +++++++-------
sound/usb/mixer.c | 6 +++
sound/usb/mixer.h | 2 +
sound/usb/mixer_scarlett2.c | 98 ++++++++++++++++++++++++-------------
4 files changed, 92 insertions(+), 52 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] ALSA: FCP: Use a private URB for the notification endpoint
2026-08-09 18:05 [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 notification endpoint Geoffrey D. Bennett
@ 2026-08-09 18:06 ` Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 2/2] ALSA: scarlett2: " Geoffrey D. Bennett
2026-08-10 11:37 ` [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 " Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Geoffrey D. Bennett @ 2026-08-09 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound
fcp_init_notify() used mixer->urb, which snd_usb_mixer_status_create()
allocates for the optional UAC2 status interrupt endpoint and mixer.c
kills, resubmits and frees. On a device with that endpoint,
fcp_init_notify()'s "already set up" early return fires on the status
URB and returns success without doing anything. No FCP notification
URB is submitted, and cmd_done is left zeroed because it is
initialised past that early return and nowhere else. fcp_init() then
issues init1_opcode and wait_for_completion_timeout() would crash
adding to the zeroed wait.head. fcp_cleanup_urb() would also kill and
free mixer.c's status URB.
Use a separate URB in fcp_data, and initialise cmd_done in
fcp_init_private() where fcp_data is allocated. fcp_init_notify() is
reached again after suspend via fcp_reinit(), and the URB kill path in
fcp_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.
Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/fcp.c | 38 +++++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c
index 6f5dcd35e1d4..6c7da5508bc3 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -82,6 +82,7 @@ struct fcp_data {
struct mutex mutex; /* serialise access to the device */
struct completion cmd_done; /* wait for command completion */
struct file *file; /* hwdep file */
+ struct urb *urb; /* FCP notification endpoint */
struct fcp_notify notify;
@@ -186,7 +187,7 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
const int max_retries = 5;
int err;
- if (!mixer->urb)
+ if (!private->urb)
return -ENODEV;
struct fcp_usb_packet *req __free(kfree) = NULL;
@@ -301,7 +302,7 @@ static int fcp_reinit(struct usb_mixer_interface *mixer)
{
struct fcp_data *private = mixer->private_data;
- if (mixer->urb)
+ if (private->urb)
return 0;
void *step0_resp __free(kfree) =
@@ -893,13 +894,15 @@ static int fcp_hwdep_init(struct usb_mixer_interface *mixer)
static void fcp_cleanup_urb(struct usb_mixer_interface *mixer)
{
- if (!mixer->urb)
+ struct fcp_data *private = mixer->private_data;
+
+ if (!private->urb)
return;
- usb_kill_urb(mixer->urb);
- kfree(mixer->urb->transfer_buffer);
- usb_free_urb(mixer->urb);
- mixer->urb = NULL;
+ usb_kill_urb(private->urb);
+ kfree(private->urb->transfer_buffer);
+ usb_free_urb(private->urb);
+ private->urb = NULL;
}
static void fcp_private_free(struct usb_mixer_interface *mixer)
@@ -970,37 +973,37 @@ static int fcp_init_notify(struct usb_mixer_interface *mixer)
int err;
/* Already set up */
- if (mixer->urb)
+ if (private->urb)
return 0;
if (usb_pipe_type_check(dev, pipe))
return -EINVAL;
- mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!mixer->urb)
+ 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(mixer->urb);
- mixer->urb = NULL;
+ usb_free_urb(private->urb);
+ private->urb = NULL;
return -ENOMEM;
}
- usb_fill_int_urb(mixer->urb, dev, pipe,
+ usb_fill_int_urb(private->urb, dev, pipe,
transfer_buffer, private->wMaxPacketSize,
fcp_notify, mixer, private->bInterval);
- init_completion(&private->cmd_done);
+ reinit_completion(&private->cmd_done);
- err = usb_submit_urb(mixer->urb, GFP_KERNEL);
+ err = usb_submit_urb(private->urb, GFP_KERNEL);
if (err) {
usb_audio_err(mixer->chip,
"%s: usb_submit_urb failed: %d\n",
__func__, err);
kfree(transfer_buffer);
- usb_free_urb(mixer->urb);
- mixer->urb = NULL;
+ usb_free_urb(private->urb);
+ private->urb = NULL;
}
return err;
@@ -1051,6 +1054,7 @@ static int fcp_init_private(struct usb_mixer_interface *mixer)
return -ENOMEM;
mutex_init(&private->mutex);
+ init_completion(&private->cmd_done);
init_waitqueue_head(&private->notify.queue);
spin_lock_init(&private->notify.lock);
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] ALSA: scarlett2: Use a private URB for the notification endpoint
2026-08-09 18:05 [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 notification endpoint Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 1/2] ALSA: FCP: Use a private URB for the " Geoffrey D. Bennett
@ 2026-08-09 18:06 ` Geoffrey D. Bennett
2026-08-10 11:37 ` [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 " Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Geoffrey D. Bennett @ 2026-08-09 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound
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 <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)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 notification endpoint
2026-08-09 18:05 [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 notification endpoint Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 1/2] ALSA: FCP: Use a private URB for the " Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 2/2] ALSA: scarlett2: " Geoffrey D. Bennett
@ 2026-08-10 11:37 ` Takashi Iwai
2 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2026-08-10 11:37 UTC (permalink / raw)
To: Geoffrey D. Bennett; +Cc: Takashi Iwai, Takashi Iwai, linux-sound
On Sun, 09 Aug 2026 20:05:41 +0200,
Geoffrey D. Bennett wrote:
>
> Hi Takashi,
>
> Both the FCP and scarlett2 drivers used mixer->urb for their
> notification endpoint URB, but that field belongs to
> snd_usb_mixer_status_create() and is managed by mixer.c. Sharing it
> means the "already set up" check in the driver's init_notify() can be
> satisfied by the status endpoint's URB, so the driver's own URB is
> never submitted and the first command would crash in
> wait_for_completion_timeout() on the still-zeroed cmd_done. mixer.c
> can also kill, resubmit or free the URB from under the driver.
>
> Give each driver its own URB in its private data. For scarlett2 this
> also means taking over the free/suspend/resume handling that mixer.c
> was doing for it.
>
> Tested on an ISA C8X and a Scarlett 18i20 Gen 4 (FCP), and a Scarlett
> 4i4 Gen 3 and 4i4 Gen 4 (scarlett2) on 7.1.7, including suspend/resume
> and configuration save on suspend.
>
> Thanks,
> Geoffrey
>
> Geoffrey D. Bennett (2):
> ALSA: FCP: Use a private URB for the notification endpoint
> ALSA: scarlett2: Use a private URB for the notification endpoint
As those are no new regression, I applied both to for-next branch now.
thanks,
Takashi
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-10 11:37 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-09 18:05 [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 notification endpoint Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 1/2] ALSA: FCP: Use a private URB for the " Geoffrey D. Bennett
2026-08-09 18:06 ` [PATCH 2/2] ALSA: scarlett2: " Geoffrey D. Bennett
2026-08-10 11:37 ` [PATCH 0/2] ALSA: Don't share mixer->urb with the FCP/scarlett2 " Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox