mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 01:11:51 -04:00
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: 1b65088958 ("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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/ffb8ba37d5d605dfdfd8576949d67098651f9349.1786290885.git.g@b4.vu
This commit is contained in:
committed by
Takashi Iwai
parent
918b8d231c
commit
cd17d6ff7b
@@ -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) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user