summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/6fire/chip.c40
-rw-r--r--sound/usb/6fire/midi.c21
-rw-r--r--sound/usb/6fire/pcm.c83
-rw-r--r--sound/usb/Kconfig12
-rw-r--r--sound/usb/bcd2000/bcd2000.c16
-rw-r--r--sound/usb/caiaq/audio.c39
-rw-r--r--sound/usb/card.c94
-rw-r--r--sound/usb/endpoint.c124
-rw-r--r--sound/usb/fcp.c17
-rw-r--r--sound/usb/hiface/chip.c11
-rw-r--r--sound/usb/hiface/pcm.c60
-rw-r--r--sound/usb/line6/capture.c6
-rw-r--r--sound/usb/line6/driver.c39
-rw-r--r--sound/usb/line6/midi.c10
-rw-r--r--sound/usb/line6/pcm.c85
-rw-r--r--sound/usb/media.c6
-rw-r--r--sound/usb/midi.c77
-rw-r--r--sound/usb/midi2.c16
-rw-r--r--sound/usb/misc/ua101.c256
-rw-r--r--sound/usb/mixer.c66
-rw-r--r--sound/usb/mixer_quirks.c354
-rw-r--r--sound/usb/mixer_s1810c.c295
-rw-r--r--sound/usb/mixer_scarlett2.c1414
-rw-r--r--sound/usb/mixer_us16x08.c3
-rw-r--r--sound/usb/pcm.c301
-rw-r--r--sound/usb/proc.c3
-rw-r--r--sound/usb/qcom/qc_audio_offload.c84
-rw-r--r--sound/usb/quirks.c200
-rw-r--r--sound/usb/quirks.h11
-rw-r--r--sound/usb/usbaudio.h110
-rw-r--r--sound/usb/usx2y/Makefile2
-rw-r--r--sound/usb/usx2y/us122l.c50
-rw-r--r--sound/usb/usx2y/us144mkii.c620
-rw-r--r--sound/usb/usx2y/us144mkii.h367
-rw-r--r--sound/usb/usx2y/us144mkii_capture.c319
-rw-r--r--sound/usb/usx2y/us144mkii_controls.c444
-rw-r--r--sound/usb/usx2y/us144mkii_midi.c403
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.c370
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.h165
-rw-r--r--sound/usb/usx2y/us144mkii_playback.c456
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c23
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c33
-rw-r--r--sound/usb/validate.c9
43 files changed, 4972 insertions, 2142 deletions
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
index 9eb4bf9b138b..5ff78814e687 100644
--- a/sound/usb/6fire/chip.c
+++ b/sound/usb/6fire/chip.c
@@ -88,24 +88,22 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
struct snd_card *card = NULL;
/* look if we already serve this card and return if so */
- mutex_lock(&register_mutex);
- for (i = 0; i < SNDRV_CARDS; i++) {
- if (devices[i] == device) {
- if (chips[i])
- chips[i]->intf_count++;
- usb_set_intfdata(intf, chips[i]);
- mutex_unlock(&register_mutex);
- return 0;
- } else if (!devices[i] && regidx < 0)
- regidx = i;
- }
- if (regidx < 0) {
- mutex_unlock(&register_mutex);
- dev_err(&intf->dev, "too many cards registered.\n");
- return -ENODEV;
+ scoped_guard(mutex, &register_mutex) {
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (devices[i] == device) {
+ if (chips[i])
+ chips[i]->intf_count++;
+ usb_set_intfdata(intf, chips[i]);
+ return 0;
+ } else if (!devices[i] && regidx < 0)
+ regidx = i;
+ }
+ if (regidx < 0) {
+ dev_err(&intf->dev, "too many cards registered.\n");
+ return -ENODEV;
+ }
+ devices[regidx] = device;
}
- devices[regidx] = device;
- mutex_unlock(&register_mutex);
/* check, if firmware is present on device, upload it if not */
ret = usb6fire_fw_init(intf);
@@ -175,10 +173,10 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf)
if (chip) { /* if !chip, fw upload has been performed */
chip->intf_count--;
if (!chip->intf_count) {
- mutex_lock(&register_mutex);
- devices[chip->regidx] = NULL;
- chips[chip->regidx] = NULL;
- mutex_unlock(&register_mutex);
+ scoped_guard(mutex, &register_mutex) {
+ devices[chip->regidx] = NULL;
+ chips[chip->regidx] = NULL;
+ }
chip->shutdown = true;
usb6fire_chip_abort(chip);
diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c
index 923f7767e62f..4d1eeb32c5fe 100644
--- a/sound/usb/6fire/midi.c
+++ b/sound/usb/6fire/midi.c
@@ -23,9 +23,8 @@ static void usb6fire_midi_out_handler(struct urb *urb)
{
struct midi_runtime *rt = urb->context;
int ret;
- unsigned long flags;
- spin_lock_irqsave(&rt->out_lock, flags);
+ guard(spinlock_irqsave)(&rt->out_lock);
if (rt->out) {
ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
@@ -43,18 +42,14 @@ static void usb6fire_midi_out_handler(struct urb *urb)
} else /* no more data to transmit */
rt->out = NULL;
}
- spin_unlock_irqrestore(&rt->out_lock, flags);
}
static void usb6fire_midi_in_received(
struct midi_runtime *rt, u8 *data, int length)
{
- unsigned long flags;
-
- spin_lock_irqsave(&rt->in_lock, flags);
+ guard(spinlock_irqsave)(&rt->in_lock);
if (rt->in)
snd_rawmidi_receive(rt->in, data, length);
- spin_unlock_irqrestore(&rt->in_lock, flags);
}
static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
@@ -73,14 +68,11 @@ static void usb6fire_midi_out_trigger(
struct midi_runtime *rt = alsa_sub->rmidi->private_data;
struct urb *urb = &rt->out_urb;
__s8 ret;
- unsigned long flags;
- spin_lock_irqsave(&rt->out_lock, flags);
+ guard(spinlock_irqsave)(&rt->out_lock);
if (up) { /* start transfer */
- if (rt->out) { /* we are already transmitting so just return */
- spin_unlock_irqrestore(&rt->out_lock, flags);
+ if (rt->out) /* we are already transmitting so just return */
return;
- }
ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
MIDI_BUFSIZE - 4);
@@ -99,7 +91,6 @@ static void usb6fire_midi_out_trigger(
}
} else if (rt->out == alsa_sub)
rt->out = NULL;
- spin_unlock_irqrestore(&rt->out_lock, flags);
}
static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
@@ -125,14 +116,12 @@ static void usb6fire_midi_in_trigger(
struct snd_rawmidi_substream *alsa_sub, int up)
{
struct midi_runtime *rt = alsa_sub->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&rt->in_lock, flags);
+ guard(spinlock_irqsave)(&rt->in_lock);
if (up)
rt->in = alsa_sub;
else
rt->in = NULL;
- spin_unlock_irqrestore(&rt->in_lock, flags);
}
static const struct snd_rawmidi_ops out_ops = {
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
index d53cad97889a..08515da5dcc8 100644
--- a/sound/usb/6fire/pcm.c
+++ b/sound/usb/6fire/pcm.c
@@ -289,7 +289,7 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
struct pcm_urb *out_urb = in_urb->peer;
struct pcm_runtime *rt = in_urb->chip->pcm;
struct pcm_substream *sub;
- unsigned long flags;
+ bool period_elapsed;
int total_length = 0;
int frame_count;
int frame;
@@ -313,17 +313,18 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
/* receive our capture data */
sub = &rt->capture;
- spin_lock_irqsave(&sub->lock, flags);
- if (sub->active) {
- usb6fire_pcm_capture(sub, in_urb);
- if (sub->period_off >= sub->instance->runtime->period_size) {
- sub->period_off %= sub->instance->runtime->period_size;
- spin_unlock_irqrestore(&sub->lock, flags);
- snd_pcm_period_elapsed(sub->instance);
- } else
- spin_unlock_irqrestore(&sub->lock, flags);
- } else
- spin_unlock_irqrestore(&sub->lock, flags);
+ period_elapsed = false;
+ scoped_guard(spinlock_irqsave, &sub->lock) {
+ if (sub->active) {
+ usb6fire_pcm_capture(sub, in_urb);
+ if (sub->period_off >= sub->instance->runtime->period_size) {
+ sub->period_off %= sub->instance->runtime->period_size;
+ period_elapsed = true;
+ }
+ }
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(sub->instance);
/* setup out urb structure */
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
@@ -338,17 +339,18 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
/* now send our playback data (if a free out urb was found) */
sub = &rt->playback;
- spin_lock_irqsave(&sub->lock, flags);
- if (sub->active) {
- usb6fire_pcm_playback(sub, out_urb);
- if (sub->period_off >= sub->instance->runtime->period_size) {
- sub->period_off %= sub->instance->runtime->period_size;
- spin_unlock_irqrestore(&sub->lock, flags);
- snd_pcm_period_elapsed(sub->instance);
- } else
- spin_unlock_irqrestore(&sub->lock, flags);
- } else
- spin_unlock_irqrestore(&sub->lock, flags);
+ period_elapsed = false;
+ scoped_guard(spinlock_irqsave, &sub->lock) {
+ if (sub->active) {
+ usb6fire_pcm_playback(sub, out_urb);
+ if (sub->period_off >= sub->instance->runtime->period_size) {
+ sub->period_off %= sub->instance->runtime->period_size;
+ period_elapsed = true;
+ }
+ }
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(sub->instance);
/* setup the 4th byte of each sample (0x40 for analog channels) */
dest = out_urb->buffer;
@@ -392,7 +394,7 @@ static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub)
if (rt->panic)
return -EPIPE;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
alsa_rt->hw = pcm_hw;
if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -408,14 +410,12 @@ static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub)
}
if (!sub) {
- mutex_unlock(&rt->stream_mutex);
dev_err(&rt->chip->dev->dev, "invalid stream type.\n");
return -EINVAL;
}
sub->instance = alsa_sub;
sub->active = false;
- mutex_unlock(&rt->stream_mutex);
return 0;
}
@@ -423,18 +423,17 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
{
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
- unsigned long flags;
if (rt->panic)
return 0;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
if (sub) {
/* deactivate substream */
- spin_lock_irqsave(&sub->lock, flags);
- sub->instance = NULL;
- sub->active = false;
- spin_unlock_irqrestore(&sub->lock, flags);
+ scoped_guard(spinlock_irqsave, &sub->lock) {
+ sub->instance = NULL;
+ sub->active = false;
+ }
/* all substreams closed? if so, stop streaming */
if (!rt->playback.instance && !rt->capture.instance) {
@@ -442,7 +441,6 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
rt->rate = ARRAY_SIZE(rates);
}
}
- mutex_unlock(&rt->stream_mutex);
return 0;
}
@@ -458,7 +456,7 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
if (!sub)
return -ENODEV;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
sub->dma_off = 0;
sub->period_off = 0;
@@ -467,7 +465,6 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
if (alsa_rt->rate == rates[rt->rate])
break;
if (rt->rate == ARRAY_SIZE(rates)) {
- mutex_unlock(&rt->stream_mutex);
dev_err(&rt->chip->dev->dev,
"invalid rate %d in prepare.\n",
alsa_rt->rate);
@@ -475,19 +472,15 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
}
ret = usb6fire_pcm_set_rate(rt);
- if (ret) {
- mutex_unlock(&rt->stream_mutex);
+ if (ret)
return ret;
- }
ret = usb6fire_pcm_stream_start(rt);
if (ret) {
- mutex_unlock(&rt->stream_mutex);
dev_err(&rt->chip->dev->dev,
"could not start pcm stream.\n");
return ret;
}
}
- mutex_unlock(&rt->stream_mutex);
return 0;
}
@@ -495,26 +488,22 @@ static int usb6fire_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
{
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
- unsigned long flags;
if (rt->panic)
return -EPIPE;
if (!sub)
return -ENODEV;
+ guard(spinlock_irqsave)(&sub->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock_irqsave(&sub->lock, flags);
sub->active = true;
- spin_unlock_irqrestore(&sub->lock, flags);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- spin_lock_irqsave(&sub->lock, flags);
sub->active = false;
- spin_unlock_irqrestore(&sub->lock, flags);
return 0;
default:
@@ -527,15 +516,13 @@ static snd_pcm_uframes_t usb6fire_pcm_pointer(
{
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
- unsigned long flags;
snd_pcm_uframes_t ret;
if (rt->panic || !sub)
return SNDRV_PCM_POS_XRUN;
- spin_lock_irqsave(&sub->lock, flags);
+ guard(spinlock_irqsave)(&sub->lock);
ret = sub->dma_off;
- spin_unlock_irqrestore(&sub->lock, flags);
return ret;
}
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 41c47301bc19..9b890abd96d3 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -117,6 +117,18 @@ config SND_USB_US122L
To compile this driver as a module, choose M here: the module
will be called snd-usb-us122l.
+config SND_USB_US144MKII
+ tristate "Tascam US-144MKII USB driver"
+ depends on X86 || COMPILE_TEST
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say Y here to include support for Tascam US-144MKII USB Audio/MIDI
+ interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-us144mkii.
+
config SND_USB_6FIRE
tristate "TerraTec DMX 6Fire USB"
select FW_LOADER
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index 392b4d8e9e76..bebb48cb9abc 100644
--- a/sound/usb/bcd2000/bcd2000.c
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -369,23 +369,19 @@ static int bcd2000_probe(struct usb_interface *interface,
char usb_path[32];
int err;
- mutex_lock(&devices_mutex);
+ guard(mutex)(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
if (!test_bit(card_index, devices_used))
break;
- if (card_index >= SNDRV_CARDS) {
- mutex_unlock(&devices_mutex);
+ if (card_index >= SNDRV_CARDS)
return -ENOENT;
- }
err = snd_card_new(&interface->dev, index[card_index], id[card_index],
THIS_MODULE, sizeof(*bcd2k), &card);
- if (err < 0) {
- mutex_unlock(&devices_mutex);
+ if (err < 0)
return err;
- }
bcd2k = card->private_data;
bcd2k->dev = interface_to_usbdev(interface);
@@ -413,14 +409,12 @@ static int bcd2000_probe(struct usb_interface *interface,
usb_set_intfdata(interface, bcd2k);
set_bit(card_index, devices_used);
- mutex_unlock(&devices_mutex);
return 0;
probe_error:
dev_info(&bcd2k->dev->dev, PREFIX "error during probing");
bcd2000_free_usb_related_resources(bcd2k, interface);
snd_card_free(card);
- mutex_unlock(&devices_mutex);
return err;
}
@@ -431,7 +425,7 @@ static void bcd2000_disconnect(struct usb_interface *interface)
if (!bcd2k)
return;
- mutex_lock(&devices_mutex);
+ guard(mutex)(&devices_mutex);
/* make sure that userspace cannot create new requests */
snd_card_disconnect(bcd2k->card);
@@ -441,8 +435,6 @@ static void bcd2000_disconnect(struct usb_interface *interface)
clear_bit(bcd2k->card_index, devices_used);
snd_card_free_when_closed(bcd2k->card);
-
- mutex_unlock(&devices_mutex);
}
static struct usb_driver bcd2000_driver = {
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index 05f964347ed6..95d425dd9d70 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -51,29 +51,24 @@ static void
activate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
- spin_lock(&cdev->spinlock);
+ guard(spinlock)(&cdev->spinlock);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
cdev->sub_playback[sub->number] = sub;
else
cdev->sub_capture[sub->number] = sub;
-
- spin_unlock(&cdev->spinlock);
}
static void
deactivate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
- unsigned long flags;
- spin_lock_irqsave(&cdev->spinlock, flags);
+ guard(spinlock_irqsave)(&cdev->spinlock);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
cdev->sub_playback[sub->number] = NULL;
else
cdev->sub_capture[sub->number] = NULL;
-
- spin_unlock_irqrestore(&cdev->spinlock, flags);
}
static int
@@ -285,25 +280,18 @@ snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub)
{
int index = sub->number;
struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
- snd_pcm_uframes_t ptr;
- spin_lock(&cdev->spinlock);
+ guard(spinlock)(&cdev->spinlock);
- if (cdev->input_panic || cdev->output_panic) {
- ptr = SNDRV_PCM_POS_XRUN;
- goto unlock;
- }
+ if (cdev->input_panic || cdev->output_panic)
+ return SNDRV_PCM_POS_XRUN;
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ptr = bytes_to_frames(sub->runtime,
- cdev->audio_out_buf_pos[index]);
+ return bytes_to_frames(sub->runtime,
+ cdev->audio_out_buf_pos[index]);
else
- ptr = bytes_to_frames(sub->runtime,
- cdev->audio_in_buf_pos[index]);
-
-unlock:
- spin_unlock(&cdev->spinlock);
- return ptr;
+ return bytes_to_frames(sub->runtime,
+ cdev->audio_in_buf_pos[index]);
}
/* operators for both playback and capture */
@@ -601,7 +589,6 @@ static void read_completed(struct urb *urb)
struct device *dev;
struct urb *out = NULL;
int i, frame, len, send_it = 0, outframe = 0;
- unsigned long flags;
size_t offset = 0;
if (urb->status || !info)
@@ -638,10 +625,10 @@ static void read_completed(struct urb *urb)
offset += len;
if (len > 0) {
- spin_lock_irqsave(&cdev->spinlock, flags);
- fill_out_urb(cdev, out, &out->iso_frame_desc[outframe]);
- read_in_urb(cdev, urb, &urb->iso_frame_desc[frame]);
- spin_unlock_irqrestore(&cdev->spinlock, flags);
+ scoped_guard(spinlock_irqsave, &cdev->spinlock) {
+ fill_out_urb(cdev, out, &out->iso_frame_desc[outframe]);
+ read_in_urb(cdev, urb, &urb->iso_frame_desc[frame]);
+ }
check_for_elapsed_periods(cdev, cdev->sub_playback);
check_for_elapsed_periods(cdev, cdev->sub_capture);
send_it = 1;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 10d9b7285597..1d5a65eac933 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -73,7 +73,7 @@ static bool lowlatency = true;
static char *quirk_alias[SNDRV_CARDS];
static char *delayed_register[SNDRV_CARDS];
static bool implicit_fb[SNDRV_CARDS];
-static unsigned int quirk_flags[SNDRV_CARDS];
+static char *quirk_flags[SNDRV_CARDS];
bool snd_usb_use_vmalloc = true;
bool snd_usb_skip_validation;
@@ -103,13 +103,32 @@ module_param_array(delayed_register, charp, NULL, 0444);
MODULE_PARM_DESC(delayed_register, "Quirk for delayed registration, given by id:iface, e.g. 0123abcd:4.");
module_param_array(implicit_fb, bool, NULL, 0444);
MODULE_PARM_DESC(implicit_fb, "Apply generic implicit feedback sync mode.");
-module_param_array(quirk_flags, uint, NULL, 0444);
-MODULE_PARM_DESC(quirk_flags, "Driver quirk bit flags.");
module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444);
MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes).");
module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444);
MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no).");
+/* protects quirk_flags */
+static DEFINE_MUTEX(quirk_flags_mutex);
+
+static int param_set_quirkp(const char *val,
+ const struct kernel_param *kp)
+{
+ guard(mutex)(&quirk_flags_mutex);
+ return param_set_charp(val, kp);
+}
+
+static const struct kernel_param_ops param_ops_quirkp = {
+ .set = param_set_quirkp,
+ .get = param_get_charp,
+ .free = param_free_charp,
+};
+
+#define param_check_quirkp param_check_charp
+
+module_param_array(quirk_flags, quirkp, NULL, 0644);
+MODULE_PARM_DESC(quirk_flags, "Add/modify USB audio quirks");
+
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
* the all interfaces on the same card as one sound device.
@@ -692,6 +711,31 @@ static void usb_audio_make_longname(struct usb_device *dev,
}
}
+static void snd_usb_init_quirk_flags(int idx, struct snd_usb_audio *chip)
+{
+ size_t i;
+
+ guard(mutex)(&quirk_flags_mutex);
+
+ /* old style option found: the position-based integer value */
+ if (quirk_flags[idx] &&
+ !kstrtou32(quirk_flags[idx], 0, &chip->quirk_flags)) {
+ snd_usb_apply_flag_dbg("module param", chip, chip->quirk_flags);
+ return;
+ }
+
+ /* take the default quirk from the quirk table */
+ snd_usb_init_quirk_flags_table(chip);
+
+ /* add or correct quirk bits from options */
+ for (i = 0; i < ARRAY_SIZE(quirk_flags); i++) {
+ if (!quirk_flags[i] || !*quirk_flags[i])
+ break;
+
+ snd_usb_init_quirk_flags_parse_string(chip, quirk_flags[i]);
+ }
+}
+
/*
* create a chip instance and set its names.
*/
@@ -750,10 +794,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
INIT_LIST_HEAD(&chip->midi_v2_list);
INIT_LIST_HEAD(&chip->mixer_list);
- if (quirk_flags[idx])
- chip->quirk_flags = quirk_flags[idx];
- else
- snd_usb_init_quirk_flags(chip);
+ snd_usb_init_quirk_flags(idx, chip);
card->private_free = snd_usb_audio_free;
@@ -900,7 +941,7 @@ static int usb_audio_probe(struct usb_interface *intf,
/* check whether it's already registered */
chip = NULL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
for (i = 0; i < SNDRV_CARDS; i++) {
if (usb_chip[i] && usb_chip[i]->dev == dev) {
if (atomic_read(&usb_chip[i]->shutdown)) {
@@ -1015,7 +1056,6 @@ static int usb_audio_probe(struct usb_interface *intf,
if (platform_ops && platform_ops->connect_cb)
platform_ops->connect_cb(chip);
- mutex_unlock(&register_mutex);
return 0;
@@ -1033,7 +1073,6 @@ static int usb_audio_probe(struct usb_interface *intf,
if (!chip->num_interfaces)
snd_card_free(chip->card);
}
- mutex_unlock(&register_mutex);
return err;
}
@@ -1041,18 +1080,14 @@ static int usb_audio_probe(struct usb_interface *intf,
* we need to take care of counter, since disconnection can be called also
* many times as well as usb_audio_probe().
*/
-static void usb_audio_disconnect(struct usb_interface *intf)
+static bool __usb_audio_disconnect(struct usb_interface *intf,
+ struct snd_usb_audio *chip,
+ struct snd_card *card)
{
- struct snd_usb_audio *chip = usb_get_intfdata(intf);
- struct snd_card *card;
struct list_head *p;
- if (chip == USB_AUDIO_IFACE_UNUSED)
- return;
-
- card = chip->card;
+ guard(mutex)(&register_mutex);
- mutex_lock(&register_mutex);
if (platform_ops && platform_ops->disconnect_cb)
platform_ops->disconnect_cb(chip);
@@ -1098,13 +1133,24 @@ static void usb_audio_disconnect(struct usb_interface *intf)
usb_enable_autosuspend(interface_to_usbdev(intf));
chip->num_interfaces--;
- if (chip->num_interfaces <= 0) {
- usb_chip[chip->index] = NULL;
- mutex_unlock(&register_mutex);
+ if (chip->num_interfaces > 0)
+ return false;
+
+ usb_chip[chip->index] = NULL;
+ return true;
+}
+
+static void usb_audio_disconnect(struct usb_interface *intf)
+{
+ struct snd_usb_audio *chip = usb_get_intfdata(intf);
+ struct snd_card *card;
+
+ if (chip == USB_AUDIO_IFACE_UNUSED)
+ return;
+
+ card = chip->card;
+ if (__usb_audio_disconnect(intf, chip, card))
snd_card_free_when_closed(card);
- } else {
- mutex_unlock(&register_mutex);
- }
}
/* lock the shutdown (disconnect) task and autoresume */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7b01e7b4e335..880f5afcce60 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -163,22 +163,19 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
static int slave_next_packet_size(struct snd_usb_endpoint *ep,
unsigned int avail)
{
- unsigned long flags;
unsigned int phase;
int ret;
if (ep->fill_max)
return ep->maxframesize;
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval);
ret = min(phase >> 16, ep->maxframesize);
if (avail && ret >= avail)
ret = -EAGAIN;
else
ep->phase = phase;
- spin_unlock_irqrestore(&ep->lock, flags);
-
return ret;
}
@@ -440,11 +437,8 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
struct snd_urb_ctx *ctx)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
- spin_unlock_irqrestore(&ep->lock, flags);
}
/*
@@ -466,23 +460,21 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep);
while (ep_state_running(ep)) {
-
- unsigned long flags;
struct snd_usb_packet_info *packet;
struct snd_urb_ctx *ctx = NULL;
int err, i;
- spin_lock_irqsave(&ep->lock, flags);
- if ((!implicit_fb || ep->next_packet_queued > 0) &&
- !list_empty(&ep->ready_playback_urbs)) {
- /* take URB out of FIFO */
- ctx = list_first_entry(&ep->ready_playback_urbs,
- struct snd_urb_ctx, ready_list);
- list_del_init(&ctx->ready_list);
- if (implicit_fb)
- packet = next_packet_fifo_dequeue(ep);
+ scoped_guard(spinlock_irqsave, &ep->lock) {
+ if ((!implicit_fb || ep->next_packet_queued > 0) &&
+ !list_empty(&ep->ready_playback_urbs)) {
+ /* take URB out of FIFO */
+ ctx = list_first_entry(&ep->ready_playback_urbs,
+ struct snd_urb_ctx, ready_list);
+ list_del_init(&ctx->ready_list);
+ if (implicit_fb)
+ packet = next_packet_fifo_dequeue(ep);
+ }
}
- spin_unlock_irqrestore(&ep->lock, flags);
if (ctx == NULL)
break;
@@ -768,12 +760,8 @@ bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip,
const struct audioformat *fp,
const struct snd_pcm_hw_params *params)
{
- bool ret;
-
- mutex_lock(&chip->mutex);
- ret = endpoint_compatible(ep, fp, params);
- mutex_unlock(&chip->mutex);
- return ret;
+ guard(mutex)(&chip->mutex);
+ return endpoint_compatible(ep, fp, params);
}
/*
@@ -799,11 +787,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep;
int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
ep = snd_usb_get_endpoint(chip, ep_num);
if (!ep) {
usb_audio_err(chip, "Cannot find EP 0x%x to open\n", ep_num);
- goto unlock;
+ return NULL;
}
if (!ep->opened) {
@@ -820,17 +808,13 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep_num, ep->iface, ep->altsetting, ep->ep_idx);
ep->iface_ref = iface_ref_find(chip, ep->iface);
- if (!ep->iface_ref) {
- ep = NULL;
- goto unlock;
- }
+ if (!ep->iface_ref)
+ return NULL;
if (fp->protocol != UAC_VERSION_1) {
ep->clock_ref = clock_ref_find(chip, fp->clock);
- if (!ep->clock_ref) {
- ep = NULL;
- goto unlock;
- }
+ if (!ep->clock_ref)
+ return NULL;
ep->clock_ref->opened++;
}
@@ -859,16 +843,13 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->implicit_fb_sync);
} else {
- if (WARN_ON(!ep->iface_ref)) {
- ep = NULL;
- goto unlock;
- }
+ if (WARN_ON(!ep->iface_ref))
+ return NULL;
if (!endpoint_compatible(ep, fp, params)) {
usb_audio_err(chip, "Incompatible EP setup for 0x%x\n",
ep_num);
- ep = NULL;
- goto unlock;
+ return NULL;
}
usb_audio_dbg(chip, "Reopened EP 0x%x (count %d)\n",
@@ -879,9 +860,6 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->iface_ref->need_setup = true;
ep->opened++;
-
- unlock:
- mutex_unlock(&chip->mutex);
return ep;
}
@@ -964,7 +942,7 @@ retry:
void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep)
{
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
ep->ep_num, ep->opened);
@@ -985,7 +963,6 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
ep->clock_ref = NULL;
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
}
- mutex_unlock(&chip->mutex);
}
/* Prepare for suspening EP, called from the main suspend handler */
@@ -1047,7 +1024,6 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
{
unsigned int i;
- unsigned long flags;
if (!force && atomic_read(&ep->running))
return -EBUSY;
@@ -1055,11 +1031,11 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
if (!ep_state_update(ep, EP_STATE_RUNNING, EP_STATE_STOPPING))
return 0;
- spin_lock_irqsave(&ep->lock, flags);
- INIT_LIST_HEAD(&ep->ready_playback_urbs);
- ep->next_packet_head = 0;
- ep->next_packet_queued = 0;
- spin_unlock_irqrestore(&ep->lock, flags);
+ scoped_guard(spinlock_irqsave, &ep->lock) {
+ INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ ep->next_packet_head = 0;
+ ep->next_packet_queued = 0;
+ }
if (keep_pending)
return 0;
@@ -1360,16 +1336,16 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep)
{
const struct audioformat *fmt = ep->cur_audiofmt;
- int err = 0;
+ int err;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (!ep->need_setup)
- goto unlock;
+ return 0;
/* release old buffers, if any */
err = release_urbs(ep, false);
if (err < 0)
- goto unlock;
+ return err;
ep->datainterval = fmt->datainterval;
ep->maxpacksize = fmt->maxpacksize;
@@ -1407,7 +1383,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
usb_audio_dbg(chip, "Set up %d URBS, ret=%d\n", ep->nurbs, err);
if (err < 0)
- goto unlock;
+ return err;
/* some unit conversions in runtime */
ep->maxframesize = ep->maxpacksize / ep->cur_frame_bytes;
@@ -1419,8 +1395,6 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
err = 0;
}
- unlock:
- mutex_unlock(&chip->mutex);
return err;
}
@@ -1467,11 +1441,11 @@ int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
bool iface_first;
int err = 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (WARN_ON(!ep->iface_ref))
- goto unlock;
+ return 0;
if (!ep->need_prepare)
- goto unlock;
+ return 0;
/* If the interface has been already set up, just set EP parameters */
if (!ep->iface_ref->need_setup) {
@@ -1481,7 +1455,7 @@ int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
if (ep->cur_audiofmt->protocol == UAC_VERSION_1) {
err = init_sample_rate(chip, ep);
if (err < 0)
- goto unlock;
+ return err;
}
goto done;
}
@@ -1499,37 +1473,33 @@ int snd_usb_endpoint_prepare(struct snd_usb_audio *chip,
if (iface_first) {
err = endpoint_set_interface(chip, ep, true);
if (err < 0)
- goto unlock;
+ return err;
}
err = snd_usb_init_pitch(chip, ep->cur_audiofmt);
if (err < 0)
- goto unlock;
+ return err;
err = init_sample_rate(chip, ep);
if (err < 0)
- goto unlock;
+ return err;
err = snd_usb_select_mode_quirk(chip, ep->cur_audiofmt);
if (err < 0)
- goto unlock;
+ return err;
/* for UAC2/3, enable the interface altset here at last */
if (!iface_first) {
err = endpoint_set_interface(chip, ep, true);
if (err < 0)
- goto unlock;
+ return err;
}
ep->iface_ref->need_setup = false;
done:
ep->need_prepare = false;
- err = 1;
-
-unlock:
- mutex_unlock(&chip->mutex);
- return err;
+ return 1;
}
EXPORT_SYMBOL_GPL(snd_usb_endpoint_prepare);
@@ -1541,14 +1511,13 @@ int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
if (!clock)
return 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
list_for_each_entry(ref, &chip->clock_ref_list, list) {
if (ref->clock == clock) {
rate = ref->rate;
break;
}
}
- mutex_unlock(&chip->mutex);
return rate;
}
@@ -1898,9 +1867,8 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
*/
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
ep->freqm = f;
- spin_unlock_irqrestore(&ep->lock, flags);
} else {
/*
* Out of range; maybe the shift value is wrong.
diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c
index 98f9964311a7..5ee8d8b66058 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -820,7 +820,6 @@ static long fcp_hwdep_read(struct snd_hwdep *hw, char __user *buf,
{
struct usb_mixer_interface *mixer = hw->private_data;
struct fcp_data *private = mixer->private_data;
- unsigned long flags;
long ret = 0;
u32 event;
@@ -832,10 +831,10 @@ static long fcp_hwdep_read(struct snd_hwdep *hw, char __user *buf,
if (ret)
return ret;
- spin_lock_irqsave(&private->notify.lock, flags);
- event = private->notify.event;
- private->notify.event = 0;
- spin_unlock_irqrestore(&private->notify.lock, flags);
+ scoped_guard(spinlock_irqsave, &private->notify.lock) {
+ event = private->notify.event;
+ private->notify.event = 0;
+ }
if (copy_to_user(buf, &event, sizeof(event)))
return -EFAULT;
@@ -946,11 +945,9 @@ static void fcp_notify(struct urb *urb)
}
if (data) {
- unsigned long flags;
-
- spin_lock_irqsave(&private->notify.lock, flags);
- private->notify.event |= data;
- spin_unlock_irqrestore(&private->notify.lock, flags);
+ scoped_guard(spinlock_irqsave, &private->notify.lock) {
+ private->notify.event |= data;
+ }
wake_up_interruptible(&private->notify.queue);
}
diff --git a/sound/usb/hiface/chip.c b/sound/usb/hiface/chip.c
index 95385e90882c..bce28f683666 100644
--- a/sound/usb/hiface/chip.c
+++ b/sound/usb/hiface/chip.c
@@ -101,7 +101,7 @@ static int hiface_chip_probe(struct usb_interface *intf,
/* check whether the card is already registered */
chip = NULL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
for (i = 0; i < SNDRV_CARDS; i++)
if (enable[i])
@@ -109,13 +109,12 @@ static int hiface_chip_probe(struct usb_interface *intf,
if (i >= SNDRV_CARDS) {
dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
- ret = -ENODEV;
- goto err;
+ return -ENODEV;
}
ret = hiface_chip_create(intf, device, i, quirk, &chip);
if (ret < 0)
- goto err;
+ return ret;
ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
if (ret < 0)
@@ -127,15 +126,11 @@ static int hiface_chip_probe(struct usb_interface *intf,
goto err_chip_destroy;
}
- mutex_unlock(&register_mutex);
-
usb_set_intfdata(intf, chip);
return 0;
err_chip_destroy:
snd_card_free(chip->card);
-err:
- mutex_unlock(&register_mutex);
return ret;
}
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
index cf650fab54d7..27cd427fbaa5 100644
--- a/sound/usb/hiface/pcm.c
+++ b/sound/usb/hiface/pcm.c
@@ -305,7 +305,6 @@ static void hiface_pcm_out_urb_handler(struct urb *usb_urb)
struct pcm_runtime *rt = out_urb->chip->pcm;
struct pcm_substream *sub;
bool do_period_elapsed = false;
- unsigned long flags;
int ret;
if (rt->panic || rt->stream_state == STREAM_STOPPING)
@@ -325,13 +324,12 @@ static void hiface_pcm_out_urb_handler(struct urb *usb_urb)
/* now send our playback data (if a free out urb was found) */
sub = &rt->playback;
- spin_lock_irqsave(&sub->lock, flags);
- if (sub->active)
- do_period_elapsed = hiface_pcm_playback(sub, out_urb);
- else
- memset(out_urb->buffer, 0, PCM_PACKET_SIZE);
-
- spin_unlock_irqrestore(&sub->lock, flags);
+ scoped_guard(spinlock_irqsave, &sub->lock) {
+ if (sub->active)
+ do_period_elapsed = hiface_pcm_playback(sub, out_urb);
+ else
+ memset(out_urb->buffer, 0, PCM_PACKET_SIZE);
+ }
if (do_period_elapsed)
snd_pcm_period_elapsed(sub->instance);
@@ -356,7 +354,7 @@ static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
if (rt->panic)
return -EPIPE;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
alsa_rt->hw = pcm_hw;
if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -364,7 +362,6 @@ static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
if (!sub) {
struct device *device = &rt->chip->dev->dev;
- mutex_unlock(&rt->stream_mutex);
dev_err(device, "Invalid stream type\n");
return -EINVAL;
}
@@ -377,15 +374,12 @@ static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
ret = snd_pcm_hw_constraint_list(alsa_sub->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_extra_rates);
- if (ret < 0) {
- mutex_unlock(&rt->stream_mutex);
+ if (ret < 0)
return ret;
- }
}
sub->instance = alsa_sub;
sub->active = false;
- mutex_unlock(&rt->stream_mutex);
return 0;
}
@@ -393,23 +387,19 @@ static int hiface_pcm_close(struct snd_pcm_substream *alsa_sub)
{
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
- unsigned long flags;
if (rt->panic)
return 0;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
if (sub) {
hiface_pcm_stream_stop(rt);
/* deactivate substream */
- spin_lock_irqsave(&sub->lock, flags);
+ guard(spinlock_irqsave)(&sub->lock);
sub->instance = NULL;
sub->active = false;
- spin_unlock_irqrestore(&sub->lock, flags);
-
}
- mutex_unlock(&rt->stream_mutex);
return 0;
}
@@ -425,7 +415,7 @@ static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
if (!sub)
return -ENODEV;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
hiface_pcm_stream_stop(rt);
@@ -435,17 +425,12 @@ static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
if (rt->stream_state == STREAM_DISABLED) {
ret = hiface_pcm_set_rate(rt, alsa_rt->rate);
- if (ret) {
- mutex_unlock(&rt->stream_mutex);
+ if (ret)
return ret;
- }
ret = hiface_pcm_stream_start(rt);
- if (ret) {
- mutex_unlock(&rt->stream_mutex);
+ if (ret)
return ret;
- }
}
- mutex_unlock(&rt->stream_mutex);
return 0;
}
@@ -462,16 +447,16 @@ static int hiface_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock_irq(&sub->lock);
- sub->active = true;
- spin_unlock_irq(&sub->lock);
+ scoped_guard(spinlock_irq, &sub->lock) {
+ sub->active = true;
+ }
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- spin_lock_irq(&sub->lock);
- sub->active = false;
- spin_unlock_irq(&sub->lock);
+ scoped_guard(spinlock_irq, &sub->lock) {
+ sub->active = false;
+ }
return 0;
default:
@@ -483,15 +468,13 @@ static snd_pcm_uframes_t hiface_pcm_pointer(struct snd_pcm_substream *alsa_sub)
{
struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
- unsigned long flags;
snd_pcm_uframes_t dma_offset;
if (rt->panic || !sub)
return SNDRV_PCM_POS_XRUN;
- spin_lock_irqsave(&sub->lock, flags);
+ guard(spinlock_irqsave)(&sub->lock);
dma_offset = sub->dma_off;
- spin_unlock_irqrestore(&sub->lock, flags);
return bytes_to_frames(alsa_sub->runtime, dma_offset);
}
@@ -532,9 +515,8 @@ void hiface_pcm_abort(struct hiface_chip *chip)
if (rt) {
rt->panic = true;
- mutex_lock(&rt->stream_mutex);
+ guard(mutex)(&rt->stream_mutex);
hiface_pcm_stream_stop(rt);
- mutex_unlock(&rt->stream_mutex);
}
}
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 84a9b7b76f43..9ef4faa006a0 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -145,8 +145,6 @@ void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
static void audio_in_callback(struct urb *urb)
{
int i, index, length = 0, shutdown = 0;
- unsigned long flags;
-
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
line6pcm->in.last_frame = urb->start_frame;
@@ -156,7 +154,7 @@ static void audio_in_callback(struct urb *urb)
if (urb == line6pcm->in.urbs[index])
break;
- spin_lock_irqsave(&line6pcm->in.lock, flags);
+ guard(spinlock_irqsave)(&line6pcm->in.lock);
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
char *fbuf;
@@ -211,8 +209,6 @@ static void audio_in_callback(struct urb *urb)
test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
line6_capture_check_period(line6pcm, length);
}
-
- spin_unlock_irqrestore(&line6pcm->in.lock, flags);
}
/* open capture callback */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index f2f9261489a2..e97368c31417 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -286,31 +286,30 @@ static void line6_data_received(struct urb *urb)
{
struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
struct midi_buffer *mb = &line6->line6midi->midibuf_in;
- unsigned long flags;
int done;
if (urb->status == -ESHUTDOWN)
return;
if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
- spin_lock_irqsave(&line6->line6midi->lock, flags);
- done =
- line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
-
- if (done < urb->actual_length) {
- line6_midibuf_ignore(mb, done);
- dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
- done, urb->actual_length);
+ scoped_guard(spinlock_irqsave, &line6->line6midi->lock) {
+ done =
+ line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+
+ if (done < urb->actual_length) {
+ line6_midibuf_ignore(mb, done);
+ dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
+ done, urb->actual_length);
+ }
}
- spin_unlock_irqrestore(&line6->line6midi->lock, flags);
for (;;) {
- spin_lock_irqsave(&line6->line6midi->lock, flags);
- done =
- line6_midibuf_read(mb, line6->buffer_message,
- LINE6_MIDI_MESSAGE_MAXLEN,
- LINE6_MIDIBUF_READ_RX);
- spin_unlock_irqrestore(&line6->line6midi->lock, flags);
+ scoped_guard(spinlock_irqsave, &line6->line6midi->lock) {
+ done =
+ line6_midibuf_read(mb, line6->buffer_message,
+ LINE6_MIDI_MESSAGE_MAXLEN,
+ LINE6_MIDIBUF_READ_RX);
+ }
if (done <= 0)
break;
@@ -628,16 +627,12 @@ line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
static __poll_t
line6_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
{
- __poll_t rv;
struct usb_line6 *line6 = hwdep->private_data;
poll_wait(file, &line6->messages.wait_queue, wait);
- mutex_lock(&line6->messages.read_lock);
- rv = kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM;
- mutex_unlock(&line6->messages.read_lock);
-
- return rv;
+ guard(mutex)(&line6->messages.read_lock);
+ return kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM;
}
static const struct snd_hwdep_ops hwdep_ops = {
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index 1d77794b4490..4731293728e6 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -72,7 +72,6 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
*/
static void midi_sent(struct urb *urb)
{
- unsigned long flags;
int status;
int num;
struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
@@ -84,7 +83,7 @@ static void midi_sent(struct urb *urb)
if (status == -ESHUTDOWN)
return;
- spin_lock_irqsave(&line6->line6midi->lock, flags);
+ guard(spinlock_irqsave)(&line6->line6midi->lock);
num = --line6->line6midi->num_active_send_urbs;
if (num == 0) {
@@ -94,8 +93,6 @@ static void midi_sent(struct urb *urb)
if (num == 0)
wake_up(&line6->line6midi->send_wait);
-
- spin_unlock_irqrestore(&line6->line6midi->lock, flags);
}
/*
@@ -158,17 +155,14 @@ static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
int up)
{
- unsigned long flags;
struct usb_line6 *line6 =
line6_rawmidi_substream_midi(substream)->line6;
line6->line6midi->substream_transmit = substream;
- spin_lock_irqsave(&line6->line6midi->lock, flags);
+ guard(spinlock_irqsave)(&line6->line6midi->lock);
if (line6->line6midi->num_active_send_urbs == 0)
line6_midi_transmit(substream);
-
- spin_unlock_irqrestore(&line6->line6midi->lock, flags);
}
static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index c1e2a8ab66fa..f61d9f6cf754 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -182,11 +182,10 @@ static void line6_buffer_release(struct snd_line6_pcm *line6pcm,
static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
int type)
{
- unsigned long flags;
struct line6_pcm_stream *pstr = get_stream(line6pcm, direction);
int ret = 0;
- spin_lock_irqsave(&pstr->lock, flags);
+ guard(spinlock_irqsave)(&pstr->lock);
if (!test_and_set_bit(type, &pstr->running) &&
!(pstr->active_urbs || pstr->unlink_urbs)) {
pstr->count = 0;
@@ -199,7 +198,6 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
if (ret < 0)
clear_bit(type, &pstr->running);
- spin_unlock_irqrestore(&pstr->lock, flags);
return ret;
}
@@ -207,21 +205,20 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction,
int type)
{
- unsigned long flags;
struct line6_pcm_stream *pstr = get_stream(line6pcm, direction);
- spin_lock_irqsave(&pstr->lock, flags);
- clear_bit(type, &pstr->running);
- if (!pstr->running) {
- spin_unlock_irqrestore(&pstr->lock, flags);
- line6_unlink_audio_urbs(line6pcm, pstr);
- spin_lock_irqsave(&pstr->lock, flags);
- if (direction == SNDRV_PCM_STREAM_CAPTURE) {
- line6pcm->prev_fbuf = NULL;
- line6pcm->prev_fsize = 0;
- }
+ scoped_guard(spinlock_irqsave, &pstr->lock) {
+ clear_bit(type, &pstr->running);
+ if (pstr->running)
+ return;
+ }
+
+ line6_unlink_audio_urbs(line6pcm, pstr);
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ guard(spinlock_irqsave)(&pstr->lock);
+ line6pcm->prev_fbuf = NULL;
+ line6pcm->prev_fsize = 0;
}
- spin_unlock_irqrestore(&pstr->lock, flags);
}
/* common PCM trigger callback */
@@ -295,6 +292,28 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
return pstr->pos_done;
}
+/* Stop and release duplex streams */
+static void __line6_pcm_release(struct snd_line6_pcm *line6pcm, int type)
+{
+ struct line6_pcm_stream *pstr;
+ int dir;
+
+ for (dir = 0; dir < 2; dir++)
+ line6_stream_stop(line6pcm, dir, type);
+ for (dir = 0; dir < 2; dir++) {
+ pstr = get_stream(line6pcm, dir);
+ line6_buffer_release(line6pcm, pstr, type);
+ }
+}
+
+/* Stop and release duplex streams */
+void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type)
+{
+ guard(mutex)(&line6pcm->state_mutex);
+ __line6_pcm_release(line6pcm, type);
+}
+EXPORT_SYMBOL_GPL(line6_pcm_release);
+
/* Acquire and optionally start duplex streams:
* type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR
*/
@@ -304,7 +323,7 @@ int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start)
int ret = 0, dir;
/* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */
- mutex_lock(&line6pcm->state_mutex);
+ guard(mutex)(&line6pcm->state_mutex);
for (dir = 0; dir < 2; dir++) {
pstr = get_stream(line6pcm, dir);
ret = line6_buffer_acquire(line6pcm, pstr, dir, type);
@@ -321,30 +340,12 @@ int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start)
}
}
error:
- mutex_unlock(&line6pcm->state_mutex);
if (ret < 0)
- line6_pcm_release(line6pcm, type);
+ __line6_pcm_release(line6pcm, type);
return ret;
}
EXPORT_SYMBOL_GPL(line6_pcm_acquire);
-/* Stop and release duplex streams */
-void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type)
-{
- struct line6_pcm_stream *pstr;
- int dir;
-
- mutex_lock(&line6pcm->state_mutex);
- for (dir = 0; dir < 2; dir++)
- line6_stream_stop(line6pcm, dir, type);
- for (dir = 0; dir < 2; dir++) {
- pstr = get_stream(line6pcm, dir);
- line6_buffer_release(line6pcm, pstr, type);
- }
- mutex_unlock(&line6pcm->state_mutex);
-}
-EXPORT_SYMBOL_GPL(line6_pcm_release);
-
/* common PCM hw_params callback */
int snd_line6_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
@@ -353,16 +354,14 @@ int snd_line6_hw_params(struct snd_pcm_substream *substream,
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
- mutex_lock(&line6pcm->state_mutex);
+ guard(mutex)(&line6pcm->state_mutex);
ret = line6_buffer_acquire(line6pcm, pstr, substream->stream,
LINE6_STREAM_PCM);
if (ret < 0)
- goto error;
+ return ret;
pstr->period = params_period_bytes(hw_params);
- error:
- mutex_unlock(&line6pcm->state_mutex);
- return ret;
+ return 0;
}
/* common PCM hw_free callback */
@@ -371,9 +370,8 @@ int snd_line6_hw_free(struct snd_pcm_substream *substream)
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
- mutex_lock(&line6pcm->state_mutex);
+ guard(mutex)(&line6pcm->state_mutex);
line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM);
- mutex_unlock(&line6pcm->state_mutex);
return 0;
}
@@ -588,7 +586,7 @@ int snd_line6_prepare(struct snd_pcm_substream *substream)
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
- mutex_lock(&line6pcm->state_mutex);
+ guard(mutex)(&line6pcm->state_mutex);
if (!pstr->running)
line6_wait_clear_audio_urbs(line6pcm, pstr);
@@ -602,6 +600,5 @@ int snd_line6_prepare(struct snd_pcm_substream *substream)
line6pcm->in.bytes = 0;
}
- mutex_unlock(&line6pcm->state_mutex);
return 0;
}
diff --git a/sound/usb/media.c b/sound/usb/media.c
index d48db6f3ae65..0064f8d12422 100644
--- a/sound/usb/media.c
+++ b/sound/usb/media.c
@@ -140,11 +140,10 @@ int snd_media_start_pipeline(struct snd_usb_substream *subs)
if (!mctl)
return 0;
- mutex_lock(&mctl->media_dev->graph_mutex);
+ guard(mutex)(&mctl->media_dev->graph_mutex);
if (mctl->media_dev->enable_source)
ret = mctl->media_dev->enable_source(&mctl->media_entity,
&mctl->media_pipe);
- mutex_unlock(&mctl->media_dev->graph_mutex);
return ret;
}
@@ -155,10 +154,9 @@ void snd_media_stop_pipeline(struct snd_usb_substream *subs)
if (!mctl)
return;
- mutex_lock(&mctl->media_dev->graph_mutex);
+ guard(mutex)(&mctl->media_dev->graph_mutex);
if (mctl->media_dev->disable_source)
mctl->media_dev->disable_source(&mctl->media_entity);
- mutex_unlock(&mctl->media_dev->graph_mutex);
}
static int snd_media_mixer_init(struct snd_usb_audio *chip)
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index acb3bf92857c..dd8249e75970 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -265,16 +265,15 @@ static void snd_usbmidi_out_urb_complete(struct urb *urb)
struct out_urb_context *context = urb->context;
struct snd_usb_midi_out_endpoint *ep = context->ep;
unsigned int urb_index;
- unsigned long flags;
-
- spin_lock_irqsave(&ep->buffer_lock, flags);
- urb_index = context - ep->urbs;
- ep->active_urbs &= ~(1 << urb_index);
- if (unlikely(ep->drain_urbs)) {
- ep->drain_urbs &= ~(1 << urb_index);
- wake_up(&ep->drain_wait);
+
+ scoped_guard(spinlock_irqsave, &ep->buffer_lock) {
+ urb_index = context - ep->urbs;
+ ep->active_urbs &= ~(1 << urb_index);
+ if (unlikely(ep->drain_urbs)) {
+ ep->drain_urbs &= ~(1 << urb_index);
+ wake_up(&ep->drain_wait);
+ }
}
- spin_unlock_irqrestore(&ep->buffer_lock, flags);
if (urb->status < 0) {
int err = snd_usbmidi_urb_error(urb);
if (err < 0) {
@@ -295,13 +294,10 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint *ep)
{
unsigned int urb_index;
struct urb *urb;
- unsigned long flags;
- spin_lock_irqsave(&ep->buffer_lock, flags);
- if (ep->umidi->disconnected) {
- spin_unlock_irqrestore(&ep->buffer_lock, flags);
+ guard(spinlock_irqsave)(&ep->buffer_lock);
+ if (ep->umidi->disconnected)
return;
- }
urb_index = ep->next_urb;
for (;;) {
@@ -325,7 +321,6 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint *ep)
break;
}
ep->next_urb = urb_index;
- spin_unlock_irqrestore(&ep->buffer_lock, flags);
}
static void snd_usbmidi_out_work(struct work_struct *work)
@@ -342,9 +337,8 @@ static void snd_usbmidi_error_timer(struct timer_list *t)
struct snd_usb_midi *umidi = timer_container_of(umidi, t, error_timer);
unsigned int i, j;
- spin_lock(&umidi->disc_lock);
+ guard(spinlock)(&umidi->disc_lock);
if (umidi->disconnected) {
- spin_unlock(&umidi->disc_lock);
return;
}
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
@@ -361,7 +355,6 @@ static void snd_usbmidi_error_timer(struct timer_list *t)
if (umidi->endpoints[i].out)
snd_usbmidi_do_output(umidi->endpoints[i].out);
}
- spin_unlock(&umidi->disc_lock);
}
/* helper function to send static data that may not DMA-able */
@@ -1148,13 +1141,11 @@ static int substream_open(struct snd_rawmidi_substream *substream, int dir,
struct snd_usb_midi *umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl;
- down_read(&umidi->disc_rwsem);
- if (umidi->disconnected) {
- up_read(&umidi->disc_rwsem);
+ guard(rwsem_read)(&umidi->disc_rwsem);
+ if (umidi->disconnected)
return open ? -ENODEV : 0;
- }
- mutex_lock(&umidi->mutex);
+ guard(mutex)(&umidi->mutex);
if (open) {
if (!umidi->opened[0] && !umidi->opened[1]) {
if (umidi->roland_load_ctl) {
@@ -1183,8 +1174,6 @@ static int substream_open(struct snd_rawmidi_substream *substream, int dir,
}
}
}
- mutex_unlock(&umidi->mutex);
- up_read(&umidi->disc_rwsem);
return 0;
}
@@ -1522,15 +1511,14 @@ static void snd_usbmidi_free(struct snd_usb_midi *umidi)
{
int i;
+ if (!umidi->disconnected)
+ snd_usbmidi_disconnect(&umidi->list);
+
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i];
- if (ep->out)
- snd_usbmidi_out_endpoint_delete(ep->out);
- if (ep->in)
- snd_usbmidi_in_endpoint_delete(ep->in);
+ kfree(ep->out);
}
mutex_destroy(&umidi->mutex);
- timer_shutdown_sync(&umidi->error_timer);
kfree(umidi);
}
@@ -1548,11 +1536,10 @@ void snd_usbmidi_disconnect(struct list_head *p)
* a timer may submit an URB. To reliably break the cycle
* a flag under lock must be used
*/
- down_write(&umidi->disc_rwsem);
- spin_lock_irq(&umidi->disc_lock);
- umidi->disconnected = 1;
- spin_unlock_irq(&umidi->disc_lock);
- up_write(&umidi->disc_rwsem);
+ scoped_guard(rwsem_write, &umidi->disc_rwsem) {
+ guard(spinlock_irq)(&umidi->disc_lock);
+ umidi->disconnected = 1;
+ }
timer_shutdown_sync(&umidi->error_timer);
@@ -2094,11 +2081,10 @@ static int roland_load_put(struct snd_kcontrol *kcontrol,
if (value->value.enumerated.item[0] > 1)
return -EINVAL;
- mutex_lock(&umidi->mutex);
+ guard(mutex)(&umidi->mutex);
changed = value->value.enumerated.item[0] != kcontrol->private_value;
if (changed)
kcontrol->private_value = value->value.enumerated.item[0];
- mutex_unlock(&umidi->mutex);
return changed;
}
@@ -2448,18 +2434,17 @@ static void snd_usbmidi_input_start_ep(struct snd_usb_midi *umidi,
struct snd_usb_midi_in_endpoint *ep)
{
unsigned int i;
- unsigned long flags;
if (!ep)
return;
for (i = 0; i < INPUT_URBS; ++i) {
struct urb *urb = ep->urbs[i];
- spin_lock_irqsave(&umidi->disc_lock, flags);
- if (!atomic_read(&urb->use_count)) {
- urb->dev = ep->umidi->dev;
- snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+ scoped_guard(spinlock_irqsave, &umidi->disc_lock) {
+ if (!atomic_read(&urb->use_count)) {
+ urb->dev = ep->umidi->dev;
+ snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+ }
}
- spin_unlock_irqrestore(&umidi->disc_lock, flags);
}
}
@@ -2488,9 +2473,8 @@ void snd_usbmidi_suspend(struct list_head *p)
struct snd_usb_midi *umidi;
umidi = list_entry(p, struct snd_usb_midi, list);
- mutex_lock(&umidi->mutex);
+ guard(mutex)(&umidi->mutex);
snd_usbmidi_input_stop(p);
- mutex_unlock(&umidi->mutex);
}
EXPORT_SYMBOL(snd_usbmidi_suspend);
@@ -2502,9 +2486,8 @@ void snd_usbmidi_resume(struct list_head *p)
struct snd_usb_midi *umidi;
umidi = list_entry(p, struct snd_usb_midi, list);
- mutex_lock(&umidi->mutex);
+ guard(mutex)(&umidi->mutex);
snd_usbmidi_input_start(p);
- mutex_unlock(&umidi->mutex);
}
EXPORT_SYMBOL(snd_usbmidi_resume);
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 030569fda416..e6793f3bdfc3 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -160,15 +160,13 @@ static void output_urb_complete(struct urb *urb)
{
struct snd_usb_midi2_urb *ctx = urb->context;
struct snd_usb_midi2_endpoint *ep = ctx->ep;
- unsigned long flags;
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
set_bit(ctx->index, &ep->urb_free);
if (urb->status >= 0 && atomic_read(&ep->running))
submit_output_urbs_locked(ep);
if (ep->urb_free == ep->urb_free_mask)
wake_up(&ep->wait);
- spin_unlock_irqrestore(&ep->lock, flags);
}
/* prepare for input submission: just set the buffer length */
@@ -189,10 +187,9 @@ static void input_urb_complete(struct urb *urb)
{
struct snd_usb_midi2_urb *ctx = urb->context;
struct snd_usb_midi2_endpoint *ep = ctx->ep;
- unsigned long flags;
int len;
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
if (ep->disconnected || urb->status < 0)
goto dequeue;
len = urb->actual_length;
@@ -208,22 +205,18 @@ static void input_urb_complete(struct urb *urb)
submit_input_urbs_locked(ep);
if (ep->urb_free == ep->urb_free_mask)
wake_up(&ep->wait);
- spin_unlock_irqrestore(&ep->lock, flags);
}
/* URB submission helper; for both direction */
static void submit_io_urbs(struct snd_usb_midi2_endpoint *ep)
{
- unsigned long flags;
-
if (!ep)
return;
- spin_lock_irqsave(&ep->lock, flags);
+ guard(spinlock_irqsave)(&ep->lock);
if (ep->direction == STR_IN)
submit_input_urbs_locked(ep);
else
submit_output_urbs_locked(ep);
- spin_unlock_irqrestore(&ep->lock, flags);
}
/* kill URBs for close, suspend and disconnect */
@@ -248,13 +241,12 @@ static void drain_urb_queue(struct snd_usb_midi2_endpoint *ep)
{
if (!ep)
return;
- spin_lock_irq(&ep->lock);
+ guard(spinlock_irq)(&ep->lock);
atomic_set(&ep->running, 0);
wait_event_lock_irq_timeout(ep->wait,
ep->disconnected ||
ep->urb_free == ep->urb_free_mask,
ep->lock, msecs_to_jiffies(500));
- spin_unlock_irq(&ep->lock);
}
/* release URBs for an EP */
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index e200e4af799c..76b6eb55dcc2 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -171,7 +171,6 @@ static void playback_urb_complete(struct urb *usb_urb)
{
struct ua101_urb *urb = (struct ua101_urb *)usb_urb;
struct ua101 *ua = urb->urb.context;
- unsigned long flags;
if (unlikely(urb->urb.status == -ENOENT || /* unlinked */
urb->urb.status == -ENODEV || /* device removed */
@@ -184,14 +183,13 @@ static void playback_urb_complete(struct urb *usb_urb)
if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) {
/* append URB to FIFO */
- spin_lock_irqsave(&ua->lock, flags);
+ guard(spinlock_irqsave)(&ua->lock);
list_add_tail(&urb->ready_list, &ua->ready_playback_urbs);
if (ua->rate_feedback_count > 0)
queue_work(system_highpri_wq, &ua->playback_work);
ua->playback.substream->runtime->delay -=
urb->urb.iso_frame_desc[0].length /
ua->playback.frame_bytes;
- spin_unlock_irqrestore(&ua->lock, flags);
}
}
@@ -249,7 +247,6 @@ static inline void add_with_wraparound(struct ua101 *ua,
static void playback_work(struct work_struct *work)
{
struct ua101 *ua = container_of(work, struct ua101, playback_work);
- unsigned long flags;
unsigned int frames;
struct ua101_urb *urb;
bool do_period_elapsed = false;
@@ -269,43 +266,43 @@ static void playback_work(struct work_struct *work)
* submitting playback URBs is possible as long as both FIFOs are
* nonempty.
*/
- spin_lock_irqsave(&ua->lock, flags);
- while (ua->rate_feedback_count > 0 &&
- !list_empty(&ua->ready_playback_urbs)) {
- /* take packet size out of FIFO */
- frames = ua->rate_feedback[ua->rate_feedback_start];
- add_with_wraparound(ua, &ua->rate_feedback_start, 1);
- ua->rate_feedback_count--;
-
- /* take URB out of FIFO */
- urb = list_first_entry(&ua->ready_playback_urbs,
- struct ua101_urb, ready_list);
- list_del(&urb->ready_list);
-
- /* fill packet with data or silence */
- urb->urb.iso_frame_desc[0].length =
- frames * ua->playback.frame_bytes;
- if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
- do_period_elapsed |= copy_playback_data(&ua->playback,
- &urb->urb,
- frames);
- else
- memset(urb->urb.transfer_buffer, 0,
- urb->urb.iso_frame_desc[0].length);
-
- /* and off you go ... */
- err = usb_submit_urb(&urb->urb, GFP_ATOMIC);
- if (unlikely(err < 0)) {
- spin_unlock_irqrestore(&ua->lock, flags);
- abort_usb_playback(ua);
- abort_alsa_playback(ua);
- dev_err(&ua->dev->dev, "USB request error %d: %s\n",
- err, usb_error_string(err));
- return;
+ scoped_guard(spinlock_irqsave, &ua->lock) {
+ while (ua->rate_feedback_count > 0 &&
+ !list_empty(&ua->ready_playback_urbs)) {
+ /* take packet size out of FIFO */
+ frames = ua->rate_feedback[ua->rate_feedback_start];
+ add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+ ua->rate_feedback_count--;
+
+ /* take URB out of FIFO */
+ urb = list_first_entry(&ua->ready_playback_urbs,
+ struct ua101_urb, ready_list);
+ list_del(&urb->ready_list);
+
+ /* fill packet with data or silence */
+ urb->urb.iso_frame_desc[0].length =
+ frames * ua->playback.frame_bytes;
+ if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
+ do_period_elapsed |= copy_playback_data(&ua->playback,
+ &urb->urb,
+ frames);
+ else
+ memset(urb->urb.transfer_buffer, 0,
+ urb->urb.iso_frame_desc[0].length);
+
+ /* and off you go ... */
+ err = usb_submit_urb(&urb->urb, GFP_ATOMIC);
+ if (unlikely(err < 0)) {
+ abort_usb_playback(ua);
+ abort_alsa_playback(ua);
+ dev_err(&ua->dev->dev, "USB request error %d: %s\n",
+ err, usb_error_string(err));
+ return;
+ }
+ ua->playback.substream->runtime->delay += frames;
}
- ua->playback.substream->runtime->delay += frames;
}
- spin_unlock_irqrestore(&ua->lock, flags);
+
if (do_period_elapsed)
snd_pcm_period_elapsed(ua->playback.substream);
}
@@ -347,7 +344,6 @@ static void capture_urb_complete(struct urb *urb)
{
struct ua101 *ua = urb->context;
struct ua101_stream *stream = &ua->capture;
- unsigned long flags;
unsigned int frames, write_ptr;
bool do_period_elapsed;
int err;
@@ -364,47 +360,45 @@ static void capture_urb_complete(struct urb *urb)
else
frames = 0;
- spin_lock_irqsave(&ua->lock, flags);
-
- if (frames > 0 && test_bit(ALSA_CAPTURE_RUNNING, &ua->states))
- do_period_elapsed = copy_capture_data(stream, urb, frames);
- else
- do_period_elapsed = false;
+ scoped_guard(spinlock_irqsave, &ua->lock) {
- if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) {
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (unlikely(err < 0)) {
- spin_unlock_irqrestore(&ua->lock, flags);
- dev_err(&ua->dev->dev, "USB request error %d: %s\n",
- err, usb_error_string(err));
- goto stream_stopped;
- }
+ if (frames > 0 && test_bit(ALSA_CAPTURE_RUNNING, &ua->states))
+ do_period_elapsed = copy_capture_data(stream, urb, frames);
+ else
+ do_period_elapsed = false;
+
+ if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) {
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err < 0)) {
+ dev_err(&ua->dev->dev, "USB request error %d: %s\n",
+ err, usb_error_string(err));
+ goto stream_stopped;
+ }
- /* append packet size to FIFO */
- write_ptr = ua->rate_feedback_start;
- add_with_wraparound(ua, &write_ptr, ua->rate_feedback_count);
- ua->rate_feedback[write_ptr] = frames;
- if (ua->rate_feedback_count < ua->playback.queue_length) {
- ua->rate_feedback_count++;
- if (ua->rate_feedback_count ==
- ua->playback.queue_length)
- wake_up(&ua->rate_feedback_wait);
- } else {
- /*
- * Ring buffer overflow; this happens when the playback
- * stream is not running. Throw away the oldest entry,
- * so that the playback stream, when it starts, sees
- * the most recent packet sizes.
- */
- add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+ /* append packet size to FIFO */
+ write_ptr = ua->rate_feedback_start;
+ add_with_wraparound(ua, &write_ptr, ua->rate_feedback_count);
+ ua->rate_feedback[write_ptr] = frames;
+ if (ua->rate_feedback_count < ua->playback.queue_length) {
+ ua->rate_feedback_count++;
+ if (ua->rate_feedback_count ==
+ ua->playback.queue_length)
+ wake_up(&ua->rate_feedback_wait);
+ } else {
+ /*
+ * Ring buffer overflow; this happens when the playback
+ * stream is not running. Throw away the oldest entry,
+ * so that the playback stream, when it starts, sees
+ * the most recent packet sizes.
+ */
+ add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+ }
+ if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) &&
+ !list_empty(&ua->ready_playback_urbs))
+ queue_work(system_highpri_wq, &ua->playback_work);
}
- if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) &&
- !list_empty(&ua->ready_playback_urbs))
- queue_work(system_highpri_wq, &ua->playback_work);
}
- spin_unlock_irqrestore(&ua->lock, flags);
-
if (do_period_elapsed)
snd_pcm_period_elapsed(stream->substream);
@@ -558,9 +552,9 @@ static int start_usb_playback(struct ua101 *ua)
clear_bit(PLAYBACK_URB_COMPLETED, &ua->states);
ua->playback.urbs[0]->urb.complete =
first_playback_urb_complete;
- spin_lock_irq(&ua->lock);
- INIT_LIST_HEAD(&ua->ready_playback_urbs);
- spin_unlock_irq(&ua->lock);
+ scoped_guard(spinlock_irq, &ua->lock) {
+ INIT_LIST_HEAD(&ua->ready_playback_urbs);
+ }
/*
* We submit the initial URBs all at once, so we have to wait for the
@@ -581,11 +575,11 @@ static int start_usb_playback(struct ua101 *ua)
for (i = 0; i < ua->playback.queue_length; ++i) {
/* all initial URBs contain silence */
- spin_lock_irq(&ua->lock);
- frames = ua->rate_feedback[ua->rate_feedback_start];
- add_with_wraparound(ua, &ua->rate_feedback_start, 1);
- ua->rate_feedback_count--;
- spin_unlock_irq(&ua->lock);
+ scoped_guard(spinlock_irq, &ua->lock) {
+ frames = ua->rate_feedback[ua->rate_feedback_start];
+ add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+ ua->rate_feedback_count--;
+ }
urb = &ua->playback.urbs[i]->urb;
urb->iso_frame_desc[0].length =
frames * ua->playback.frame_bytes;
@@ -658,11 +652,10 @@ static int capture_pcm_open(struct snd_pcm_substream *substream)
DIV_ROUND_CLOSEST(ua->rate, ua->packets_per_second);
substream->runtime->delay = substream->runtime->hw.fifo_size;
- mutex_lock(&ua->mutex);
+ guard(mutex)(&ua->mutex);
err = start_usb_capture(ua);
if (err >= 0)
set_bit(ALSA_CAPTURE_OPEN, &ua->states);
- mutex_unlock(&ua->mutex);
return err;
}
@@ -679,31 +672,28 @@ static int playback_pcm_open(struct snd_pcm_substream *substream)
DIV_ROUND_CLOSEST(ua->rate * ua->playback.queue_length,
ua->packets_per_second);
- mutex_lock(&ua->mutex);
+ guard(mutex)(&ua->mutex);
err = start_usb_capture(ua);
if (err < 0)
- goto error;
+ return err;
err = start_usb_playback(ua);
if (err < 0) {
if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states))
stop_usb_capture(ua);
- goto error;
+ return err;
}
set_bit(ALSA_PLAYBACK_OPEN, &ua->states);
-error:
- mutex_unlock(&ua->mutex);
- return err;
+ return 0;
}
static int capture_pcm_close(struct snd_pcm_substream *substream)
{
struct ua101 *ua = substream->private_data;
- mutex_lock(&ua->mutex);
+ guard(mutex)(&ua->mutex);
clear_bit(ALSA_CAPTURE_OPEN, &ua->states);
if (!test_bit(ALSA_PLAYBACK_OPEN, &ua->states))
stop_usb_capture(ua);
- mutex_unlock(&ua->mutex);
return 0;
}
@@ -711,12 +701,11 @@ static int playback_pcm_close(struct snd_pcm_substream *substream)
{
struct ua101 *ua = substream->private_data;
- mutex_lock(&ua->mutex);
+ guard(mutex)(&ua->mutex);
stop_usb_playback(ua);
clear_bit(ALSA_PLAYBACK_OPEN, &ua->states);
if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states))
stop_usb_capture(ua);
- mutex_unlock(&ua->mutex);
return 0;
}
@@ -724,12 +713,9 @@ static int capture_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct ua101 *ua = substream->private_data;
- int err;
- mutex_lock(&ua->mutex);
- err = start_usb_capture(ua);
- mutex_unlock(&ua->mutex);
- return err;
+ guard(mutex)(&ua->mutex);
+ return start_usb_capture(ua);
}
static int playback_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -738,11 +724,10 @@ static int playback_pcm_hw_params(struct snd_pcm_substream *substream,
struct ua101 *ua = substream->private_data;
int err;
- mutex_lock(&ua->mutex);
+ guard(mutex)(&ua->mutex);
err = start_usb_capture(ua);
if (err >= 0)
err = start_usb_playback(ua);
- mutex_unlock(&ua->mutex);
return err;
}
@@ -751,9 +736,9 @@ static int capture_pcm_prepare(struct snd_pcm_substream *substream)
struct ua101 *ua = substream->private_data;
int err;
- mutex_lock(&ua->mutex);
- err = start_usb_capture(ua);
- mutex_unlock(&ua->mutex);
+ scoped_guard(mutex, &ua->mutex) {
+ err = start_usb_capture(ua);
+ }
if (err < 0)
return err;
@@ -781,11 +766,11 @@ static int playback_pcm_prepare(struct snd_pcm_substream *substream)
struct ua101 *ua = substream->private_data;
int err;
- mutex_lock(&ua->mutex);
- err = start_usb_capture(ua);
- if (err >= 0)
- err = start_usb_playback(ua);
- mutex_unlock(&ua->mutex);
+ scoped_guard(mutex, &ua->mutex) {
+ err = start_usb_capture(ua);
+ if (err >= 0)
+ err = start_usb_playback(ua);
+ }
if (err < 0)
return err;
@@ -843,13 +828,8 @@ static int playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static inline snd_pcm_uframes_t ua101_pcm_pointer(struct ua101 *ua,
struct ua101_stream *stream)
{
- unsigned long flags;
- unsigned int pos;
-
- spin_lock_irqsave(&ua->lock, flags);
- pos = stream->buffer_pos;
- spin_unlock_irqrestore(&ua->lock, flags);
- return pos;
+ guard(spinlock_irqsave)(&ua->lock);
+ return stream->buffer_pos;
}
static snd_pcm_uframes_t capture_pcm_pointer(struct snd_pcm_substream *subs)
@@ -1127,18 +1107,18 @@ static void free_usb_related_resources(struct ua101 *ua,
unsigned int i;
struct usb_interface *intf;
- mutex_lock(&ua->mutex);
- free_stream_urbs(&ua->capture);
- free_stream_urbs(&ua->playback);
- mutex_unlock(&ua->mutex);
+ scoped_guard(mutex, &ua->mutex) {
+ free_stream_urbs(&ua->capture);
+ free_stream_urbs(&ua->playback);
+ }
free_stream_buffers(ua, &ua->capture);
free_stream_buffers(ua, &ua->playback);
for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) {
- mutex_lock(&ua->mutex);
- intf = ua->intf[i];
- ua->intf[i] = NULL;
- mutex_unlock(&ua->mutex);
+ scoped_guard(mutex, &ua->mutex) {
+ intf = ua->intf[i];
+ ua->intf[i] = NULL;
+ }
if (intf) {
usb_set_intfdata(intf, NULL);
if (intf != interface)
@@ -1192,22 +1172,18 @@ static int ua101_probe(struct usb_interface *interface,
intf_numbers[is_ua1000][0])
return -ENODEV;
- mutex_lock(&devices_mutex);
+ guard(mutex)(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
if (enable[card_index] && !(devices_used & (1 << card_index)))
break;
- if (card_index >= SNDRV_CARDS) {
- mutex_unlock(&devices_mutex);
+ if (card_index >= SNDRV_CARDS)
return -ENOENT;
- }
err = snd_card_new(&interface->dev,
index[card_index], id[card_index], THIS_MODULE,
sizeof(*ua), &card);
- if (err < 0) {
- mutex_unlock(&devices_mutex);
+ if (err < 0)
return err;
- }
card->private_free = ua101_card_free;
ua = card->private_data;
ua->dev = interface_to_usbdev(interface);
@@ -1290,13 +1266,11 @@ static int ua101_probe(struct usb_interface *interface,
usb_set_intfdata(interface, ua);
devices_used |= 1 << card_index;
- mutex_unlock(&devices_mutex);
return 0;
probe_error:
free_usb_related_resources(ua, interface);
snd_card_free(card);
- mutex_unlock(&devices_mutex);
return err;
}
@@ -1308,7 +1282,7 @@ static void ua101_disconnect(struct usb_interface *interface)
if (!ua)
return;
- mutex_lock(&devices_mutex);
+ guard(mutex)(&devices_mutex);
set_bit(DISCONNECTED, &ua->states);
wake_up(&ua->rate_feedback_wait);
@@ -1321,18 +1295,16 @@ static void ua101_disconnect(struct usb_interface *interface)
snd_usbmidi_disconnect(midi);
abort_alsa_playback(ua);
abort_alsa_capture(ua);
- mutex_lock(&ua->mutex);
- stop_usb_playback(ua);
- stop_usb_capture(ua);
- mutex_unlock(&ua->mutex);
+ scoped_guard(mutex, &ua->mutex) {
+ stop_usb_playback(ua);
+ stop_usb_capture(ua);
+ }
free_usb_related_resources(ua, interface);
devices_used &= ~(1 << ua->card_index);
snd_card_free_when_closed(ua->card);
-
- mutex_unlock(&devices_mutex);
}
static const struct usb_device_id ua101_ids[] = {
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 63b300bc67ba..34bcbfd8b54e 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -313,8 +313,8 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
int timeout = 10;
int idx = 0, err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
return -EIO;
while (timeout-- > 0) {
@@ -324,20 +324,15 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
validx, idx, buf, val_len);
if (err >= val_len) {
*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
- err = 0;
- goto out;
+ return 0;
} else if (err == -ETIMEDOUT) {
- goto out;
+ return err;
}
}
usb_audio_dbg(chip,
"cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
request, validx, idx, cval->val_type);
- err = -EINVAL;
-
- out:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return -EINVAL;
}
static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
@@ -362,14 +357,16 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
memset(buf, 0, sizeof(buf));
- if (snd_usb_lock_shutdown(chip))
- return -EIO;
+ {
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err)
+ return -EIO;
- idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
- ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- validx, idx, buf, size);
- snd_usb_unlock_shutdown(chip);
+ idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx, idx, buf, size);
+ }
if (ret < 0) {
usb_audio_dbg(chip,
@@ -484,8 +481,8 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
buf[2] = (value_set >> 16) & 0xff;
buf[3] = (value_set >> 24) & 0xff;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
return -EIO;
while (timeout-- > 0) {
@@ -494,20 +491,14 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
usb_sndctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
validx, idx, buf, val_len);
- if (err >= 0) {
- err = 0;
- goto out;
- } else if (err == -ETIMEDOUT) {
- goto out;
- }
+ if (err >= 0)
+ return 0;
+ else if (err == -ETIMEDOUT)
+ return err;
}
usb_audio_dbg(chip, "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
request, validx, idx, cval->val_type, buf[0], buf[1]);
- err = -EINVAL;
-
- out:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return -EINVAL;
}
static int set_cur_ctl_value(struct usb_mixer_elem_info *cval,
@@ -1191,6 +1182,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 1;
}
break;
+ case USB_ID(0x3302, 0x12db): /* MOONDROP Quark2 */
+ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
+ usb_audio_info(chip,
+ "set volume quirk for MOONDROP Quark2\n");
+ cval->min = -14208; /* Mute under it */
+ }
+ break;
}
}
@@ -1494,9 +1492,11 @@ static int get_connector_value(struct usb_mixer_elem_info *cval,
validx = cval->control << 8 | 0;
- ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
- if (ret)
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err) {
+ ret = -EIO;
goto error;
+ }
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
if (cval->head.mixer->protocol == UAC_VERSION_2) {
@@ -1517,8 +1517,6 @@ static int get_connector_value(struct usb_mixer_elem_info *cval,
*val = !!uac3_conn.bmConInserted;
}
- snd_usb_unlock_shutdown(chip);
-
if (ret < 0) {
if (name && strstr(name, "Speaker")) {
if (val)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 3df537fdb9f1..828af3095b86 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -307,9 +307,9 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
struct snd_usb_audio *chip = mixer->chip;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
if (chip->usb_id == USB_ID(0x041e, 0x3042))
err = snd_usb_ctl_msg(chip->dev,
@@ -327,7 +327,6 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
usb_sndctrlpipe(chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
value, index + 2, NULL, 0);
- snd_usb_unlock_shutdown(chip);
return err;
}
@@ -438,15 +437,14 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
for (i = 0; jacks[i].name; ++i) {
snd_iprintf(buffer, "%s: ", jacks[i].name);
- err = snd_usb_lock_shutdown(mixer->chip);
- if (err < 0)
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err < 0)
return;
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0),
UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0,
jacks[i].unitid << 8, buf, 3);
- snd_usb_unlock_shutdown(mixer->chip);
if (err == 3 && (buf[0] == 3 || buf[0] == 6))
snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
else
@@ -474,21 +472,18 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
int value)
{
struct snd_usb_audio *chip = mixer->chip;
- int err;
unsigned char buf[2];
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
buf[0] = 0x01;
buf[1] = value ? 0x02 : 0x01;
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- 0x0400, 0x0e00, buf, 2);
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ 0x0400, 0x0e00, buf, 2);
}
static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
@@ -804,17 +799,14 @@ static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer,
unsigned char status)
{
struct snd_usb_audio *chip = mixer->chip;
- int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0), 0x08,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- 50, 0, &status, 1);
- snd_usb_unlock_shutdown(chip);
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ 50, 0, &status, 1);
}
static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
@@ -945,20 +937,17 @@ static int snd_mbox1_clk_switch_get(struct snd_kcontrol *kctl,
struct snd_usb_audio *chip = list->mixer->chip;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- goto err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
err = snd_mbox1_is_spdif_synced(chip);
if (err < 0)
- goto err;
+ return err;
kctl->private_value = err;
- err = 0;
ucontrol->value.enumerated.item[0] = kctl->private_value;
-err:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return 0;
}
static int snd_mbox1_clk_switch_update(struct usb_mixer_interface *mixer, int is_spdif_sync)
@@ -966,27 +955,24 @@ static int snd_mbox1_clk_switch_update(struct usb_mixer_interface *mixer, int is
struct snd_usb_audio *chip = mixer->chip;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
err = snd_mbox1_is_spdif_input(chip);
if (err < 0)
- goto err;
+ return err;
err = snd_mbox1_is_spdif_synced(chip);
if (err < 0)
- goto err;
+ return err;
/* FIXME: hardcoded sample rate */
err = snd_mbox1_set_clk_source(chip, is_spdif_sync ? 0 : 48000);
if (err < 0)
- goto err;
+ return err;
- err = snd_mbox1_is_spdif_synced(chip);
-err:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_mbox1_is_spdif_synced(chip);
}
static int snd_mbox1_clk_switch_put(struct snd_kcontrol *kctl,
@@ -1037,26 +1023,23 @@ static int snd_mbox1_src_switch_update(struct usb_mixer_interface *mixer, int is
struct snd_usb_audio *chip = mixer->chip;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
err = snd_mbox1_is_spdif_input(chip);
if (err < 0)
- goto err;
+ return err;
err = snd_mbox1_set_input_source(chip, is_spdif_input);
if (err < 0)
- goto err;
+ return err;
err = snd_mbox1_is_spdif_input(chip);
if (err < 0)
- goto err;
+ return err;
- err = snd_mbox1_is_spdif_synced(chip);
-err:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_mbox1_is_spdif_synced(chip);
}
static int snd_mbox1_src_switch_put(struct snd_kcontrol *kctl,
@@ -1167,17 +1150,14 @@ static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
{
struct snd_usb_audio *chip = list->mixer->chip;
unsigned int pval = list->kctl->private_value;
- int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
- err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
- (pval >> 16) & 0xff,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
- pval >> 24, pval & 0xffff, NULL, 0, 1000);
- snd_usb_unlock_shutdown(chip);
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+ return usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
+ (pval >> 16) & 0xff,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ pval >> 24, pval & 0xffff, NULL, 0, 1000);
}
static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
@@ -1329,23 +1309,20 @@ static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
struct snd_usb_audio *chip = list->mixer->chip;
unsigned int pval = list->kctl->private_value;
unsigned char value[2];
- int err;
value[0] = pval >> 24;
value[1] = 0;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0),
- UAC_SET_CUR,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- pval & 0xff00,
- snd_usb_ctrl_intf(list->mixer->hostif) | ((pval & 0xff) << 8),
- value, 2);
- snd_usb_unlock_shutdown(chip);
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ pval & 0xff00,
+ snd_usb_ctrl_intf(list->mixer->hostif) | ((pval & 0xff) << 8),
+ value, 2);
}
static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
@@ -1908,9 +1885,9 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
unsigned char data[3];
int rate;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
@@ -1918,15 +1895,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
/* use known values for that card: interface#1 altsetting#1 */
iface = usb_ifnum_to_if(chip->dev, 1);
- if (!iface || iface->num_altsetting < 2) {
- err = -EINVAL;
- goto end;
- }
+ if (!iface || iface->num_altsetting < 2)
+ return -EINVAL;
alts = &iface->altsetting[1];
- if (get_iface_desc(alts)->bNumEndpoints < 1) {
- err = -EINVAL;
- goto end;
- }
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
err = snd_usb_ctl_msg(chip->dev,
@@ -1938,16 +1911,13 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
data,
sizeof(data));
if (err < 0)
- goto end;
+ return err;
rate = data[0] | (data[1] << 8) | (data[2] << 16);
ucontrol->value.iec958.status[3] = (rate == 48000) ?
IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
- err = 0;
- end:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return 0;
}
static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
@@ -1957,9 +1927,9 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
u8 reg;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
err = snd_usb_ctl_msg(chip->dev,
@@ -1971,7 +1941,7 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
NULL,
0);
if (err < 0)
- goto end;
+ return err;
reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
reg |= (pval >> 12) & 0x0f;
@@ -1983,11 +1953,6 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
3,
NULL,
0);
- if (err < 0)
- goto end;
-
- end:
- snd_usb_unlock_shutdown(chip);
return err;
}
@@ -2042,23 +2007,19 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
{
struct snd_usb_audio *chip = list->mixer->chip;
u8 reg = list->kctl->private_value;
- int err;
-
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0),
- UAC_SET_CUR,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- reg,
- 9,
- NULL,
- 0);
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 9,
+ NULL,
+ 0);
}
static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
@@ -2137,21 +2098,18 @@ static int snd_soundblaster_e1_switch_update(struct usb_mixer_interface *mixer,
unsigned char state)
{
struct snd_usb_audio *chip = mixer->chip;
- int err;
unsigned char buff[2];
buff[0] = 0x02;
buff[1] = state ? 0x02 : 0x00;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
- 0x0202, 3, buff, 2);
- snd_usb_unlock_shutdown(chip);
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x0202, 3, buff, 2);
}
static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
@@ -2273,32 +2231,28 @@ static int realtek_ctl_connector_get(struct snd_kcontrol *kcontrol,
bool presence;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
err = realtek_hda_get(chip,
HDA_VERB_CMD(AC_VERB_GET_PIN_SENSE, node_id, 0),
&sense);
if (err < 0)
- goto err;
+ return err;
if (pv & REALTEK_MIC_FLAG) {
err = realtek_hda_set(chip,
HDA_VERB_CMD(AC_VERB_SET_COEF_INDEX,
REALTEK_VENDOR_REGISTERS,
REALTEK_CBJ_CTRL2));
if (err < 0)
- goto err;
+ return err;
err = realtek_hda_get(chip,
HDA_VERB_CMD(AC_VERB_GET_PROC_COEF,
REALTEK_VENDOR_REGISTERS, 0),
&cbj_ctrl2);
if (err < 0)
- goto err;
+ return err;
}
-err:
- snd_usb_unlock_shutdown(chip);
- if (err < 0)
- return err;
presence = sense & AC_PINSENSE_PRESENCE;
if (pv & REALTEK_MIC_FLAG)
@@ -2485,14 +2439,11 @@ static int snd_rme_get_status1(struct snd_kcontrol *kcontrol,
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = list->mixer->chip;
- int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
- err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, status1);
- snd_usb_unlock_shutdown(chip);
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+ return snd_rme_read_value(chip, SND_RME_GET_STATUS1, status1);
}
static int snd_rme_rate_get(struct snd_kcontrol *kcontrol,
@@ -2609,22 +2560,19 @@ static int snd_rme_current_freq_get(struct snd_kcontrol *kcontrol,
unsigned int freq;
int err;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, &status1);
if (err < 0)
- goto end;
+ return err;
err = snd_rme_read_value(chip, SND_RME_GET_CURRENT_FREQ, &den);
if (err < 0)
- goto end;
+ return err;
freq = (den == 0) ? 0 : div64_u64(num, den);
freq <<= SND_RME_CLK_FREQMUL(status1);
ucontrol->value.integer.value[0] = freq;
-
-end:
- snd_usb_unlock_shutdown(chip);
- return err;
+ return 0;
}
static int snd_rme_rate_info(struct snd_kcontrol *kcontrol,
@@ -2831,13 +2779,12 @@ enum {
static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
u8 index, u8 value)
{
- int err;
u16 usb_req, usb_idx, usb_val;
struct snd_usb_audio *chip = mixer->chip;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
if (reg == SND_BBFPRO_CTL_REG1) {
usb_req = SND_BBFPRO_USBREQ_CTL_REG1;
@@ -2854,13 +2801,10 @@ static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
usb_val = value ? usb_idx : 0;
}
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0), usb_req,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- usb_val, usb_idx, NULL, 0);
-
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), usb_req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ usb_val, usb_idx, NULL, 0);
}
static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol,
@@ -2975,7 +2919,6 @@ static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
static int snd_bbfpro_gain_update(struct usb_mixer_interface *mixer,
u8 channel, u8 gain)
{
- int err;
struct snd_usb_audio *chip = mixer->chip;
if (channel < 2) {
@@ -2986,18 +2929,15 @@ static int snd_bbfpro_gain_update(struct usb_mixer_interface *mixer,
gain = ((gain % 6) << 5) | (60 / 3);
}
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
-
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0),
- SND_BBFPRO_USBREQ_GAIN,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- gain, channel, NULL, 0);
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ SND_BBFPRO_USBREQ_GAIN,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ gain, channel, NULL, 0);
}
static int snd_bbfpro_gain_get(struct snd_kcontrol *kcontrol,
@@ -3084,14 +3024,13 @@ static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
u32 value)
{
struct snd_usb_audio *chip = mixer->chip;
- int err;
u16 idx;
u16 usb_idx, usb_val;
u32 v;
- err = snd_usb_lock_shutdown(chip);
- if (err < 0)
- return err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
idx = index & SND_BBFPRO_MIXER_IDX_MASK;
// 18 bit linear volume, split so 2 bits end up in index.
@@ -3099,15 +3038,12 @@ static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
usb_idx = idx | (v & 0x3) << 14;
usb_val = (v >> 2) & 0xffff;
- err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0),
- SND_BBFPRO_USBREQ_MIXER,
- USB_DIR_OUT | USB_TYPE_VENDOR |
- USB_RECIP_DEVICE,
- usb_val, usb_idx, NULL, 0);
-
- snd_usb_unlock_shutdown(chip);
- return err;
+ return snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0),
+ SND_BBFPRO_USBREQ_MIXER,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ usb_val, usb_idx, NULL, 0);
}
static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol,
@@ -4212,26 +4148,22 @@ static int snd_djm_controls_info(struct snd_kcontrol *kctl,
static int snd_djm_controls_update(struct usb_mixer_interface *mixer,
u8 device_idx, u8 group, u16 value)
{
- int err;
const struct snd_djm_device *device = &snd_djm_devices[device_idx];
if (group >= device->ncontrols || value >= device->controls[group].noptions)
return -EINVAL;
- err = snd_usb_lock_shutdown(mixer->chip);
- if (err)
- return err;
-
- err = snd_usb_ctl_msg(mixer->chip->dev,
- usb_sndctrlpipe(mixer->chip->dev, 0),
- USB_REQ_SET_FEATURE,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- device->controls[group].options[value],
- device->controls[group].wIndex,
- NULL, 0);
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err)
+ return pm.err;
- snd_usb_unlock_shutdown(mixer->chip);
- return err;
+ return snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ device->controls[group].options[value],
+ device->controls[group].wIndex,
+ NULL, 0);
}
static int snd_djm_controls_get(struct snd_kcontrol *kctl,
@@ -4611,10 +4543,20 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
}
/* lowest playback value is muted on some devices */
- if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_MIN_MUTE)
- if (strstr(kctl->id.name, "Playback"))
+ if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE)
+ if (strstr(kctl->id.name, "Playback")) {
+ usb_audio_info(mixer->chip,
+ "applying playback min mute quirk\n");
cval->min_mute = 1;
+ }
+ /* lowest capture value is muted on some devices */
+ if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE)
+ if (strstr(kctl->id.name, "Capture")) {
+ usb_audio_info(mixer->chip,
+ "applying capture min mute quirk\n");
+ cval->min_mute = 1;
+ }
/* ALSA-ify some Plantronics headset control names */
if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f &&
(cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME))
diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c
index fac4bbc6b275..15960d25e748 100644
--- a/sound/usb/mixer_s1810c.c
+++ b/sound/usb/mixer_s1810c.c
@@ -93,6 +93,7 @@ struct s1810c_ctl_packet {
#define SC1810C_CTL_LINE_SW 0
#define SC1810C_CTL_MUTE_SW 1
+#define SC1824C_CTL_MONO_SW 2
#define SC1810C_CTL_AB_SW 3
#define SC1810C_CTL_48V_SW 4
@@ -123,6 +124,7 @@ struct s1810c_state_packet {
#define SC1810C_STATE_48V_SW 58
#define SC1810C_STATE_LINE_SW 59
#define SC1810C_STATE_MUTE_SW 60
+#define SC1824C_STATE_MONO_SW 61
#define SC1810C_STATE_AB_SW 62
struct s1810_mixer_state {
@@ -145,12 +147,7 @@ snd_s1810c_send_ctl_packet(struct usb_device *dev, u32 a,
pkt.b = b;
pkt.c = c;
pkt.d = d;
- /*
- * Value for settings 0/1 for this
- * output channel is always 0 (probably because
- * there is no ADAT output on 1810c)
- */
- pkt.e = (c == 4) ? 0 : e;
+ pkt.e = e;
ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
SC1810C_CMD_REQ,
@@ -213,115 +210,164 @@ snd_sc1810c_get_status_field(struct usb_device *dev,
*/
static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
{
- u32 a, b, c, e, n, off;
+ u32 a, b, c, e, n, off, left, right;
struct usb_device *dev = chip->dev;
- /* Set initial volume levels ? */
- a = 0x64;
- e = 0xbc;
- for (n = 0; n < 2; n++) {
- off = n * 18;
- for (b = off; b < 18 + off; b++) {
- /* This channel to all outputs ? */
- for (c = 0; c <= 8; c++) {
+ switch (chip->usb_id) {
+ case USB_ID(0x194f, 0x010c): /* 1810c */
+ /* Set initial volume levels ? */
+ a = 0x64;
+ e = 0xbc;
+ for (n = 0; n < 2; n++) {
+ off = n * 18;
+ for (b = off; b < 18 + off; b++) {
+ /* This channel to all outputs ? */
+ for (c = 0; c <= 8; c++) {
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
+ }
+ /* This channel to main output (again) */
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
+ }
+ /*
+ * I noticed on UC that DAW channels have different
+ * initial volumes, so this makes sense.
+ */
+ e = 0xb53bf0;
+ }
+
+ /* Connect analog outputs ? */
+ a = 0x65;
+ e = 0x01000000;
+ for (b = 1; b < 3; b++) {
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
+ }
+ snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, e);
+
+ /* Set initial volume levels for S/PDIF mappings ? */
+ a = 0x64;
+ e = 0xbc;
+ c = 3;
+ for (n = 0; n < 2; n++) {
+ off = n * 18;
+ for (b = off; b < 18 + off; b++) {
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
}
- /* This channel to main output (again) */
+ e = 0xb53bf0;
+ }
+
+ /* Connect S/PDIF output ? */
+ a = 0x65;
+ e = 0x01000000;
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+
+ /* Connect all outputs (again) ? */
+ a = 0x65;
+ e = 0x01000000;
+ for (b = 0; b < 4; b++) {
snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
}
- /*
- * I noticed on UC that DAW channels have different
- * initial volumes, so this makes sense.
- */
- e = 0xb53bf0;
- }
- /* Connect analog outputs ? */
- a = 0x65;
- e = 0x01000000;
- for (b = 1; b < 3; b++) {
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
- }
- snd_s1810c_send_ctl_packet(dev, a, 0, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 0, 0, 1, e);
-
- /* Set initial volume levels for S/PDIF mappings ? */
- a = 0x64;
- e = 0xbc;
- c = 3;
- for (n = 0; n < 2; n++) {
- off = n * 18;
- for (b = off; b < 18 + off; b++) {
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
+ /* Basic routing to get sound out of the device */
+ a = 0x64;
+ e = 0x01000000;
+ for (c = 0; c < 4; c++) {
+ for (b = 0; b < 36; b++) {
+ if ((c == 0 && b == 18) || /* DAW1/2 -> Main */
+ (c == 1 && b == 20) || /* DAW3/4 -> Line3/4 */
+ (c == 2 && b == 22) || /* DAW4/5 -> Line5/6 */
+ (c == 3 && b == 24)) { /* DAW5/6 -> S/PDIF */
+ /* Left */
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
+ b++;
+ /* Right */
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
+ } else {
+ /* Leave the rest disconnected */
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
+ }
+ }
}
- e = 0xb53bf0;
- }
- /* Connect S/PDIF output ? */
- a = 0x65;
- e = 0x01000000;
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
-
- /* Connect all outputs (again) ? */
- a = 0x65;
- e = 0x01000000;
- for (b = 0; b < 4; b++) {
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, 0, 1, e);
- }
-
- /* Basic routing to get sound out of the device */
- a = 0x64;
- e = 0x01000000;
- for (c = 0; c < 4; c++) {
- for (b = 0; b < 36; b++) {
- if ((c == 0 && b == 18) || /* DAW1/2 -> Main */
- (c == 1 && b == 20) || /* DAW3/4 -> Line3/4 */
- (c == 2 && b == 22) || /* DAW4/5 -> Line5/6 */
- (c == 3 && b == 24)) { /* DAW5/6 -> S/PDIF */
- /* Left */
+ /* Set initial volume levels for S/PDIF (again) ? */
+ a = 0x64;
+ e = 0xbc;
+ c = 3;
+ for (n = 0; n < 2; n++) {
+ off = n * 18;
+ for (b = off; b < 18 + off; b++) {
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
- b++;
- /* Right */
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
- } else {
- /* Leave the rest disconnected */
- snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0);
- snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0);
}
+ e = 0xb53bf0;
}
- }
- /* Set initial volume levels for S/PDIF (again) ? */
- a = 0x64;
- e = 0xbc;
- c = 3;
- for (n = 0; n < 2; n++) {
- off = n * 18;
- for (b = off; b < 18 + off; b++) {
+ /* Connect S/PDIF outputs (again) ? */
+ a = 0x65;
+ e = 0x01000000;
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+
+ /* Again ? */
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
+ snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+ break;
+
+ case USB_ID(0x194f, 0x010d): /* 1824c */
+ /* Set all output faders to unity gain */
+ a = 0x65;
+ c = 0x00;
+ e = 0x01000000;
+
+ for (b = 0; b < 9; b++) {
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
snd_s1810c_send_ctl_packet(dev, a, b, c, 1, e);
}
- e = 0xb53bf0;
- }
-
- /* Connect S/PDIF outputs (again) ? */
- a = 0x65;
- e = 0x01000000;
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
-
- /* Again ? */
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 0, e);
- snd_s1810c_send_ctl_packet(dev, a, 3, 0, 1, e);
+ /* Set
+ * Daw 1 -> Line out 1 (left), Daw 2 -> Line out 2 (right)
+ * Daw 3 -> Line out 3, (left) Daw 4 -> Line out 4 (right)
+ * Daw 5 -> Line out 5, (left) Daw 6 -> Line out 6 (right)
+ * Daw 7 -> Line out 7, (left) Daw 8 -> Line out 8 (right)
+ * Daw 9 -> SPDIF out 1, (left) Daw 10 -> SPDIF out 2 (right)
+ * Daw 11 -> ADAT out 1, (left) Daw 12 -> ADAT out 2 (right)
+ * Daw 13 -> ADAT out 3, (left) Daw 14 -> ADAT out 4 (right)
+ * Daw 15 -> ADAT out 5, (left) Daw 16 -> ADAT out 6 (right)
+ * Daw 17 -> ADAT out 7, (left) Daw 18 -> ADAT out 8 (right)
+ * Everything else muted
+ */
+ a = 0x64;
+ /* The first Daw channel is channel 18 */
+ left = 18;
+
+ for (c = 0; c < 9; c++) {
+ right = left + 1;
+
+ for (b = 0; b < 36; b++) {
+ if (b == left) {
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x01000000);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x00);
+ } else if (b == right) {
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x00);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x01000000);
+ } else {
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 0, 0x00);
+ snd_s1810c_send_ctl_packet(dev, a, b, c, 1, 0x00);
+ }
+ }
+ left += 2;
+ }
+ break;
+ }
return 0;
}
@@ -338,18 +384,16 @@ snd_s1810c_get_switch_state(struct usb_mixer_interface *mixer,
struct s1810_mixer_state *private = mixer->private_data;
u32 field = 0;
u32 ctl_idx = (u32) (kctl->private_value & 0xFF);
- int ret = 0;
+ int ret;
- mutex_lock(&private->usb_mutex);
+ guard(mutex)(&private->usb_mutex);
ret = snd_sc1810c_get_status_field(chip->dev, &field,
ctl_idx, &private->seqnum);
if (ret < 0)
- goto unlock;
+ return ret;
*state = field;
- unlock:
- mutex_unlock(&private->usb_mutex);
- return ret ? ret : 0;
+ return ret;
}
/*
@@ -366,12 +410,9 @@ snd_s1810c_set_switch_state(struct usb_mixer_interface *mixer,
u32 pval = (u32) kctl->private_value;
u32 ctl_id = (pval >> 8) & 0xFF;
u32 ctl_val = (pval >> 16) & 0x1;
- int ret = 0;
- mutex_lock(&private->usb_mutex);
- ret = snd_s1810c_send_ctl_packet(chip->dev, 0, 0, 0, ctl_id, ctl_val);
- mutex_unlock(&private->usb_mutex);
- return ret;
+ guard(mutex)(&private->usb_mutex);
+ return snd_s1810c_send_ctl_packet(chip->dev, 0, 0, 0, ctl_id, ctl_val);
}
/* Generic get/set/init functions for switch controls */
@@ -386,12 +427,12 @@ snd_s1810c_switch_get(struct snd_kcontrol *kctl,
u32 pval = (u32) kctl->private_value;
u32 ctl_idx = pval & 0xFF;
u32 state = 0;
- int ret = 0;
+ int ret;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
ret = snd_s1810c_get_switch_state(mixer, kctl, &state);
if (ret < 0)
- goto unlock;
+ return ret;
switch (ctl_idx) {
case SC1810C_STATE_LINE_SW:
@@ -402,9 +443,7 @@ snd_s1810c_switch_get(struct snd_kcontrol *kctl,
ctl_elem->value.integer.value[0] = (long)state;
}
- unlock:
- mutex_unlock(&private->data_mutex);
- return (ret < 0) ? ret : 0;
+ return 0;
}
static int
@@ -420,10 +459,10 @@ snd_s1810c_switch_set(struct snd_kcontrol *kctl,
u32 newval = 0;
int ret = 0;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
ret = snd_s1810c_get_switch_state(mixer, kctl, &curval);
if (ret < 0)
- goto unlock;
+ return ret;
switch (ctl_idx) {
case SC1810C_STATE_LINE_SW:
@@ -435,14 +474,12 @@ snd_s1810c_switch_set(struct snd_kcontrol *kctl,
}
if (curval == newval)
- goto unlock;
+ return 0;
kctl->private_value &= ~(0x1 << 16);
kctl->private_value |= (unsigned int)(newval & 0x1) << 16;
ret = snd_s1810c_set_switch_state(mixer, kctl);
- unlock:
- mutex_unlock(&private->data_mutex);
return (ret < 0) ? 0 : 1;
}
@@ -502,6 +539,15 @@ static const struct snd_kcontrol_new snd_s1810c_mute_sw = {
.private_value = (SC1810C_STATE_MUTE_SW | SC1810C_CTL_MUTE_SW << 8)
};
+static const struct snd_kcontrol_new snd_s1824c_mono_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mono Main Out Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_s1810c_switch_get,
+ .put = snd_s1810c_switch_set,
+ .private_value = (SC1824C_STATE_MONO_SW | SC1824C_CTL_MONO_SW << 8)
+};
+
static const struct snd_kcontrol_new snd_s1810c_48v_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "48V Phantom Power On Mic Inputs Switch",
@@ -588,8 +634,17 @@ int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer)
if (ret < 0)
return ret;
- ret = snd_s1810c_switch_init(mixer, &snd_s1810c_ab_sw);
- if (ret < 0)
- return ret;
+ // The 1824c has a Mono Main switch instead of a
+ // A/B select switch.
+ if (mixer->chip->usb_id == USB_ID(0x194f, 0x010d)) {
+ ret = snd_s1810c_switch_init(mixer, &snd_s1824c_mono_sw);
+ if (ret < 0)
+ return ret;
+ } else if (mixer->chip->usb_id == USB_ID(0x194f, 0x010c)) {
+ ret = snd_s1810c_switch_init(mixer, &snd_s1810c_ab_sw);
+ if (ret < 0)
+ return ret;
+ }
+
return ret;
}
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 1ec203cbbd94..f2446bf3982c 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -10,8 +10,9 @@
* - Solo/2i2/4i4 Gen 4
* - Clarett 2Pre/4Pre/8Pre USB
* - Clarett+ 2Pre/4Pre/8Pre
+ * - Vocaster One/Two
*
- * Copyright (c) 2018-2024 by Geoffrey D. Bennett <g at b4.vu>
+ * Copyright (c) 2018-2025 by Geoffrey D. Bennett <g at b4.vu>
* Copyright (c) 2020-2021 by Vladimir Sadovnikov <sadko4u@gmail.com>
* Copyright (c) 2022 by Christian Colglazier <christian@cacolglazier.com>
*
@@ -75,6 +76,9 @@
* to many LinuxMusicians people and to Focusrite for hardware
* donations).
*
+ * Support for Vocaster One and Two added in Mar 2024 (thanks to many
+ * LinuxMusicians people and to Focusrite for hardware donations).
+ *
* This ALSA mixer gives access to (model-dependent):
* - input, output, mixer-matrix muxes
* - mixer-matrix gain stages
@@ -364,6 +368,21 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
"Mute Playback Switch", "Dim Playback Switch"
};
+/* Vocaster One speaker/headphone mute names */
+static const char *const vocaster_one_sp_hp_mute_names[] = {
+ "Speaker Mute Playback Switch",
+ "Headphones Mute Playback Switch",
+ NULL
+};
+
+/* Vocaster Two speaker/headphone mute names */
+static const char *const vocaster_two_sp_hp_mute_names[] = {
+ "Speaker Mute Playback Switch",
+ "Headphones 1 Mute Playback Switch",
+ "Headphones 2 Mute Playback Switch",
+ NULL
+};
+
/* The autogain_status is set based on the autogain_switch and
* raw_autogain_status values.
*
@@ -547,6 +566,7 @@ enum {
SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
SCARLETT2_CONFIG_SPDIF_MODE,
+ SCARLETT2_CONFIG_SP_HP_MUTE,
SCARLETT2_CONFIG_COUNT
};
@@ -814,6 +834,9 @@ static const struct scarlett2_config_set scarlett2_config_set_vocaster = {
[SCARLETT2_CONFIG_BLUETOOTH_VOLUME] = {
.offset = 0xbf, .size = 8, .activate = 28 },
+
+ [SCARLETT2_CONFIG_SP_HP_MUTE] = {
+ .offset = 0xab, .size = 8, .activate = 10 },
}
};
@@ -1177,6 +1200,9 @@ struct scarlett2_device_info {
/* additional description for the line out volume controls */
const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
+ /* Vocaster speaker/headphone mute control names */
+ const char * const *sp_hp_mute_names;
+
/* number of sources/destinations of each port type */
const int port_count[SCARLETT2_PORT_TYPE_COUNT][SCARLETT2_PORT_DIRNS];
@@ -1249,6 +1275,7 @@ struct scarlett2_data {
u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX];
u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX];
u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT];
+ u8 sp_hp_mute;
u8 air_switch[SCARLETT2_AIR_SWITCH_MAX];
u8 dsp_switch[SCARLETT2_DSP_SWITCH_MAX];
s32 compressor_values[SCARLETT2_COMPRESSOR_CTLS_MAX];
@@ -1791,6 +1818,7 @@ static const struct scarlett2_device_info vocaster_one_info = {
.peq_flt_total_count = 4,
.mute_input_count = 1,
.gain_input_count = 1,
+ .sp_hp_mute_names = vocaster_one_sp_hp_mute_names,
.port_count = {
[SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
@@ -1835,6 +1863,7 @@ static const struct scarlett2_device_info vocaster_two_info = {
.mute_input_count = 2,
.gain_input_count = 2,
.has_bluetooth = 1,
+ .sp_hp_mute_names = vocaster_two_sp_hp_mute_names,
.port_count = {
[SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
@@ -2348,7 +2377,8 @@ static int scarlett2_usb(
{
struct scarlett2_data *private = mixer->private_data;
struct usb_device *dev = mixer->chip->dev;
- struct scarlett2_usb_packet *req, *resp = NULL;
+ struct scarlett2_usb_packet *req __free(kfree) = NULL;
+ struct scarlett2_usb_packet *resp __free(kfree) = NULL;
size_t req_buf_size = struct_size(req, data, req_size);
size_t resp_buf_size = struct_size(resp, data, resp_size);
int retries = 0;
@@ -2356,18 +2386,14 @@ static int scarlett2_usb(
int err;
req = kmalloc(req_buf_size, GFP_KERNEL);
- if (!req) {
- err = -ENOMEM;
- goto error;
- }
+ if (!req)
+ return -ENOMEM;
resp = kmalloc(resp_buf_size, GFP_KERNEL);
- if (!resp) {
- err = -ENOMEM;
- goto error;
- }
+ if (!resp)
+ return -ENOMEM;
- mutex_lock(&private->usb_mutex);
+ guard(mutex)(&private->usb_mutex);
/* build request message and send it */
@@ -2389,8 +2415,7 @@ retry:
mixer->chip,
"%s USB request result cmd %x was %d\n",
private->series_name, cmd, err);
- err = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
if (!wait_for_completion_timeout(&private->cmd_done,
@@ -2400,8 +2425,7 @@ retry:
"%s USB request timed out, cmd %x\n",
private->series_name, cmd);
- err = -ETIMEDOUT;
- goto unlock;
+ return -ETIMEDOUT;
}
/* send a second message to get the response */
@@ -2418,17 +2442,14 @@ retry:
* reboot request
*/
if (cmd == SCARLETT2_USB_REBOOT &&
- (err == -ESHUTDOWN || err == -EPROTO)) {
- err = 0;
- goto unlock;
- }
+ (err == -ESHUTDOWN || err == -EPROTO))
+ return 0;
usb_audio_err(
mixer->chip,
"%s USB response result cmd %x was %d expected %zu\n",
private->series_name, cmd, err, resp_buf_size);
- err = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
/* cmd/seq/size should match except when initialising
@@ -2451,18 +2472,12 @@ retry:
resp_size, le16_to_cpu(resp->size),
le32_to_cpu(resp->error),
le32_to_cpu(resp->pad));
- err = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
if (resp_data && resp_size > 0)
memcpy(resp_data, resp->data, resp_size);
-unlock:
- mutex_unlock(&private->usb_mutex);
-error:
- kfree(req);
- kfree(resp);
return err;
}
@@ -3321,25 +3336,20 @@ static int scarlett2_sync_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->sync_updated) {
err = scarlett2_update_sync(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->sync;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static const struct snd_kcontrol_new scarlett2_sync_ctl = {
@@ -3589,17 +3599,13 @@ static int scarlett2_autogain_switch_ctl_info(
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
err = scarlett2_check_input_phantom_updated(mixer);
if (err < 0)
- goto unlock;
-
- err = snd_ctl_boolean_mono_info(kctl, uinfo);
+ return err;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return snd_ctl_boolean_mono_info(kctl, uinfo);
}
static int scarlett2_autogain_switch_ctl_get(
@@ -3610,23 +3616,18 @@ static int scarlett2_autogain_switch_ctl_get(
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
ucontrol->value.enumerated.item[0] =
private->autogain_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_autogain_status_ctl_get(
@@ -3637,23 +3638,18 @@ static int scarlett2_autogain_status_ctl_get(
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
ucontrol->value.enumerated.item[0] =
private->autogain_status[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_autogain_switch_ctl_put(
@@ -3662,46 +3658,37 @@ static int scarlett2_autogain_switch_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_input_phantom_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
- if (scarlett2_phantom_is_switching(private, index)) {
- err = -EPERM;
- goto unlock;
- }
+ if (scarlett2_phantom_is_switching(private, index))
+ return -EPERM;
oval = private->autogain_switch[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->autogain_switch[index] = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH, index, val);
- if (err == 0)
- err = 1;
scarlett2_autogain_update_access(mixer);
scarlett2_autogain_notify_access(mixer);
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_autogain_status_ctl_info(
@@ -3742,16 +3729,14 @@ static int scarlett2_ag_target_ctl_info(
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
@@ -3759,9 +3744,7 @@ static int scarlett2_ag_target_ctl_info(
uinfo->value.integer.max = 0;
uinfo->value.integer.step = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_ag_target_ctl_get(
@@ -3770,26 +3753,21 @@ static int scarlett2_ag_target_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->autogain_updated) {
err = scarlett2_update_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->ag_targets[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_ag_target_ctl_put(
@@ -3798,39 +3776,32 @@ static int scarlett2_ag_target_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->ag_targets[index];
val = clamp(ucontrol->value.integer.value[0],
(long)SCARLETT2_AG_TARGET_MIN, 0L);
if (oval == val)
- goto unlock;
+ return 0;
private->ag_targets[index] = val;
/* Send new value to the device */
err = scarlett2_usb_set_config(
mixer, scarlett2_ag_target_configs[index], 1, -val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const DECLARE_TLV_DB_MINMAX(
@@ -3885,25 +3856,20 @@ static int scarlett2_input_select_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_select_updated) {
err = scarlett2_update_input_select(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->input_select_switch;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_input_select_ctl_put(
@@ -3913,19 +3879,16 @@ static int scarlett2_input_select_ctl_put(
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
-
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->input_select_switch;
val = ucontrol->value.integer.value[0];
@@ -3936,7 +3899,7 @@ static int scarlett2_input_select_ctl_put(
val = info->gain_input_count - 1;
if (oval == val)
- goto unlock;
+ return 0;
private->input_select_switch = val;
@@ -3944,12 +3907,8 @@ static int scarlett2_input_select_ctl_put(
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
0, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_input_select_ctl_info(
@@ -3958,43 +3917,38 @@ static int scarlett2_input_select_ctl_info(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int inputs = private->info->gain_input_count;
int i, err;
- char **values = kcalloc(inputs, sizeof(char *), GFP_KERNEL);
+ char **values __free(kfree) = NULL;
+ values = kcalloc(inputs, sizeof(char *), GFP_KERNEL);
if (!values)
return -ENOMEM;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
/* Loop through each input */
for (i = 0; i < inputs; i++) {
values[i] = kasprintf(GFP_KERNEL, "Input %d", i + 1);
if (!values[i]) {
err = -ENOMEM;
- goto unlock;
+ goto clear;
}
}
err = snd_ctl_enum_info(uinfo, 1, i,
(const char * const *)values);
-unlock:
- mutex_unlock(&private->data_mutex);
-
+clear:
for (i = 0; i < inputs; i++)
kfree(values[i]);
- kfree(values);
return err;
}
@@ -4020,22 +3974,16 @@ static int scarlett2_autogain_disables_ctl_info(struct snd_kcontrol *kctl,
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
-
- err = snd_ctl_boolean_mono_info(kctl, uinfo);
+ return err;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return snd_ctl_boolean_mono_info(kctl, uinfo);
}
static int scarlett2_input_link_ctl_get(
@@ -4044,26 +3992,21 @@ static int scarlett2_input_link_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_select_updated) {
err = scarlett2_update_input_select(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] =
private->input_link_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_input_link_ctl_put(
@@ -4072,37 +4015,30 @@ static int scarlett2_input_link_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->input_link_switch[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->input_link_switch[index] = val;
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_input_link_ctl = {
@@ -4138,16 +4074,14 @@ static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl,
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = elem->channels;
@@ -4155,9 +4089,7 @@ static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl,
uinfo->value.integer.max = SCARLETT2_MAX_GAIN_VALUE;
uinfo->value.integer.step = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_input_gain_ctl_get(struct snd_kcontrol *kctl,
@@ -4166,26 +4098,21 @@ static int scarlett2_input_gain_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_gain_updated) {
err = scarlett2_update_input_gain(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] =
private->gain[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl,
@@ -4194,38 +4121,31 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->gain[index];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->gain[index] = val;
/* Send gain change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_INPUT_GAIN,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
@@ -4262,26 +4182,21 @@ static int scarlett2_safe_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_safe_updated) {
err = scarlett2_update_input_safe(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] =
private->safe_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl,
@@ -4290,38 +4205,31 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->safe_switch[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->safe_switch[index] = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_safe_ctl = {
@@ -4356,20 +4264,17 @@ static int scarlett2_pcm_input_switch_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = elem->head.mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
if (private->pcm_input_switch_updated) {
err = scarlett2_update_pcm_input_switch(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->pcm_input_switch;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_pcm_input_switch_ctl_put(
@@ -4378,21 +4283,18 @@ static int scarlett2_pcm_input_switch_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err;
- int oval, val, err = 0;
-
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->pcm_input_switch;
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->pcm_input_switch = val;
@@ -4400,12 +4302,8 @@ static int scarlett2_pcm_input_switch_ctl_put(
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
0, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_pcm_input_switch_ctl_info(
@@ -4492,25 +4390,20 @@ static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->vol_updated) {
err = scarlett2_update_volumes(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->master_vol;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_headphone_volume_ctl_get(
@@ -4520,25 +4413,20 @@ static int scarlett2_headphone_volume_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->vol_updated) {
err = scarlett2_update_volumes(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->headphone_vol;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int line_out_remap(struct scarlett2_data *private, int index)
@@ -4561,25 +4449,20 @@ static int scarlett2_volume_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->vol_updated) {
err = scarlett2_update_volumes(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->vol[index];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl,
@@ -4589,30 +4472,24 @@ static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->vol[index];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->vol[index] = val;
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
index, val - SCARLETT2_VOLUME_BIAS);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const DECLARE_TLV_DB_MINMAX(
@@ -4691,25 +4568,20 @@ static int scarlett2_mute_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->dim_mute_updated) {
err = scarlett2_update_dim_mute(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->mute_switch[index];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_mute_ctl_put(struct snd_kcontrol *kctl,
@@ -4719,32 +4591,26 @@ static int scarlett2_mute_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->mute_switch[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->mute_switch[index] = val;
/* Send mute change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_mute_ctl = {
@@ -4863,28 +4729,22 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
struct scarlett2_data *private = mixer->private_data;
int ctl_index = elem->control;
int index = line_out_remap(private, ctl_index);
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->vol_sw_hw_switch[index];
val = !!ucontrol->value.enumerated.item[0];
if (oval == val)
- goto unlock;
+ return 0;
err = scarlett2_sw_hw_change(mixer, ctl_index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = {
@@ -4924,22 +4784,16 @@ static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl,
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
-
- err = snd_ctl_enum_info(uinfo, 1, 2, values);
+ return err;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return snd_ctl_enum_info(uinfo, 1, 2, values);
}
static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
@@ -4949,28 +4803,22 @@ static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
-
int index = elem->control + info->level_input_first;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_level_updated) {
err = scarlett2_update_input_level(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = scarlett2_decode_muteable(
private->level_switch[index]);
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
@@ -4980,26 +4828,23 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
-
int index = elem->control + info->level_input_first;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->level_switch[index];
val = !!ucontrol->value.enumerated.item[0];
if (oval == val)
- goto unlock;
+ return 0;
private->level_switch[index] = val;
@@ -5010,12 +4855,8 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_level_enum_ctl = {
@@ -5049,26 +4890,21 @@ static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_pad_updated) {
err = scarlett2_update_input_pad(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] =
private->pad_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl,
@@ -5077,34 +4913,27 @@ static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->pad_switch[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->pad_switch[index] = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PAD_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_pad_ctl = {
@@ -5138,25 +4967,20 @@ static int scarlett2_air_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_air_updated) {
err = scarlett2_update_input_air(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->air_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
@@ -5165,38 +4989,31 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->air_switch[index];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->air_switch[index] = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_AIR_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_air_with_drive_ctl_info(
@@ -5210,22 +5027,16 @@ static int scarlett2_air_with_drive_ctl_info(
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_autogain_updated(mixer);
if (err < 0)
- goto unlock;
-
- err = snd_ctl_enum_info(uinfo, 1, 3, values);
+ return err;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return snd_ctl_enum_info(uinfo, 1, 3, values);
}
static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
@@ -5268,25 +5079,20 @@ static int scarlett2_dsp_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_dsp_updated) {
err = scarlett2_update_input_dsp(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->dsp_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_dsp_ctl_put(struct snd_kcontrol *kctl,
@@ -5295,38 +5101,31 @@ static int scarlett2_dsp_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->dsp_switch[index];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->dsp_switch[index] = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DSP_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_dsp_ctl = {
@@ -5389,30 +5188,26 @@ static int scarlett2_compressor_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int channel = index / SCARLETT2_COMPRESSOR_PARAM_COUNT;
int param_index = index % SCARLETT2_COMPRESSOR_PARAM_COUNT;
const struct compressor_param *param = &compressor_params[param_index];
-
int oval, val, err;
s32 scaled_val;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->compressor_values[index];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->compressor_values[index] = val;
@@ -5427,19 +5222,12 @@ static int scarlett2_compressor_ctl_put(
err = scarlett2_usb_set_data(
mixer, private->config_set->param_buf_addr + 1, 1, channel);
if (err < 0)
- goto unlock;
+ return err;
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS, index, scaled_val);
- if (err < 0)
- goto unlock;
- if (err == 0)
- err = 1;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_compressor_ctl_info(
@@ -5495,20 +5283,18 @@ static int scarlett2_precomp_flt_switch_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->precomp_flt_switch[elem->control];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->precomp_flt_switch[elem->control] = val;
@@ -5516,12 +5302,8 @@ static int scarlett2_precomp_flt_switch_ctl_put(
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
elem->control, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_peq_flt_switch_ctl_put(
@@ -5530,20 +5312,18 @@ static int scarlett2_peq_flt_switch_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->peq_flt_switch[elem->control];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->peq_flt_switch[elem->control] = val;
@@ -5551,12 +5331,8 @@ static int scarlett2_peq_flt_switch_ctl_put(
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
elem->control, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_precomp_flt_switch_ctl = {
@@ -5677,20 +5453,17 @@ static int scarlett2_precomp_flt_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control * SCARLETT2_BIQUAD_COEFFS;
int i, oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
/* Check if any of the values have changed; if not, return */
for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) {
@@ -5701,7 +5474,7 @@ static int scarlett2_precomp_flt_ctl_put(
}
if (i == SCARLETT2_BIQUAD_COEFFS)
- goto unlock;
+ return 0;
/* Update the values */
for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++)
@@ -5712,19 +5485,14 @@ static int scarlett2_precomp_flt_ctl_put(
err = scarlett2_usb_set_data(
mixer, private->config_set->param_buf_addr, 1, index);
if (err < 0)
- goto unlock;
+ return err;
err = scarlett2_usb_set_config_buf(
mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
index, SCARLETT2_BIQUAD_COEFFS,
&private->precomp_flt_values[index]);
- if (err == 0)
- err = 1;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_peq_flt_ctl_put(
@@ -5734,7 +5502,6 @@ static int scarlett2_peq_flt_ctl_put(
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
-
int src_index = elem->control * SCARLETT2_BIQUAD_COEFFS;
int dst_index = (
elem->control /
@@ -5744,16 +5511,14 @@ static int scarlett2_peq_flt_ctl_put(
) * SCARLETT2_BIQUAD_COEFFS;
int i, oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
/* Check if any of the values have changed; if not, return */
for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) {
@@ -5764,7 +5529,7 @@ static int scarlett2_peq_flt_ctl_put(
}
if (i == SCARLETT2_BIQUAD_COEFFS)
- goto unlock;
+ return 0;
/* Update the values */
for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++)
@@ -5775,19 +5540,14 @@ static int scarlett2_peq_flt_ctl_put(
err = scarlett2_usb_set_data(
mixer, private->config_set->param_buf_addr, 1, dst_index);
if (err < 0)
- goto unlock;
+ return err;
err = scarlett2_usb_set_config_buf(
mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
dst_index, SCARLETT2_BIQUAD_COEFFS,
&private->peq_flt_values[src_index]);
- if (err == 0)
- err = 1;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_flt_ctl_info(
@@ -5840,26 +5600,21 @@ static int scarlett2_input_mute_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->input_mute_updated) {
err = scarlett2_update_input_mute(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] =
private->input_mute_switch[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_input_mute_ctl_put(struct snd_kcontrol *kctl,
@@ -5868,26 +5623,23 @@ static int scarlett2_input_mute_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->input_mute_switch[index];
val = ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->input_mute_switch[index] = val;
@@ -5895,12 +5647,8 @@ static int scarlett2_input_mute_ctl_put(struct snd_kcontrol *kctl,
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_input_mute_ctl = {
@@ -6011,23 +5759,18 @@ static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl,
struct scarlett2_data *private = mixer->private_data;
int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_input_phantom_updated(mixer);
if (err < 0)
- goto unlock;
+ return err;
ucontrol->value.integer.value[0] = scarlett2_decode_muteable(
private->phantom_switch[elem->control]);
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
@@ -6037,26 +5780,23 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
-
int index = elem->control;
int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_check_put_during_autogain(mixer);
if (err < 0)
- goto unlock;
+ return err;
oval = private->phantom_switch[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->phantom_switch[index] = val;
@@ -6067,15 +5807,10 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH,
index + info->phantom_first, val);
- if (err == 0)
- err = 1;
-
scarlett2_phantom_update_access(mixer);
scarlett2_phantom_notify_access(mixer);
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_phantom_ctl = {
@@ -6104,34 +5839,27 @@ static int scarlett2_phantom_persistence_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->phantom_persistence;
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->phantom_persistence = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_phantom_persistence_ctl = {
@@ -6222,25 +5950,20 @@ static int scarlett2_speaker_switch_enum_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->monitor_other_updated) {
err = scarlett2_update_monitor_other(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->speaker_switching_switch;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
/* when speaker switching gets enabled, switch the main/alt speakers
@@ -6306,21 +6029,18 @@ static int scarlett2_speaker_switch_enum_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err;
- int oval, val, err = 0;
-
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->speaker_switching_switch;
val = min(ucontrol->value.enumerated.item[0], 2U);
if (oval == val)
- goto unlock;
+ return 0;
private->speaker_switching_switch = val;
@@ -6329,14 +6049,14 @@ static int scarlett2_speaker_switch_enum_ctl_put(
mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE,
0, !!val);
if (err < 0)
- goto unlock;
+ return err;
/* if speaker switching is enabled, select main or alt */
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH,
0, val == 2);
if (err < 0)
- goto unlock;
+ return err;
/* update controls if speaker switching gets enabled or disabled */
if (!oval && val)
@@ -6344,12 +6064,7 @@ static int scarlett2_speaker_switch_enum_ctl_put(
else if (oval && !val)
scarlett2_speaker_switch_disable(mixer);
- if (err == 0)
- err = 1;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_speaker_switch_enum_ctl = {
@@ -6392,25 +6107,20 @@ static int scarlett2_talkback_enum_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->monitor_other_updated) {
err = scarlett2_update_monitor_other(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->talkback_switch;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_talkback_enum_ctl_put(
@@ -6419,21 +6129,18 @@ static int scarlett2_talkback_enum_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err;
- int oval, val, err = 0;
-
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->talkback_switch;
val = min(ucontrol->value.enumerated.item[0], 2U);
if (oval == val)
- goto unlock;
+ return 0;
private->talkback_switch = val;
@@ -6442,18 +6149,14 @@ static int scarlett2_talkback_enum_ctl_put(
mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE,
1, !!val);
if (err < 0)
- goto unlock;
+ return err;
/* if talkback is enabled, select main or alt */
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH,
1, val == 2);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_talkback_enum_ctl = {
@@ -6484,21 +6187,19 @@ static int scarlett2_talkback_map_ctl_put(
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = elem->control;
- int oval, val, err = 0, i;
+ int oval, val, err, i;
u16 bitmap = 0;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->talkback_map[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->talkback_map[index] = val;
@@ -6508,12 +6209,8 @@ static int scarlett2_talkback_map_ctl_put(
/* Send updated bitmap to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_TALKBACK_MAP,
0, bitmap);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_talkback_map_ctl = {
@@ -6561,25 +6258,20 @@ static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->dim_mute_updated) {
err = scarlett2_update_dim_mute(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->dim_mute[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
@@ -6589,29 +6281,24 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = elem->control;
- int oval, val, err = 0, i;
+ int oval, val, err, i;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->dim_mute[index];
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->dim_mute[index] = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DIM_MUTE,
index, val);
- if (err == 0)
- err = 1;
-
if (index == SCARLETT2_BUTTON_MUTE)
for (i = 0; i < private->num_line_out; i++) {
int line_index = line_out_remap(private, i);
@@ -6624,9 +6311,7 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
}
}
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_dim_mute_ctl = {
@@ -6637,6 +6322,61 @@ static const struct snd_kcontrol_new scarlett2_dim_mute_ctl = {
.put = scarlett2_dim_mute_ctl_put
};
+/*** Vocaster Speaker/Headphone Mute Controls ***/
+
+static int scarlett2_sp_hp_mute_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct scarlett2_data *private = elem->head.mixer->private_data;
+
+ ucontrol->value.integer.value[0] =
+ !!(private->sp_hp_mute & (1 << elem->control));
+
+ return 0;
+}
+
+static int scarlett2_sp_hp_mute_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int index = elem->control;
+ int val, err;
+
+ guard(mutex)(&private->data_mutex);
+
+ if (private->hwdep_in_use)
+ return -EBUSY;
+
+ val = private->sp_hp_mute;
+
+ if (ucontrol->value.integer.value[0])
+ val |= (1 << index);
+ else
+ val &= ~(1 << index);
+
+ if (val == private->sp_hp_mute)
+ return 0;
+
+ private->sp_hp_mute = val;
+
+ /* Send change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SP_HP_MUTE,
+ 0, val);
+
+ return err < 0 ? err : 1;
+}
+
+static const struct snd_kcontrol_new scarlett2_sp_hp_mute_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_sp_hp_mute_ctl_get,
+ .put = scarlett2_sp_hp_mute_ctl_put
+};
+
/*** Create the analogue output controls ***/
static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
@@ -6669,6 +6409,17 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
return err;
}
+ /* Add Vocaster speaker/headphone mute controls */
+ if (private->info->sp_hp_mute_names)
+ for (i = 0; private->info->sp_hp_mute_names[i]; i++) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_sp_hp_mute_ctl,
+ i, 1, private->info->sp_hp_mute_names[i],
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
/* Remaining controls are only applicable if the device
* has per-channel line-out volume controls.
*/
@@ -7038,25 +6789,20 @@ static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->mix_updated) {
err = scarlett2_update_mix(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->mix[elem->control];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
@@ -7065,15 +6811,13 @@ static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, mix_num, err = 0;
+ int oval, val, mix_num, err;
int index = elem->control;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->mix[index];
val = clamp(ucontrol->value.integer.value[0],
@@ -7081,16 +6825,12 @@ static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
mix_num = index / private->num_mix_in;
if (oval == val)
- goto unlock;
+ return 0;
private->mix[index] = val;
err = scarlett2_usb_set_mix(mixer, mix_num);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const DECLARE_TLV_DB_MINMAX(
@@ -7177,25 +6917,20 @@ static int scarlett2_direct_monitor_ctl_get(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->direct_monitor_updated) {
err = scarlett2_update_direct_monitor(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->direct_monitor_switch;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_direct_monitor_ctl_put(
@@ -7204,34 +6939,27 @@ static int scarlett2_direct_monitor_ctl_put(
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
-
int index = elem->control;
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->direct_monitor_switch;
val = min(ucontrol->value.enumerated.item[0], 2U);
if (oval == val)
- goto unlock;
+ return 0;
private->direct_monitor_switch = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, index, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_direct_monitor_stereo_enum_ctl_info(
@@ -7281,33 +7009,27 @@ static int scarlett2_monitor_mix_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, err = 0;
+ int oval, val, err;
int index = elem->control;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->monitor_mix[index];
val = clamp(ucontrol->value.integer.value[0],
0L, (long)SCARLETT2_MIXER_MAX_VALUE);
if (oval == val)
- goto unlock;
+ return 0;
private->monitor_mix[index] = val;
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
index, scarlett2_mixer_values[val]);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_monitor_mix_ctl = {
@@ -7433,25 +7155,20 @@ static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->mux_updated) {
err = scarlett2_usb_get_mux(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.enumerated.item[0] = private->mux[index];
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl,
@@ -7461,30 +7178,24 @@ static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
int index = line_out_remap(private, elem->control);
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->mux[index];
val = min(ucontrol->value.enumerated.item[0],
private->num_mux_srcs - 1U);
if (oval == val)
- goto unlock;
+ return 0;
private->mux[index] = val;
err = scarlett2_usb_set_mux(mixer);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = {
@@ -7561,17 +7272,15 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
u16 meter_levels[SCARLETT2_MAX_METERS];
int i, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
err = scarlett2_usb_get_meter_levels(mixer, elem->channels,
meter_levels);
if (err < 0)
- goto unlock;
+ return err;
/* copy & translate from meter_levels[] using meter_level_map[] */
for (i = 0; i < elem->channels; i++) {
@@ -7586,10 +7295,7 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
ucontrol->value.integer.value[i] = value;
}
-unlock:
- mutex_unlock(&private->data_mutex);
-
- return err;
+ return 0;
}
static const struct snd_kcontrol_new scarlett2_meter_ctl = {
@@ -7631,33 +7337,26 @@ static int scarlett2_msd_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err;
- int oval, val, err = 0;
-
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->msd_switch;
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->msd_switch = val;
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MSD_SWITCH,
0, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_msd_ctl = {
@@ -7702,21 +7401,18 @@ static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
+ int oval, val, err;
- int oval, val, err = 0;
-
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->standalone_switch;
val = !!ucontrol->value.integer.value[0];
if (oval == val)
- goto unlock;
+ return 0;
private->standalone_switch = val;
@@ -7724,12 +7420,8 @@ static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl,
err = scarlett2_usb_set_config(mixer,
SCARLETT2_CONFIG_STANDALONE_SWITCH,
0, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_standalone_ctl = {
@@ -7789,20 +7481,17 @@ static int scarlett2_power_status_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
if (private->power_status_updated) {
err = scarlett2_update_power_status(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->power_status;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_power_status_ctl_info(
@@ -7864,25 +7553,20 @@ static int scarlett2_bluetooth_volume_ctl_get(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int err = 0;
+ int err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
if (private->bluetooth_updated) {
err = scarlett2_update_bluetooth_volume(mixer);
if (err < 0)
- goto unlock;
+ return err;
}
ucontrol->value.integer.value[0] = private->bluetooth_volume;
-
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return 0;
}
static int scarlett2_bluetooth_volume_ctl_put(struct snd_kcontrol *kctl,
@@ -7891,32 +7575,26 @@ static int scarlett2_bluetooth_volume_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, err = 0;
+ int oval, val, err;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
- if (private->hwdep_in_use) {
- err = -EBUSY;
- goto unlock;
- }
+ if (private->hwdep_in_use)
+ return -EBUSY;
oval = private->bluetooth_volume;
val = clamp(ucontrol->value.integer.value[0],
0L, (long)SCARLETT2_MAX_BLUETOOTH_VOLUME);
if (oval == val)
- goto unlock;
+ return 0;
private->bluetooth_volume = val;
err = scarlett2_usb_set_config(mixer,
SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
0, val);
- if (err == 0)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_bluetooth_volume_ctl_info(
@@ -8011,39 +7689,31 @@ static int scarlett2_spdif_mode_ctl_put(struct snd_kcontrol *kctl,
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, err = 0;
+ int oval, val, err;
int i;
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
oval = private->spdif_mode;
val = ucontrol->value.enumerated.item[0];
- if (val < 0) {
- err = -EINVAL;
- goto unlock;
- }
+ if (val < 0)
+ return -EINVAL;
for (i = 0; i <= val; i++)
- if (private->info->spdif_mode_values[i] == 0xff) {
- err = -EINVAL;
- goto unlock;
- }
+ if (private->info->spdif_mode_values[i] == 0xff)
+ return -EINVAL;
if (oval == val)
- goto unlock;
+ return 0;
private->spdif_mode = val;
err = scarlett2_usb_set_config(
mixer, SCARLETT2_CONFIG_SPDIF_MODE, 0,
private->info->spdif_mode_values[val]);
- if (!err)
- err = 1;
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_spdif_mode_ctl = {
@@ -9140,9 +8810,8 @@ static int snd_scarlett2_controls_create(
*/
static void scarlett2_lock(struct scarlett2_data *private)
{
- mutex_lock(&private->data_mutex);
+ guard(mutex)(&private->data_mutex);
private->hwdep_in_use = 1;
- mutex_unlock(&private->data_mutex);
}
/* Call SCARLETT2_USB_GET_ERASE to get the erase progress */
@@ -9414,7 +9083,7 @@ static long scarlett2_hwdep_read(struct snd_hwdep *hw,
__le32 len;
} __packed req;
- u8 *resp;
+ u8 *resp __free(kfree) = NULL;
/* Flash segment must first be selected */
if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED)
@@ -9460,20 +9129,14 @@ static long scarlett2_hwdep_read(struct snd_hwdep *hw,
err = scarlett2_usb(mixer, SCARLETT2_USB_READ_SEGMENT,
&req, sizeof(req), resp, count);
if (err < 0)
- goto error;
+ return err;
/* Copy the response to userspace */
- if (copy_to_user(buf, resp, count)) {
- err = -EFAULT;
- goto error;
- }
+ if (copy_to_user(buf, resp, count))
+ return -EFAULT;
*offset += count;
- err = count;
-
-error:
- kfree(resp);
- return err;
+ return count;
}
static long scarlett2_hwdep_write(struct snd_hwdep *hw,
@@ -9491,7 +9154,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
__le32 offset;
__le32 pad;
u8 data[];
- } __packed *req;
+ } __packed *req __free(kfree) = NULL;
/* Calculate the maximum permitted in data[] */
const size_t max_data_size = SCARLETT2_FLASH_RW_MAX -
@@ -9545,22 +9208,16 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
req->offset = cpu_to_le32(*offset);
req->pad = 0;
- if (copy_from_user(req->data, buf, count)) {
- err = -EFAULT;
- goto error;
- }
+ if (copy_from_user(req->data, buf, count))
+ return -EFAULT;
err = scarlett2_usb(mixer, SCARLETT2_USB_WRITE_SEGMENT,
req, len, NULL, 0);
if (err < 0)
- goto error;
+ return err;
*offset += count;
- err = count;
-
-error:
- kfree(req);
- return err;
+ return count;
}
static int scarlett2_hwdep_release(struct snd_hwdep *hw, struct file *file)
@@ -9610,7 +9267,7 @@ static ssize_t scarlett2_devmap_read(
loff_t pos)
{
struct usb_mixer_interface *mixer = entry->private_data;
- u8 *resp_buf;
+ u8 *resp_buf __free(kfree) = NULL;
const size_t block_size = SCARLETT2_DEVMAP_BLOCK_SIZE;
size_t copied = 0;
@@ -9649,15 +9306,11 @@ static ssize_t scarlett2_devmap_read(
req = cpu_to_le32(pos / block_size);
err = scarlett2_usb(mixer, SCARLETT2_USB_GET_DEVMAP,
&req, sizeof(req), resp_buf, read_size);
- if (err < 0) {
- kfree(resp_buf);
+ if (err < 0)
return copied ? copied : err;
- }
- if (copy_to_user(buf, resp_buf + offset, copy_size)) {
- kfree(resp_buf);
+ if (copy_to_user(buf, resp_buf + offset, copy_size))
return -EFAULT;
- }
buf += copy_size;
pos += copy_size;
@@ -9665,7 +9318,6 @@ static ssize_t scarlett2_devmap_read(
count -= copy_size;
}
- kfree(resp_buf);
return copied;
}
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
index 236b69054867..1c5712c31f5e 100644
--- a/sound/usb/mixer_us16x08.c
+++ b/sound/usb/mixer_us16x08.c
@@ -152,12 +152,11 @@ static int snd_us16x08_recv_urb(struct snd_usb_audio *chip,
unsigned char *buf, int size)
{
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0),
SND_US16X08_URB_METER_REQUEST,
SND_US16X08_URB_METER_REQUESTTYPE, 0, 0, buf, size);
- mutex_unlock(&chip->mutex);
return 0;
}
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index bff92505e408..54d01dfd820f 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -77,10 +77,10 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
if (atomic_read(&subs->stream->chip->shutdown))
return SNDRV_PCM_POS_XRUN;
- spin_lock(&subs->lock);
- hwptr_done = subs->hwptr_done;
- runtime->delay = snd_usb_pcm_delay(subs, runtime);
- spin_unlock(&subs->lock);
+ scoped_guard(spinlock, &subs->lock) {
+ hwptr_done = subs->hwptr_done;
+ runtime->delay = snd_usb_pcm_delay(subs, runtime);
+ }
return bytes_to_frames(runtime, hwptr_done);
}
@@ -560,9 +560,9 @@ int snd_usb_hw_params(struct snd_usb_substream *subs,
subs->sync_endpoint);
}
- mutex_lock(&chip->mutex);
- subs->cur_audiofmt = fmt;
- mutex_unlock(&chip->mutex);
+ scoped_guard(mutex, &chip->mutex) {
+ subs->cur_audiofmt = fmt;
+ }
if (!subs->data_endpoint->need_setup)
goto unlock;
@@ -611,14 +611,14 @@ int snd_usb_hw_free(struct snd_usb_substream *subs)
struct snd_usb_audio *chip = subs->stream->chip;
snd_media_stop_pipeline(subs);
- mutex_lock(&chip->mutex);
- subs->cur_audiofmt = NULL;
- mutex_unlock(&chip->mutex);
- if (!snd_usb_lock_shutdown(chip)) {
+ scoped_guard(mutex, &chip->mutex) {
+ subs->cur_audiofmt = NULL;
+ }
+ CLASS(snd_usb_lock, pm)(chip);
+ if (!pm.err) {
if (stop_endpoints(subs, false))
sync_pending_stops(subs);
close_endpoints(chip, subs);
- snd_usb_unlock_shutdown(chip);
}
return 0;
@@ -675,28 +675,26 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
int retry = 0;
int ret;
- ret = snd_usb_lock_shutdown(chip);
- if (ret < 0)
- return ret;
- if (snd_BUG_ON(!subs->data_endpoint)) {
- ret = -EIO;
- goto unlock;
- }
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+ if (snd_BUG_ON(!subs->data_endpoint))
+ return -EIO;
ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
if (ret < 0)
- goto unlock;
+ return ret;
again:
if (subs->sync_endpoint) {
ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);
if (ret < 0)
- goto unlock;
+ return ret;
}
ret = snd_usb_endpoint_prepare(chip, subs->data_endpoint);
if (ret < 0)
- goto unlock;
+ return ret;
else if (ret > 0)
snd_usb_set_format_quirk(subs, subs->cur_audiofmt);
ret = 0;
@@ -722,8 +720,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
goto again;
}
}
- unlock:
- snd_usb_unlock_shutdown(chip);
+
return ret;
}
@@ -1244,13 +1241,11 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
struct snd_usb_audio *chip = subs->stream->chip;
int ret;
- mutex_lock(&chip->mutex);
- if (subs->opened) {
- mutex_unlock(&chip->mutex);
- return -EBUSY;
+ scoped_guard(mutex, &chip->mutex) {
+ if (subs->opened)
+ return -EBUSY;
+ subs->opened = 1;
}
- subs->opened = 1;
- mutex_unlock(&chip->mutex);
runtime->hw = snd_usb_hardware;
/* need an explicit sync to catch applptr update in low-latency mode */
@@ -1281,9 +1276,9 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
err_resume:
snd_usb_autosuspend(subs->stream->chip);
err_open:
- mutex_lock(&chip->mutex);
- subs->opened = 0;
- mutex_unlock(&chip->mutex);
+ scoped_guard(mutex, &chip->mutex) {
+ subs->opened = 0;
+ }
return ret;
}
@@ -1298,18 +1293,20 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
snd_media_stop_pipeline(subs);
- if (!snd_usb_lock_shutdown(subs->stream->chip)) {
+ {
+ CLASS(snd_usb_lock, pm)(subs->stream->chip);
+ if (pm.err)
+ return pm.err;
ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1);
- snd_usb_unlock_shutdown(subs->stream->chip);
if (ret < 0)
return ret;
}
subs->pcm_substream = NULL;
snd_usb_autosuspend(subs->stream->chip);
- mutex_lock(&chip->mutex);
- subs->opened = 0;
- mutex_unlock(&chip->mutex);
+ scoped_guard(mutex, &chip->mutex) {
+ subs->opened = 0;
+ }
return 0;
}
@@ -1325,7 +1322,6 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
unsigned int stride, frames, bytes, oldptr;
int i, period_elapsed = 0;
- unsigned long flags;
unsigned char *cp;
int current_frame_number;
@@ -1358,22 +1354,21 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
oldbytes, bytes);
}
/* update the current pointer */
- spin_lock_irqsave(&subs->lock, flags);
- oldptr = subs->hwptr_done;
- subs->hwptr_done += bytes;
- if (subs->hwptr_done >= subs->buffer_bytes)
- subs->hwptr_done -= subs->buffer_bytes;
- frames = (bytes + (oldptr % stride)) / stride;
- subs->transfer_done += frames;
- if (subs->transfer_done >= runtime->period_size) {
- subs->transfer_done -= runtime->period_size;
- period_elapsed = 1;
- }
-
- /* realign last_frame_number */
- subs->last_frame_number = current_frame_number;
+ scoped_guard(spinlock_irqsave, &subs->lock) {
+ oldptr = subs->hwptr_done;
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= subs->buffer_bytes)
+ subs->hwptr_done -= subs->buffer_bytes;
+ frames = (bytes + (oldptr % stride)) / stride;
+ subs->transfer_done += frames;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
+ }
- spin_unlock_irqrestore(&subs->lock, flags);
+ /* realign last_frame_number */
+ subs->last_frame_number = current_frame_number;
+ }
/* copy a data chunk */
if (oldptr + bytes > subs->buffer_bytes) {
unsigned int bytes1 = subs->buffer_bytes - oldptr;
@@ -1533,8 +1528,6 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
int counts;
unsigned int transfer_done, frame_limit, avail = 0;
int i, stride, period_elapsed = 0;
- unsigned long flags;
- int err = 0;
stride = ep->stride;
@@ -1542,106 +1535,101 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
ctx->queued = 0;
urb->number_of_packets = 0;
- spin_lock_irqsave(&subs->lock, flags);
- frame_limit = subs->frame_limit + ep->max_urb_frames;
- transfer_done = subs->transfer_done;
-
- if (subs->lowlatency_playback &&
- runtime->state != SNDRV_PCM_STATE_DRAINING) {
- unsigned int hwptr = subs->hwptr_done / stride;
+ scoped_guard(spinlock_irqsave, &subs->lock) {
+ frame_limit = subs->frame_limit + ep->max_urb_frames;
+ transfer_done = subs->transfer_done;
- /* calculate the byte offset-in-buffer of the appl_ptr */
- avail = (runtime->control->appl_ptr - runtime->hw_ptr_base)
- % runtime->buffer_size;
- if (avail <= hwptr)
- avail += runtime->buffer_size;
- avail -= hwptr;
- }
+ if (subs->lowlatency_playback &&
+ runtime->state != SNDRV_PCM_STATE_DRAINING) {
+ unsigned int hwptr = subs->hwptr_done / stride;
+
+ /* calculate the byte offset-in-buffer of the appl_ptr */
+ avail = (runtime->control->appl_ptr - runtime->hw_ptr_base)
+ % runtime->buffer_size;
+ if (avail <= hwptr)
+ avail += runtime->buffer_size;
+ avail -= hwptr;
+ }
- for (i = 0; i < ctx->packets; i++) {
- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
- if (counts < 0)
- break;
- /* set up descriptor */
- urb->iso_frame_desc[i].offset = frames * stride;
- urb->iso_frame_desc[i].length = counts * stride;
- frames += counts;
- avail -= counts;
- urb->number_of_packets++;
- transfer_done += counts;
- if (transfer_done >= runtime->period_size) {
- transfer_done -= runtime->period_size;
- frame_limit = 0;
- period_elapsed = 1;
- if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
- if (transfer_done > 0) {
- /* FIXME: fill-max mode is not
- * supported yet */
- frames -= transfer_done;
- counts -= transfer_done;
- urb->iso_frame_desc[i].length =
- counts * stride;
- transfer_done = 0;
- }
- i++;
- if (i < ctx->packets) {
- /* add a transfer delimiter */
- urb->iso_frame_desc[i].offset =
- frames * stride;
- urb->iso_frame_desc[i].length = 0;
- urb->number_of_packets++;
- }
+ for (i = 0; i < ctx->packets; i++) {
+ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
+ if (counts < 0)
break;
+ /* set up descriptor */
+ urb->iso_frame_desc[i].offset = frames * stride;
+ urb->iso_frame_desc[i].length = counts * stride;
+ frames += counts;
+ avail -= counts;
+ urb->number_of_packets++;
+ transfer_done += counts;
+ if (transfer_done >= runtime->period_size) {
+ transfer_done -= runtime->period_size;
+ frame_limit = 0;
+ period_elapsed = 1;
+ if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+ if (transfer_done > 0) {
+ /* FIXME: fill-max mode is not
+ * supported yet */
+ frames -= transfer_done;
+ counts -= transfer_done;
+ urb->iso_frame_desc[i].length =
+ counts * stride;
+ transfer_done = 0;
+ }
+ i++;
+ if (i < ctx->packets) {
+ /* add a transfer delimiter */
+ urb->iso_frame_desc[i].offset =
+ frames * stride;
+ urb->iso_frame_desc[i].length = 0;
+ urb->number_of_packets++;
+ }
+ break;
+ }
}
+ /* finish at the period boundary or after enough frames */
+ if ((period_elapsed || transfer_done >= frame_limit) &&
+ !snd_usb_endpoint_implicit_feedback_sink(ep))
+ break;
}
- /* finish at the period boundary or after enough frames */
- if ((period_elapsed || transfer_done >= frame_limit) &&
- !snd_usb_endpoint_implicit_feedback_sink(ep))
- break;
- }
- if (!frames) {
- err = -EAGAIN;
- goto unlock;
- }
-
- bytes = frames * stride;
- subs->transfer_done = transfer_done;
- subs->frame_limit = frame_limit;
- if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
- subs->cur_audiofmt->dsd_dop)) {
- fill_playback_urb_dsd_dop(subs, urb, bytes);
- } else if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U8 &&
- subs->cur_audiofmt->dsd_bitrev)) {
- fill_playback_urb_dsd_bitrev(subs, urb, bytes);
- } else {
- /* usual PCM */
- if (!subs->tx_length_quirk)
- copy_to_urb(subs, urb, 0, stride, bytes);
- else
- bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+ if (!frames)
+ return -EAGAIN;
+
+ bytes = frames * stride;
+ subs->transfer_done = transfer_done;
+ subs->frame_limit = frame_limit;
+ if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
+ subs->cur_audiofmt->dsd_dop)) {
+ fill_playback_urb_dsd_dop(subs, urb, bytes);
+ } else if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U8 &&
+ subs->cur_audiofmt->dsd_bitrev)) {
+ fill_playback_urb_dsd_bitrev(subs, urb, bytes);
+ } else {
+ /* usual PCM */
+ if (!subs->tx_length_quirk)
+ copy_to_urb(subs, urb, 0, stride, bytes);
+ else
+ bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
/* bytes is now amount of outgoing data */
- }
+ }
- subs->last_frame_number = usb_get_current_frame_number(subs->dev);
+ subs->last_frame_number = usb_get_current_frame_number(subs->dev);
- if (subs->trigger_tstamp_pending_update) {
- /* this is the first actual URB submitted,
- * update trigger timestamp to reflect actual start time
- */
- snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
- subs->trigger_tstamp_pending_update = false;
- }
+ if (subs->trigger_tstamp_pending_update) {
+ /* this is the first actual URB submitted,
+ * update trigger timestamp to reflect actual start time
+ */
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ subs->trigger_tstamp_pending_update = false;
+ }
- if (period_elapsed && !subs->running && subs->lowlatency_playback) {
- subs->period_elapsed_pending = 1;
- period_elapsed = 0;
+ if (period_elapsed && !subs->running && subs->lowlatency_playback) {
+ subs->period_elapsed_pending = 1;
+ period_elapsed = 0;
+ }
}
- unlock:
- spin_unlock_irqrestore(&subs->lock, flags);
- if (err < 0)
- return err;
urb->transfer_buffer_length = bytes;
if (period_elapsed) {
if (in_stream_lock)
@@ -1659,24 +1647,23 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
static void retire_playback_urb(struct snd_usb_substream *subs,
struct urb *urb)
{
- unsigned long flags;
struct snd_urb_ctx *ctx = urb->context;
bool period_elapsed = false;
- spin_lock_irqsave(&subs->lock, flags);
- if (ctx->queued) {
- if (subs->inflight_bytes >= ctx->queued)
- subs->inflight_bytes -= ctx->queued;
- else
- subs->inflight_bytes = 0;
- }
+ scoped_guard(spinlock_irqsave, &subs->lock) {
+ if (ctx->queued) {
+ if (subs->inflight_bytes >= ctx->queued)
+ subs->inflight_bytes -= ctx->queued;
+ else
+ subs->inflight_bytes = 0;
+ }
- subs->last_frame_number = usb_get_current_frame_number(subs->dev);
- if (subs->running) {
- period_elapsed = subs->period_elapsed_pending;
- subs->period_elapsed_pending = 0;
+ subs->last_frame_number = usb_get_current_frame_number(subs->dev);
+ if (subs->running) {
+ period_elapsed = subs->period_elapsed_pending;
+ subs->period_elapsed_pending = 0;
+ }
}
- spin_unlock_irqrestore(&subs->lock, flags);
if (period_elapsed)
snd_pcm_period_elapsed(subs->pcm_substream);
}
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index c8b967bd7065..f4b7a7ff3203 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -193,7 +193,7 @@ static void proc_dump_substream_status(struct snd_usb_audio *chip,
struct snd_usb_substream *subs,
struct snd_info_buffer *buffer)
{
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (subs->running) {
snd_iprintf(buffer, " Status: Running\n");
if (subs->cur_audiofmt) {
@@ -204,7 +204,6 @@ static void proc_dump_substream_status(struct snd_usb_audio *chip,
} else {
snd_iprintf(buffer, " Status: Stop\n");
}
- mutex_unlock(&chip->mutex);
}
static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
index 9ad76fff741b..cfb30a195364 100644
--- a/sound/usb/qcom/qc_audio_offload.c
+++ b/sound/usb/qcom/qc_audio_offload.c
@@ -755,7 +755,7 @@ static void qmi_stop_session(void)
int if_idx;
int idx;
- mutex_lock(&qdev_mutex);
+ guard(mutex)(&qdev_mutex);
/* find all active intf for set alt 0 and cleanup usb audio dev */
for (idx = 0; idx < SNDRV_CARDS; idx++) {
if (!atomic_read(&uadev[idx].in_use))
@@ -791,11 +791,9 @@ static void qmi_stop_session(void)
disable_audio_stream(subs);
}
atomic_set(&uadev[idx].in_use, 0);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
uaudio_dev_cleanup(&uadev[idx]);
- mutex_unlock(&chip->mutex);
}
- mutex_unlock(&qdev_mutex);
}
/**
@@ -821,8 +819,8 @@ static int uaudio_sideband_notifier(struct usb_interface *intf,
chip = usb_get_intfdata(intf);
- mutex_lock(&qdev_mutex);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
dev = &uadev[chip->card->number];
@@ -836,9 +834,6 @@ static int uaudio_sideband_notifier(struct usb_interface *intf,
}
}
- mutex_unlock(&chip->mutex);
- mutex_unlock(&qdev_mutex);
-
return 0;
}
@@ -972,21 +967,21 @@ static int enable_audio_stream(struct snd_usb_substream *subs,
goto put_suspend;
if (!atomic_read(&chip->shutdown)) {
- ret = snd_usb_lock_shutdown(chip);
- if (ret < 0)
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0) {
+ ret = pm.err;
goto detach_ep;
+ }
if (subs->sync_endpoint) {
ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);
if (ret < 0)
- goto unlock;
+ goto detach_ep;
}
ret = snd_usb_endpoint_prepare(chip, subs->data_endpoint);
if (ret < 0)
- goto unlock;
-
- snd_usb_unlock_shutdown(chip);
+ goto detach_ep;
dev_dbg(uaudio_qdev->data->dev,
"selected %s iface:%d altsetting:%d datainterval:%dus\n",
@@ -1000,9 +995,6 @@ static int enable_audio_stream(struct snd_usb_substream *subs,
return 0;
-unlock:
- snd_usb_unlock_shutdown(chip);
-
detach_ep:
snd_usb_hw_free(subs);
@@ -1584,17 +1576,15 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
goto response;
}
- mutex_lock(&chip->mutex);
- if (req_msg->enable) {
- if (info_idx < 0 || chip->system_suspend || subs->opened) {
- ret = -EBUSY;
- mutex_unlock(&chip->mutex);
-
- goto response;
+ scoped_guard(mutex, &chip->mutex) {
+ if (req_msg->enable) {
+ if (info_idx < 0 || chip->system_suspend || subs->opened) {
+ ret = -EBUSY;
+ goto response;
+ }
+ subs->opened = 1;
}
- subs->opened = 1;
}
- mutex_unlock(&chip->mutex);
if (req_msg->service_interval_valid) {
ret = get_data_interval_from_si(subs,
@@ -1617,9 +1607,8 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
ret = prepare_qmi_response(subs, req_msg, &resp,
info_idx);
if (ret < 0) {
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
subs->opened = 0;
- mutex_unlock(&chip->mutex);
}
} else {
info = &uadev[pcm_card_num].info[info_idx];
@@ -1650,14 +1639,13 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
}
disable_audio_stream(subs);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
subs->opened = 0;
- mutex_unlock(&chip->mutex);
}
response:
if (!req_msg->enable && ret != -EINVAL && ret != -ENODEV) {
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
if (info_idx >= 0) {
info = &uadev[pcm_card_num].info[info_idx];
uaudio_dev_intf_cleanup(uadev[pcm_card_num].udev,
@@ -1666,7 +1654,6 @@ response:
if (atomic_read(&uadev[pcm_card_num].in_use))
kref_put(&uadev[pcm_card_num].kref,
uaudio_dev_release);
- mutex_unlock(&chip->mutex);
}
mutex_unlock(&qdev_mutex);
@@ -1769,12 +1756,12 @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
!usb_qmi_get_pcm_num(chip, 0))
return;
- mutex_lock(&qdev_mutex);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
if (!uadev[chip->card->number].chip) {
sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
if (!sdev)
- goto exit;
+ return;
sb = xhci_sideband_register(intf, XHCI_SIDEBAND_VENDOR,
uaudio_sideband_notifier);
@@ -1813,9 +1800,6 @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
snd_soc_usb_connect(uaudio_qdev->auxdev->dev.parent, sdev);
}
- mutex_unlock(&chip->mutex);
- mutex_unlock(&qdev_mutex);
-
return;
unreg_xhci:
@@ -1825,9 +1809,6 @@ free_sdev:
kfree(sdev);
uadev[chip->card->number].sdev = NULL;
uadev[chip->card->number].chip = NULL;
-exit:
- mutex_unlock(&chip->mutex);
- mutex_unlock(&qdev_mutex);
}
/**
@@ -1863,16 +1844,13 @@ static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
if (card_num >= SNDRV_CARDS)
return;
- mutex_lock(&qdev_mutex);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
dev = &uadev[card_num];
/* Device has already been cleaned up, or never populated */
- if (!dev->chip) {
- mutex_unlock(&chip->mutex);
- mutex_unlock(&qdev_mutex);
+ if (!dev->chip)
return;
- }
/* cleaned up already */
if (!dev->udev)
@@ -1893,9 +1871,6 @@ done:
kfree(dev->sdev);
dev->sdev = NULL;
}
- mutex_unlock(&chip->mutex);
-
- mutex_unlock(&qdev_mutex);
}
/**
@@ -1920,13 +1895,10 @@ static void qc_usb_audio_offload_suspend(struct usb_interface *intf,
if (card_num >= SNDRV_CARDS)
return;
- mutex_lock(&qdev_mutex);
- mutex_lock(&chip->mutex);
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
uaudio_send_disconnect_ind(chip);
-
- mutex_unlock(&chip->mutex);
- mutex_unlock(&qdev_mutex);
}
static struct snd_usb_platform_ops offload_ops = {
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 766db7d00cbc..634cb4fb586f 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1599,9 +1599,6 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
/* presonus studio 1810c: skip altsets incompatible with device_setup */
if (chip->usb_id == USB_ID(0x194f, 0x010c))
return s1810c_skip_setting_quirk(chip, iface, altno);
- /* presonus studio 1824c: skip altsets incompatible with device_setup */
- if (chip->usb_id == USB_ID(0x194f, 0x010d))
- return s1810c_skip_setting_quirk(chip, iface, altno);
return 0;
}
@@ -2200,9 +2197,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x0556, 0x0014, /* Phoenix Audio TMX320VC */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x0572, 0x1b08, /* Conexant Systems (Rockwell), Inc. */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0572, 0x1b09, /* Conexant Systems (Rockwell), Inc. */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x05a3, 0x9420, /* ELP HD USB Camera */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x05a7, 0x1020, /* Bose Companion 5 */
@@ -2243,18 +2240,22 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
+ DEVICE_FLG(0x0b05, 0x18a6, /* ASUSTek Computer, Inc. */
+ QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE),
DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x0bda, 0x498a, /* Realtek Semiconductor Corp. */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE),
DEVICE_FLG(0x0c45, 0x6340, /* Sonix HD USB Camera */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x0c45, 0x636b, /* Microdia JP001 USB Camera */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x0d8c, 0x000c, /* C-Media */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x0d8c, 0x0012, /* C-Media */
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */
- QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
@@ -2264,7 +2265,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x12d1, 0x3a07, /* Huawei Technologies Co., Ltd. */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE),
DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */
@@ -2304,7 +2305,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x1901, 0x0191, /* GE B850V3 CP2114 audio interface */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x19f7, 0x0003, /* RODE NT-USB */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
@@ -2356,7 +2357,7 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE | QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE),
DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */
@@ -2368,13 +2369,13 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x2d95, 0x8021, /* VIVO USB-C-XE710 HEADSET */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x2d99, 0x0026, /* HECATE G2 GAMING HEADSET */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x2fc6, 0xf0b7, /* iBasso DC07 Pro */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x339b, 0x3a07, /* Synaptics HONOR USB-C HEADSET */
- QUIRK_FLAG_MIXER_MIN_MUTE),
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x534d, 0x0021, /* MacroSilicon MS2100/MS2106 */
@@ -2442,7 +2443,85 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
{} /* terminator */
};
-void snd_usb_init_quirk_flags(struct snd_usb_audio *chip)
+#define QUIRK_STRING_ENTRY(x) \
+ [QUIRK_TYPE_ ## x] = __stringify(x)
+
+static const char *const snd_usb_audio_quirk_flag_names[] = {
+ QUIRK_STRING_ENTRY(GET_SAMPLE_RATE),
+ QUIRK_STRING_ENTRY(SHARE_MEDIA_DEVICE),
+ QUIRK_STRING_ENTRY(ALIGN_TRANSFER),
+ QUIRK_STRING_ENTRY(TX_LENGTH),
+ QUIRK_STRING_ENTRY(PLAYBACK_FIRST),
+ QUIRK_STRING_ENTRY(SKIP_CLOCK_SELECTOR),
+ QUIRK_STRING_ENTRY(IGNORE_CLOCK_SOURCE),
+ QUIRK_STRING_ENTRY(ITF_USB_DSD_DAC),
+ QUIRK_STRING_ENTRY(CTL_MSG_DELAY),
+ QUIRK_STRING_ENTRY(CTL_MSG_DELAY_1M),
+ QUIRK_STRING_ENTRY(CTL_MSG_DELAY_5M),
+ QUIRK_STRING_ENTRY(IFACE_DELAY),
+ QUIRK_STRING_ENTRY(VALIDATE_RATES),
+ QUIRK_STRING_ENTRY(DISABLE_AUTOSUSPEND),
+ QUIRK_STRING_ENTRY(IGNORE_CTL_ERROR),
+ QUIRK_STRING_ENTRY(DSD_RAW),
+ QUIRK_STRING_ENTRY(SET_IFACE_FIRST),
+ QUIRK_STRING_ENTRY(GENERIC_IMPLICIT_FB),
+ QUIRK_STRING_ENTRY(SKIP_IMPLICIT_FB),
+ QUIRK_STRING_ENTRY(IFACE_SKIP_CLOSE),
+ QUIRK_STRING_ENTRY(FORCE_IFACE_RESET),
+ QUIRK_STRING_ENTRY(FIXED_RATE),
+ QUIRK_STRING_ENTRY(MIC_RES_16),
+ QUIRK_STRING_ENTRY(MIC_RES_384),
+ QUIRK_STRING_ENTRY(MIXER_PLAYBACK_MIN_MUTE),
+ QUIRK_STRING_ENTRY(MIXER_CAPTURE_MIN_MUTE),
+ NULL
+};
+
+const char *snd_usb_quirk_flag_find_name(unsigned long index)
+{
+ if (index >= ARRAY_SIZE(snd_usb_audio_quirk_flag_names))
+ return NULL;
+
+ return snd_usb_audio_quirk_flag_names[index];
+}
+
+u32 snd_usb_quirk_flags_from_name(const char *name)
+{
+ int i;
+
+ if (!name || !*name)
+ return 0;
+
+ for (i = 0; snd_usb_audio_quirk_flag_names[i]; i++) {
+ if (strcasecmp(name, snd_usb_audio_quirk_flag_names[i]) == 0)
+ return BIT_U32(i);
+ }
+
+ return 0;
+}
+
+void snd_usb_apply_flag_dbg(const char *reason,
+ struct snd_usb_audio *chip,
+ unsigned long flag)
+{
+ unsigned long bit;
+
+ for_each_set_bit(bit, &flag, BYTES_TO_BITS(sizeof(flag))) {
+ const char *name = snd_usb_audio_quirk_flag_names[bit];
+
+ if (name)
+ usb_audio_dbg(chip,
+ "From %s apply quirk flag %s for device %04x:%04x\n",
+ reason, name, USB_ID_VENDOR(chip->usb_id),
+ USB_ID_PRODUCT(chip->usb_id));
+ else
+ usb_audio_warn(chip,
+ "From %s apply unknown quirk flag 0x%lx for device %04x:%04x\n",
+ reason, bit, USB_ID_VENDOR(chip->usb_id),
+ USB_ID_PRODUCT(chip->usb_id));
+ }
+}
+
+void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
{
const struct usb_audio_quirk_flags_table *p;
@@ -2450,12 +2529,97 @@ void snd_usb_init_quirk_flags(struct snd_usb_audio *chip)
if (chip->usb_id == p->id ||
(!USB_ID_PRODUCT(p->id) &&
USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
- usb_audio_dbg(chip,
- "Set quirk_flags 0x%x for device %04x:%04x\n",
- p->flags, USB_ID_VENDOR(chip->usb_id),
- USB_ID_PRODUCT(chip->usb_id));
+ snd_usb_apply_flag_dbg("builtin table", chip, p->flags);
chip->quirk_flags |= p->flags;
return;
}
}
}
+
+void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip,
+ const char *str)
+{
+ u16 chip_vid = USB_ID_VENDOR(chip->usb_id);
+ u16 chip_pid = USB_ID_PRODUCT(chip->usb_id);
+ u32 mask_flags, unmask_flags, bit;
+ char *p, *field, *flag;
+ bool is_unmask;
+ u16 vid, pid;
+
+ char *val __free(kfree) = kstrdup(str, GFP_KERNEL);
+
+ if (!val)
+ return;
+
+ for (p = val; p && *p;) {
+ /* Each entry consists of VID:PID:flags */
+ field = strsep(&p, ":");
+ if (!field)
+ break;
+
+ if (strcmp(field, "*") == 0)
+ vid = 0;
+ else if (kstrtou16(field, 16, &vid))
+ break;
+
+ field = strsep(&p, ":");
+ if (!field)
+ break;
+
+ if (strcmp(field, "*") == 0)
+ pid = 0;
+ else if (kstrtou16(field, 16, &pid))
+ break;
+
+ field = strsep(&p, ";");
+ if (!field || !*field)
+ break;
+
+ if ((vid != 0 && vid != chip_vid) ||
+ (pid != 0 && pid != chip_pid))
+ continue;
+
+ /* Collect the flags */
+ mask_flags = 0;
+ unmask_flags = 0;
+ while (field && *field) {
+ flag = strsep(&field, "|");
+
+ if (!flag)
+ break;
+
+ if (*flag == '!') {
+ is_unmask = true;
+ flag++;
+ } else {
+ is_unmask = false;
+ }
+
+ if (!kstrtou32(flag, 16, &bit)) {
+ if (is_unmask)
+ unmask_flags |= bit;
+ else
+ mask_flags |= bit;
+
+ break;
+ }
+
+ bit = snd_usb_quirk_flags_from_name(flag);
+
+ if (bit) {
+ if (is_unmask)
+ unmask_flags |= bit;
+ else
+ mask_flags |= bit;
+ } else {
+ pr_warn("snd_usb_audio: unknown flag %s while parsing param quirk_flags\n",
+ flag);
+ }
+ }
+
+ chip->quirk_flags &= ~unmask_flags;
+ chip->quirk_flags |= mask_flags;
+ snd_usb_apply_flag_dbg("module param", chip,
+ chip->quirk_flags);
+ }
+}
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index f9bfd5ac7bab..f24d6a5a197a 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -48,6 +48,15 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
struct audioformat *fp,
int stream);
-void snd_usb_init_quirk_flags(struct snd_usb_audio *chip);
+void snd_usb_apply_flag_dbg(const char *reason,
+ struct snd_usb_audio *chip,
+ unsigned long flag);
+
+void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip);
+void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip,
+ const char *str);
+
+const char *snd_usb_quirk_flag_find_name(unsigned long flag);
+u32 snd_usb_quirk_flags_from_name(const char *name);
#endif /* __USBAUDIO_QUIRKS_H */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 1ef4d39978df..79978cae9799 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -139,6 +139,29 @@ struct snd_usb_audio_quirk {
int snd_usb_lock_shutdown(struct snd_usb_audio *chip);
void snd_usb_unlock_shutdown(struct snd_usb_audio *chip);
+/* auto-cleanup */
+struct __snd_usb_lock {
+ struct snd_usb_audio *chip;
+ int err;
+};
+
+static inline struct __snd_usb_lock __snd_usb_lock_shutdown(struct snd_usb_audio *chip)
+{
+ struct __snd_usb_lock T = { .chip = chip };
+ T.err = snd_usb_lock_shutdown(chip);
+ return T;
+}
+
+static inline void __snd_usb_unlock_shutdown(struct __snd_usb_lock *lock)
+{
+ if (!lock->err)
+ snd_usb_unlock_shutdown(lock->chip);
+}
+
+DEFINE_CLASS(snd_usb_lock, struct __snd_usb_lock,
+ __snd_usb_unlock_shutdown(&(_T)), __snd_usb_lock_shutdown(chip),
+ struct snd_usb_audio *chip)
+
extern bool snd_usb_use_vmalloc;
extern bool snd_usb_skip_validation;
@@ -196,35 +219,70 @@ extern bool snd_usb_skip_validation;
* for the given endpoint.
* QUIRK_FLAG_MIC_RES_16 and QUIRK_FLAG_MIC_RES_384
* Set the fixed resolution for Mic Capture Volume (mostly for webcams)
- * QUIRK_FLAG_MIXER_MIN_MUTE
+ * QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE
* Set minimum volume control value as mute for devices where the lowest
* playback value represents muted state instead of minimum audible volume
+ * QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE
+ * Similar to QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE, but for capture streams
*/
-#define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0)
-#define QUIRK_FLAG_SHARE_MEDIA_DEVICE (1U << 1)
-#define QUIRK_FLAG_ALIGN_TRANSFER (1U << 2)
-#define QUIRK_FLAG_TX_LENGTH (1U << 3)
-#define QUIRK_FLAG_PLAYBACK_FIRST (1U << 4)
-#define QUIRK_FLAG_SKIP_CLOCK_SELECTOR (1U << 5)
-#define QUIRK_FLAG_IGNORE_CLOCK_SOURCE (1U << 6)
-#define QUIRK_FLAG_ITF_USB_DSD_DAC (1U << 7)
-#define QUIRK_FLAG_CTL_MSG_DELAY (1U << 8)
-#define QUIRK_FLAG_CTL_MSG_DELAY_1M (1U << 9)
-#define QUIRK_FLAG_CTL_MSG_DELAY_5M (1U << 10)
-#define QUIRK_FLAG_IFACE_DELAY (1U << 11)
-#define QUIRK_FLAG_VALIDATE_RATES (1U << 12)
-#define QUIRK_FLAG_DISABLE_AUTOSUSPEND (1U << 13)
-#define QUIRK_FLAG_IGNORE_CTL_ERROR (1U << 14)
-#define QUIRK_FLAG_DSD_RAW (1U << 15)
-#define QUIRK_FLAG_SET_IFACE_FIRST (1U << 16)
-#define QUIRK_FLAG_GENERIC_IMPLICIT_FB (1U << 17)
-#define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18)
-#define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19)
-#define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20)
-#define QUIRK_FLAG_FIXED_RATE (1U << 21)
-#define QUIRK_FLAG_MIC_RES_16 (1U << 22)
-#define QUIRK_FLAG_MIC_RES_384 (1U << 23)
-#define QUIRK_FLAG_MIXER_MIN_MUTE (1U << 24)
+enum {
+ QUIRK_TYPE_GET_SAMPLE_RATE = 0,
+ QUIRK_TYPE_SHARE_MEDIA_DEVICE = 1,
+ QUIRK_TYPE_ALIGN_TRANSFER = 2,
+ QUIRK_TYPE_TX_LENGTH = 3,
+ QUIRK_TYPE_PLAYBACK_FIRST = 4,
+ QUIRK_TYPE_SKIP_CLOCK_SELECTOR = 5,
+ QUIRK_TYPE_IGNORE_CLOCK_SOURCE = 6,
+ QUIRK_TYPE_ITF_USB_DSD_DAC = 7,
+ QUIRK_TYPE_CTL_MSG_DELAY = 8,
+ QUIRK_TYPE_CTL_MSG_DELAY_1M = 9,
+ QUIRK_TYPE_CTL_MSG_DELAY_5M = 10,
+ QUIRK_TYPE_IFACE_DELAY = 11,
+ QUIRK_TYPE_VALIDATE_RATES = 12,
+ QUIRK_TYPE_DISABLE_AUTOSUSPEND = 13,
+ QUIRK_TYPE_IGNORE_CTL_ERROR = 14,
+ QUIRK_TYPE_DSD_RAW = 15,
+ QUIRK_TYPE_SET_IFACE_FIRST = 16,
+ QUIRK_TYPE_GENERIC_IMPLICIT_FB = 17,
+ QUIRK_TYPE_SKIP_IMPLICIT_FB = 18,
+ QUIRK_TYPE_IFACE_SKIP_CLOSE = 19,
+ QUIRK_TYPE_FORCE_IFACE_RESET = 20,
+ QUIRK_TYPE_FIXED_RATE = 21,
+ QUIRK_TYPE_MIC_RES_16 = 22,
+ QUIRK_TYPE_MIC_RES_384 = 23,
+ QUIRK_TYPE_MIXER_PLAYBACK_MIN_MUTE = 24,
+ QUIRK_TYPE_MIXER_CAPTURE_MIN_MUTE = 25,
+/* Please also edit snd_usb_audio_quirk_flag_names */
+};
+
+#define QUIRK_FLAG(x) BIT_U32(QUIRK_TYPE_ ## x)
+
+#define QUIRK_FLAG_GET_SAMPLE_RATE QUIRK_FLAG(GET_SAMPLE_RATE)
+#define QUIRK_FLAG_SHARE_MEDIA_DEVICE QUIRK_FLAG(SHARE_MEDIA_DEVICE)
+#define QUIRK_FLAG_ALIGN_TRANSFER QUIRK_FLAG(ALIGN_TRANSFER)
+#define QUIRK_FLAG_TX_LENGTH QUIRK_FLAG(TX_LENGTH)
+#define QUIRK_FLAG_PLAYBACK_FIRST QUIRK_FLAG(PLAYBACK_FIRST)
+#define QUIRK_FLAG_SKIP_CLOCK_SELECTOR QUIRK_FLAG(SKIP_CLOCK_SELECTOR)
+#define QUIRK_FLAG_IGNORE_CLOCK_SOURCE QUIRK_FLAG(IGNORE_CLOCK_SOURCE)
+#define QUIRK_FLAG_ITF_USB_DSD_DAC QUIRK_FLAG(ITF_USB_DSD_DAC)
+#define QUIRK_FLAG_CTL_MSG_DELAY QUIRK_FLAG(CTL_MSG_DELAY)
+#define QUIRK_FLAG_CTL_MSG_DELAY_1M QUIRK_FLAG(CTL_MSG_DELAY_1M)
+#define QUIRK_FLAG_CTL_MSG_DELAY_5M QUIRK_FLAG(CTL_MSG_DELAY_5M)
+#define QUIRK_FLAG_IFACE_DELAY QUIRK_FLAG(IFACE_DELAY)
+#define QUIRK_FLAG_VALIDATE_RATES QUIRK_FLAG(VALIDATE_RATES)
+#define QUIRK_FLAG_DISABLE_AUTOSUSPEND QUIRK_FLAG(DISABLE_AUTOSUSPEND)
+#define QUIRK_FLAG_IGNORE_CTL_ERROR QUIRK_FLAG(IGNORE_CTL_ERROR)
+#define QUIRK_FLAG_DSD_RAW QUIRK_FLAG(DSD_RAW)
+#define QUIRK_FLAG_SET_IFACE_FIRST QUIRK_FLAG(SET_IFACE_FIRST)
+#define QUIRK_FLAG_GENERIC_IMPLICIT_FB QUIRK_FLAG(GENERIC_IMPLICIT_FB)
+#define QUIRK_FLAG_SKIP_IMPLICIT_FB QUIRK_FLAG(SKIP_IMPLICIT_FB)
+#define QUIRK_FLAG_IFACE_SKIP_CLOSE QUIRK_FLAG(IFACE_SKIP_CLOSE)
+#define QUIRK_FLAG_FORCE_IFACE_RESET QUIRK_FLAG(FORCE_IFACE_RESET)
+#define QUIRK_FLAG_FIXED_RATE QUIRK_FLAG(FIXED_RATE)
+#define QUIRK_FLAG_MIC_RES_16 QUIRK_FLAG(MIC_RES_16)
+#define QUIRK_FLAG_MIC_RES_384 QUIRK_FLAG(MIC_RES_384)
+#define QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE QUIRK_FLAG(MIXER_PLAYBACK_MIN_MUTE)
+#define QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE QUIRK_FLAG(MIXER_CAPTURE_MIN_MUTE)
#endif /* __USBAUDIO_H */
diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile
index fc033aba03a4..9db87ae39ee9 100644
--- a/sound/usb/usx2y/Makefile
+++ b/sound/usb/usx2y/Makefile
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
snd-usb-usx2y-y := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
snd-usb-us122l-y := us122l.o
+snd-usb-us144mkii-y := us144mkii.o us144mkii_pcm.o us144mkii_playback.o us144mkii_capture.o us144mkii_midi.o us144mkii_controls.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
obj-$(CONFIG_SND_USB_US122L) += snd-usb-us122l.o
+obj-$(CONFIG_SND_USB_US144MKII) += snd-usb-us144mkii.o \ No newline at end of file
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index 2ace3ba46091..011ea96e9779 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -97,10 +97,10 @@ static vm_fault_t usb_stream_hwdep_vm_fault(struct vm_fault *vmf)
struct us122l *us122l = vmf->vma->vm_private_data;
struct usb_stream *s;
- mutex_lock(&us122l->mutex);
+ guard(mutex)(&us122l->mutex);
s = us122l->sk.s;
if (!s)
- goto unlock;
+ return VM_FAULT_SIGBUS;
offset = vmf->pgoff << PAGE_SHIFT;
if (offset < PAGE_ALIGN(s->read_size)) {
@@ -108,21 +108,17 @@ static vm_fault_t usb_stream_hwdep_vm_fault(struct vm_fault *vmf)
} else {
offset -= PAGE_ALIGN(s->read_size);
if (offset >= PAGE_ALIGN(s->write_size))
- goto unlock;
+ return VM_FAULT_SIGBUS;
vaddr = us122l->sk.write_page + offset;
}
page = virt_to_page(vaddr);
get_page(page);
- mutex_unlock(&us122l->mutex);
vmf->page = page;
return 0;
-unlock:
- mutex_unlock(&us122l->mutex);
- return VM_FAULT_SIGBUS;
}
@@ -163,12 +159,11 @@ static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file)
usb_autopm_put_interface(iface);
if (us122l->first == file)
us122l->first = NULL;
- mutex_lock(&us122l->mutex);
+ guard(mutex)(&us122l->mutex);
if (us122l->master == file)
us122l->master = us122l->slave;
us122l->slave = NULL;
- mutex_unlock(&us122l->mutex);
return 0;
}
@@ -179,23 +174,19 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
struct us122l *us122l = hw->private_data;
unsigned long offset;
struct usb_stream *s;
- int err = 0;
bool read;
offset = area->vm_pgoff << PAGE_SHIFT;
- mutex_lock(&us122l->mutex);
+ guard(mutex)(&us122l->mutex);
s = us122l->sk.s;
read = offset < s->read_size;
- if (read && area->vm_flags & VM_WRITE) {
- err = -EPERM;
- goto out;
- }
+ if (read && area->vm_flags & VM_WRITE)
+ return -EPERM;
/* if userspace tries to mmap beyond end of our buffer, fail */
if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) {
dev_warn(hw->card->dev, "%s: size %lu > %u\n", __func__,
size, read ? s->read_size : s->write_size);
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
area->vm_ops = &usb_stream_hwdep_vm_ops;
@@ -203,9 +194,7 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
if (!read)
vm_flags_set(area, VM_DONTEXPAND);
area->vm_private_data = us122l;
-out:
- mutex_unlock(&us122l->mutex);
- return err;
+ return 0;
}
static __poll_t usb_stream_hwdep_poll(struct snd_hwdep *hw,
@@ -361,7 +350,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
snd_power_wait(hw->card);
- mutex_lock(&us122l->mutex);
+ guard(mutex)(&us122l->mutex);
s = us122l->sk.s;
if (!us122l->master) {
us122l->master = file;
@@ -381,7 +370,6 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
err = 1;
}
unlock:
- mutex_unlock(&us122l->mutex);
wake_up_all(&us122l->sk.sleep);
return err;
}
@@ -577,9 +565,9 @@ static void snd_us122l_disconnect(struct usb_interface *intf)
snd_card_disconnect(card);
us122l = US122L(card);
- mutex_lock(&us122l->mutex);
- us122l_stop(us122l);
- mutex_unlock(&us122l->mutex);
+ scoped_guard(mutex, &us122l->mutex) {
+ us122l_stop(us122l);
+ }
/* release the midi resources */
list_for_each(p, &us122l->midi_list) {
@@ -611,9 +599,8 @@ static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message)
list_for_each(p, &us122l->midi_list)
snd_usbmidi_input_stop(p);
- mutex_lock(&us122l->mutex);
+ guard(mutex)(&us122l->mutex);
usb_stream_stop(&us122l->sk);
- mutex_unlock(&us122l->mutex);
return 0;
}
@@ -633,7 +620,7 @@ static int snd_us122l_resume(struct usb_interface *intf)
if (!us122l)
return 0;
- mutex_lock(&us122l->mutex);
+ guard(mutex)(&us122l->mutex);
/* needed, doesn't restart without: */
if (us122l->is_us144) {
err = usb_set_interface(us122l->dev, 0, 1);
@@ -664,7 +651,6 @@ static int snd_us122l_resume(struct usb_interface *intf)
list_for_each(p, &us122l->midi_list)
snd_usbmidi_input_start(p);
unlock:
- mutex_unlock(&us122l->mutex);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return err;
}
@@ -686,12 +672,6 @@ static const struct usb_device_id snd_us122l_usb_id_table[] = {
.idVendor = 0x0644,
.idProduct = USB_ID_US122MKII
},
- {
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = 0x0644,
- .idProduct = USB_ID_US144MKII,
- .driver_info = US122L_FLAG_US144
- },
{ /* terminator */ }
};
MODULE_DEVICE_TABLE(usb, snd_us122l_usb_id_table);
diff --git a/sound/usb/usx2y/us144mkii.c b/sound/usb/usx2y/us144mkii.c
new file mode 100644
index 000000000000..f6572a576c15
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+/*
+ * ALSA Driver for TASCAM US-144MKII Audio Interface
+ */
+
+#include "us144mkii.h"
+
+MODULE_AUTHOR("Šerif Rami <ramiserifpersia@gmail.com>");
+MODULE_DESCRIPTION("ALSA Driver for TASCAM US-144MKII");
+MODULE_LICENSE("GPL");
+
+/**
+ * @brief Module parameters for ALSA card instantiation.
+ *
+ * These parameters allow users to configure how the ALSA sound card
+ * for the TASCAM US-144MKII is instantiated.
+ *
+ * @param index: Array of integers specifying the ALSA card index for each
+ * device. Defaults to -1 (automatic).
+ * @param id: Array of strings specifying the ALSA card ID for each device.
+ * Defaults to "US144MKII".
+ * @param enable: Array of booleans to enable or disable each device.
+ * Defaults to {1, 0, ..., 0} (first device enabled).
+ * @param dev_idx: Internal counter for the number of TASCAM devices probed.
+ */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = { 1, [1 ...(SNDRV_CARDS - 1)] = 0 };
+static int dev_idx;
+
+static int tascam_probe(struct usb_interface *intf,
+ const struct usb_device_id *usb_id);
+static void tascam_disconnect(struct usb_interface *intf);
+static int tascam_suspend(struct usb_interface *intf, pm_message_t message);
+static int tascam_resume(struct usb_interface *intf);
+
+void tascam_free_urbs(struct tascam_card *tascam)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&tascam->playback_anchor);
+ for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
+ if (tascam->playback_urbs[i]) {
+ usb_free_coherent(
+ tascam->dev, tascam->playback_urb_alloc_size,
+ tascam->playback_urbs[i]->transfer_buffer,
+ tascam->playback_urbs[i]->transfer_dma);
+ usb_free_urb(tascam->playback_urbs[i]);
+ tascam->playback_urbs[i] = NULL;
+ }
+ }
+
+ usb_kill_anchored_urbs(&tascam->feedback_anchor);
+ for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
+ if (tascam->feedback_urbs[i]) {
+ usb_free_coherent(
+ tascam->dev, tascam->feedback_urb_alloc_size,
+ tascam->feedback_urbs[i]->transfer_buffer,
+ tascam->feedback_urbs[i]->transfer_dma);
+ usb_free_urb(tascam->feedback_urbs[i]);
+ tascam->feedback_urbs[i] = NULL;
+ }
+ }
+
+ usb_kill_anchored_urbs(&tascam->capture_anchor);
+ for (i = 0; i < NUM_CAPTURE_URBS; i++) {
+ if (tascam->capture_urbs[i]) {
+ usb_free_coherent(
+ tascam->dev, tascam->capture_urb_alloc_size,
+ tascam->capture_urbs[i]->transfer_buffer,
+ tascam->capture_urbs[i]->transfer_dma);
+ usb_free_urb(tascam->capture_urbs[i]);
+ tascam->capture_urbs[i] = NULL;
+ }
+ }
+
+ usb_kill_anchored_urbs(&tascam->midi_in_anchor);
+ for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
+ if (tascam->midi_in_urbs[i]) {
+ usb_free_coherent(
+ tascam->dev, MIDI_IN_BUF_SIZE,
+ tascam->midi_in_urbs[i]->transfer_buffer,
+ tascam->midi_in_urbs[i]->transfer_dma);
+ usb_free_urb(tascam->midi_in_urbs[i]);
+ tascam->midi_in_urbs[i] = NULL;
+ }
+ }
+
+ usb_kill_anchored_urbs(&tascam->midi_out_anchor);
+ for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
+ if (tascam->midi_out_urbs[i]) {
+ usb_free_coherent(
+ tascam->dev, MIDI_OUT_BUF_SIZE,
+ tascam->midi_out_urbs[i]->transfer_buffer,
+ tascam->midi_out_urbs[i]->transfer_dma);
+ usb_free_urb(tascam->midi_out_urbs[i]);
+ tascam->midi_out_urbs[i] = NULL;
+ }
+ }
+
+ kfree(tascam->capture_routing_buffer);
+ tascam->capture_routing_buffer = NULL;
+ kfree(tascam->capture_decode_dst_block);
+ tascam->capture_decode_dst_block = NULL;
+ kfree(tascam->capture_decode_raw_block);
+ tascam->capture_decode_raw_block = NULL;
+ kfree(tascam->capture_ring_buffer);
+ tascam->capture_ring_buffer = NULL;
+}
+
+int tascam_alloc_urbs(struct tascam_card *tascam)
+{
+ int i;
+ size_t max_packet_size;
+
+ max_packet_size = ((96000 / 8000) + 2) * BYTES_PER_FRAME;
+ tascam->playback_urb_alloc_size =
+ max_packet_size * PLAYBACK_URB_PACKETS;
+
+ for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
+ struct urb *urb =
+ usb_alloc_urb(PLAYBACK_URB_PACKETS, GFP_KERNEL);
+
+ if (!urb)
+ goto error;
+ tascam->playback_urbs[i] = urb;
+
+ urb->transfer_buffer = usb_alloc_coherent(
+ tascam->dev, tascam->playback_urb_alloc_size,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!urb->transfer_buffer)
+ goto error;
+
+ urb->dev = tascam->dev;
+ urb->pipe = usb_sndisocpipe(tascam->dev, EP_AUDIO_OUT);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1;
+ urb->context = tascam;
+ urb->complete = playback_urb_complete;
+ }
+
+ tascam->feedback_urb_alloc_size =
+ FEEDBACK_PACKET_SIZE * FEEDBACK_URB_PACKETS;
+
+ for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
+ struct urb *f_urb =
+ usb_alloc_urb(FEEDBACK_URB_PACKETS, GFP_KERNEL);
+
+ if (!f_urb)
+ goto error;
+ tascam->feedback_urbs[i] = f_urb;
+
+ f_urb->transfer_buffer = usb_alloc_coherent(
+ tascam->dev, tascam->feedback_urb_alloc_size,
+ GFP_KERNEL, &f_urb->transfer_dma);
+ if (!f_urb->transfer_buffer)
+ goto error;
+
+ f_urb->dev = tascam->dev;
+ f_urb->pipe =
+ usb_rcvisocpipe(tascam->dev, EP_PLAYBACK_FEEDBACK);
+ f_urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ f_urb->interval = 4;
+ f_urb->context = tascam;
+ f_urb->complete = feedback_urb_complete;
+ }
+
+ tascam->capture_urb_alloc_size = CAPTURE_URB_SIZE;
+ for (i = 0; i < NUM_CAPTURE_URBS; i++) {
+ struct urb *c_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!c_urb)
+ goto error;
+ tascam->capture_urbs[i] = c_urb;
+
+ c_urb->transfer_buffer = usb_alloc_coherent(
+ tascam->dev, tascam->capture_urb_alloc_size, GFP_KERNEL,
+ &c_urb->transfer_dma);
+ if (!c_urb->transfer_buffer)
+ goto error;
+
+ usb_fill_bulk_urb(c_urb, tascam->dev,
+ usb_rcvbulkpipe(tascam->dev, EP_AUDIO_IN),
+ c_urb->transfer_buffer,
+ tascam->capture_urb_alloc_size,
+ capture_urb_complete, tascam);
+ c_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ /* MIDI URB and buffer allocation */
+ for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
+ struct urb *m_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!m_urb)
+ goto error;
+ tascam->midi_in_urbs[i] = m_urb;
+ m_urb->transfer_buffer =
+ usb_alloc_coherent(tascam->dev, MIDI_IN_BUF_SIZE,
+ GFP_KERNEL, &m_urb->transfer_dma);
+ if (!m_urb->transfer_buffer)
+ goto error;
+ usb_fill_bulk_urb(m_urb, tascam->dev,
+ usb_rcvbulkpipe(tascam->dev, EP_MIDI_IN),
+ m_urb->transfer_buffer, MIDI_IN_BUF_SIZE,
+ tascam_midi_in_urb_complete, tascam);
+ m_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
+ struct urb *m_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!m_urb)
+ goto error;
+ tascam->midi_out_urbs[i] = m_urb;
+ m_urb->transfer_buffer =
+ usb_alloc_coherent(tascam->dev, MIDI_OUT_BUF_SIZE,
+ GFP_KERNEL, &m_urb->transfer_dma);
+ if (!m_urb->transfer_buffer)
+ goto error;
+ usb_fill_bulk_urb(m_urb, tascam->dev,
+ usb_sndbulkpipe(tascam->dev, EP_MIDI_OUT),
+ m_urb->transfer_buffer,
+ 0, /* length set later */
+ tascam_midi_out_urb_complete, tascam);
+ m_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ tascam->capture_ring_buffer =
+ kmalloc(CAPTURE_RING_BUFFER_SIZE, GFP_KERNEL);
+ if (!tascam->capture_ring_buffer)
+ goto error;
+
+ tascam->capture_decode_raw_block =
+ kmalloc(RAW_BYTES_PER_DECODE_BLOCK, GFP_KERNEL);
+ if (!tascam->capture_decode_raw_block)
+ goto error;
+
+ tascam->capture_decode_dst_block =
+ kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
+ DECODED_SAMPLE_SIZE,
+ GFP_KERNEL);
+ if (!tascam->capture_decode_dst_block)
+ goto error;
+
+ tascam->capture_routing_buffer =
+ kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
+ DECODED_SAMPLE_SIZE,
+ GFP_KERNEL);
+ if (!tascam->capture_routing_buffer)
+ goto error;
+
+ return 0;
+
+error:
+ dev_err(tascam->card->dev, "Failed to allocate URBs\n");
+ tascam_free_urbs(tascam);
+ return -ENOMEM;
+}
+
+void tascam_stop_work_handler(struct work_struct *work)
+{
+ struct tascam_card *tascam =
+ container_of(work, struct tascam_card, stop_work);
+
+ usb_kill_anchored_urbs(&tascam->playback_anchor);
+ usb_kill_anchored_urbs(&tascam->feedback_anchor);
+ usb_kill_anchored_urbs(&tascam->capture_anchor);
+ atomic_set(&tascam->active_urbs, 0);
+}
+
+/**
+ * tascam_card_private_free() - Frees private data associated with the sound
+ * card.
+ * @card: Pointer to the ALSA sound card instance.
+ *
+ * This function is called when the sound card is being freed. It releases
+ * resources allocated for the tascam_card structure, including the MIDI
+ * input FIFO and decrements the USB device reference count.
+ */
+static void tascam_card_private_free(struct snd_card *card)
+{
+ struct tascam_card *tascam = card->private_data;
+
+ if (tascam) {
+ kfifo_free(&tascam->midi_in_fifo);
+ if (tascam->dev) {
+ usb_put_dev(tascam->dev);
+ tascam->dev = NULL;
+ }
+ }
+}
+
+/**
+ * tascam_suspend() - Handles device suspension.
+ * @intf: The USB interface being suspended.
+ * @message: Power management message.
+ *
+ * This function is called when the device is suspended. It stops all active
+ * streams, kills all URBs, and sends a vendor-specific deep sleep command
+ * to the device to ensure a stable low-power state.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct tascam_card *tascam = usb_get_intfdata(intf);
+
+ if (!tascam)
+ return 0;
+
+ snd_pcm_suspend_all(tascam->pcm);
+
+ cancel_work_sync(&tascam->stop_work);
+ cancel_work_sync(&tascam->capture_work);
+ cancel_work_sync(&tascam->midi_in_work);
+ cancel_work_sync(&tascam->midi_out_work);
+ cancel_work_sync(&tascam->stop_pcm_work);
+ usb_kill_anchored_urbs(&tascam->playback_anchor);
+ usb_kill_anchored_urbs(&tascam->capture_anchor);
+ usb_kill_anchored_urbs(&tascam->feedback_anchor);
+ usb_kill_anchored_urbs(&tascam->midi_in_anchor);
+ usb_kill_anchored_urbs(&tascam->midi_out_anchor);
+
+ dev_info(&intf->dev, "sending deep sleep command\n");
+ int err = usb_control_msg(tascam->dev, usb_sndctrlpipe(tascam->dev, 0),
+ VENDOR_REQ_DEEP_SLEEP, RT_H2D_VENDOR_DEV,
+ 0x0000, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ dev_err(&intf->dev, "deep sleep command failed: %d\n", err);
+
+ return 0;
+}
+
+/**
+ * tascam_resume() - Handles device resumption from suspend.
+ * @intf: The USB interface being resumed.
+ *
+ * This function is called when the device resumes from suspend. It
+ * re-establishes the active USB interface settings and re-configures the sample
+ * rate if it was previously active.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int tascam_resume(struct usb_interface *intf)
+{
+ struct tascam_card *tascam = usb_get_intfdata(intf);
+ int err;
+
+ if (!tascam)
+ return 0;
+
+ dev_info(&intf->dev, "resuming TASCAM US-144MKII\n");
+
+ /*
+ * The device requires a full re-initialization sequence upon resume.
+ * First, re-establish the active USB interface settings.
+ */
+ err = usb_set_interface(tascam->dev, 0, 1);
+ if (err < 0) {
+ dev_err(&intf->dev,
+ "resume: failed to set alt setting on intf 0: %d\n",
+ err);
+ return err;
+ }
+ err = usb_set_interface(tascam->dev, 1, 1);
+ if (err < 0) {
+ dev_err(&intf->dev,
+ "resume: failed to set alt setting on intf 1: %d\n",
+ err);
+ return err;
+ }
+
+ /* Re-configure the sample rate if one was previously active */
+ if (tascam->current_rate > 0)
+ us144mkii_configure_device_for_rate(tascam,
+ tascam->current_rate);
+
+ return 0;
+}
+
+static void tascam_error_timer(struct timer_list *t)
+{
+ struct tascam_card *tascam =
+ container_of(t, struct tascam_card, error_timer);
+
+ if (atomic_read(&tascam->midi_in_active))
+ schedule_work(&tascam->midi_in_work);
+ if (atomic_read(&tascam->midi_out_active))
+ schedule_work(&tascam->midi_out_work);
+}
+
+/**
+ * tascam_probe() - Probes for the TASCAM US-144MKII device.
+ * @intf: The USB interface being probed.
+ * @usb_id: The USB device ID.
+ *
+ * This function is the entry point for the USB driver when a matching device
+ * is found. It performs initial device setup, including:
+ * - Checking for the second interface (MIDI) and associating it.
+ * - Performing a vendor-specific handshake with the device.
+ * - Setting alternate settings for USB interfaces.
+ * - Creating and registering the ALSA sound card, PCM device, and MIDI device.
+ * - Allocating and initializing URBs for audio and MIDI transfers.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int tascam_probe(struct usb_interface *intf,
+ const struct usb_device_id *usb_id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct snd_card *card;
+ struct tascam_card *tascam;
+ int err;
+ char *handshake_buf __free(kfree) = NULL;
+
+ if (dev->speed != USB_SPEED_HIGH)
+ dev_info(
+ &dev->dev,
+ "Device is connected to a USB 1.1 port, this is not supported.\n");
+
+ /* The device has two interfaces; we drive both from this driver. */
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+ tascam = usb_get_intfdata(usb_ifnum_to_if(dev, 0));
+ if (tascam) {
+ usb_set_intfdata(intf, tascam);
+ tascam->iface1 = intf;
+ }
+ return 0; /* Let the core handle this interface */
+ }
+
+ if (dev_idx >= SNDRV_CARDS) {
+ dev_err(&dev->dev, "Too many TASCAM devices present");
+ return -ENODEV;
+ }
+
+ if (!enable[dev_idx]) {
+ dev_info(&dev->dev, "TASCAM US-144MKII device disabled");
+ return -ENOENT;
+ }
+
+ handshake_buf = kmalloc(1, GFP_KERNEL);
+ if (!handshake_buf)
+ return -ENOMEM;
+
+ /* Perform vendor-specific handshake */
+ err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV,
+ MODE_VAL_HANDSHAKE_READ, 0x0000, handshake_buf, 1,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0) {
+ dev_err(&dev->dev, "Handshake read failed with %d\n", err);
+ return err;
+ }
+
+ if (handshake_buf[0] != 0x12 && handshake_buf[0] != 0x16 &&
+ handshake_buf[0] != 0x30 && handshake_buf[0] != 0x32) {
+ dev_err(&dev->dev, "Unexpected handshake value: 0x%x\n",
+ handshake_buf[0]);
+ return -ENODEV;
+ }
+
+ /* Set alternate settings to enable audio/MIDI endpoints */
+ err = usb_set_interface(dev, 0, 1);
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "Failed to set alt setting 1 on interface 0: %d\n",
+ err);
+ return err;
+ }
+
+ err = usb_set_interface(dev, 1, 1);
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "Failed to set alt setting 1 on interface 1: %d\n",
+ err);
+ return err;
+ }
+
+ err = snd_card_new(&dev->dev, index[dev_idx], id[dev_idx], THIS_MODULE,
+ sizeof(struct tascam_card), &card);
+ if (err < 0) {
+ dev_err(&dev->dev, "Failed to create sound card instance\n");
+ return err;
+ }
+
+ tascam = card->private_data;
+ card->private_free = tascam_card_private_free;
+ tascam->dev = usb_get_dev(dev);
+ tascam->card = card;
+ tascam->iface0 = intf;
+ tascam->digital_out_source = 1;
+ tascam->capture_34_source = 1;
+
+ spin_lock_init(&tascam->lock);
+ spin_lock_init(&tascam->midi_in_lock);
+ spin_lock_init(&tascam->midi_out_lock);
+ init_usb_anchor(&tascam->playback_anchor);
+ init_usb_anchor(&tascam->capture_anchor);
+ init_usb_anchor(&tascam->feedback_anchor);
+ init_usb_anchor(&tascam->midi_in_anchor);
+ init_usb_anchor(&tascam->midi_out_anchor);
+
+ timer_setup(&tascam->error_timer, tascam_error_timer, 0);
+
+ INIT_WORK(&tascam->stop_work, tascam_stop_work_handler);
+ INIT_WORK(&tascam->stop_pcm_work, tascam_stop_pcm_work_handler);
+ INIT_WORK(&tascam->capture_work, tascam_capture_work_handler);
+ init_completion(&tascam->midi_out_drain_completion);
+
+ if (kfifo_alloc(&tascam->midi_in_fifo, MIDI_IN_FIFO_SIZE, GFP_KERNEL)) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+
+ strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+ if (le16_to_cpu(dev->descriptor.idProduct) == USB_PID_TASCAM_US144) {
+ strscpy(card->shortname, "TASCAM US-144",
+ sizeof(card->shortname));
+ } else if (le16_to_cpu(dev->descriptor.idProduct) == USB_PID_TASCAM_US144MKII) {
+ strscpy(card->shortname, "TASCAM US-144MKII",
+ sizeof(card->shortname));
+ } else {
+ strscpy(card->shortname, "TASCAM Unknown",
+ sizeof(card->shortname));
+ }
+ snprintf(card->longname, sizeof(card->longname), "%s (%04x:%04x) at %s",
+ card->shortname, USB_VID_TASCAM, dev->descriptor.idProduct,
+ dev_name(&dev->dev));
+
+ err = snd_pcm_new(card, "US144MKII PCM", 0, 1, 1, &tascam->pcm);
+ if (err < 0)
+ goto free_card;
+ tascam->pcm->private_data = tascam;
+ strscpy(tascam->pcm->name, "US144MKII PCM", sizeof(tascam->pcm->name));
+
+ err = tascam_init_pcm(tascam->pcm);
+ if (err < 0)
+ goto free_card;
+
+ err = tascam_create_midi(tascam);
+ if (err < 0)
+ goto free_card;
+
+ err = tascam_create_controls(tascam);
+ if (err < 0)
+ goto free_card;
+
+ err = tascam_alloc_urbs(tascam);
+ if (err < 0)
+ goto free_card;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto free_card;
+
+ usb_set_intfdata(intf, tascam);
+
+ dev_idx++;
+ return 0;
+
+free_card:
+ tascam_free_urbs(tascam);
+ snd_card_free(card);
+ return err;
+}
+
+/**
+ * tascam_disconnect() - Disconnects the TASCAM US-144MKII device.
+ * @intf: The USB interface being disconnected.
+ *
+ * This function is called when the device is disconnected from the system.
+ * It cleans up all allocated resources, including killing URBs, freeing
+ * the sound card, and releasing memory.
+ */
+static void tascam_disconnect(struct usb_interface *intf)
+{
+ struct tascam_card *tascam = usb_get_intfdata(intf);
+
+ if (!tascam)
+ return;
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+ /* Ensure all deferred work is complete before freeing resources */
+ snd_card_disconnect(tascam->card);
+ cancel_work_sync(&tascam->stop_work);
+ cancel_work_sync(&tascam->capture_work);
+ cancel_work_sync(&tascam->midi_in_work);
+ cancel_work_sync(&tascam->midi_out_work);
+ cancel_work_sync(&tascam->stop_pcm_work);
+
+ usb_kill_anchored_urbs(&tascam->playback_anchor);
+ usb_kill_anchored_urbs(&tascam->capture_anchor);
+ usb_kill_anchored_urbs(&tascam->feedback_anchor);
+ usb_kill_anchored_urbs(&tascam->midi_in_anchor);
+ usb_kill_anchored_urbs(&tascam->midi_out_anchor);
+ timer_delete_sync(&tascam->error_timer);
+ tascam_free_urbs(tascam);
+ snd_card_free(tascam->card);
+ dev_idx--;
+ }
+}
+
+static const struct usb_device_id tascam_usb_ids[] = {
+ { USB_DEVICE(USB_VID_TASCAM, USB_PID_TASCAM_US144) },
+ { USB_DEVICE(USB_VID_TASCAM, USB_PID_TASCAM_US144MKII) },
+ { /* Terminating entry */ }
+};
+MODULE_DEVICE_TABLE(usb, tascam_usb_ids);
+
+static struct usb_driver tascam_alsa_driver = {
+ .name = DRIVER_NAME,
+ .probe = tascam_probe,
+ .disconnect = tascam_disconnect,
+ .suspend = tascam_suspend,
+ .resume = tascam_resume,
+ .id_table = tascam_usb_ids,
+};
+
+module_usb_driver(tascam_alsa_driver);
diff --git a/sound/usb/usx2y/us144mkii.h b/sound/usb/usx2y/us144mkii.h
new file mode 100644
index 000000000000..95c4341f038a
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii.h
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#ifndef __US144MKII_H
+#define __US144MKII_H
+
+#include <linux/kfifo.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#define DRIVER_NAME "us144mkii"
+
+/* --- USB Device Identification --- */
+#define USB_VID_TASCAM 0x0644
+#define USB_PID_TASCAM_US144 0x800f
+#define USB_PID_TASCAM_US144MKII 0x8020
+
+/* --- USB Endpoints (Alternate Setting 1) --- */
+#define EP_PLAYBACK_FEEDBACK 0x81
+#define EP_AUDIO_OUT 0x02
+#define EP_MIDI_IN 0x83
+#define EP_MIDI_OUT 0x04
+#define EP_AUDIO_IN 0x86
+
+/* --- USB Control Message Protocol --- */
+#define RT_H2D_CLASS_EP (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT)
+#define RT_D2H_CLASS_EP (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT)
+#define RT_H2D_VENDOR_DEV (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+#define RT_D2H_VENDOR_DEV (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+
+enum uac_request {
+ UAC_SET_CUR = 0x01,
+ UAC_GET_CUR = 0x81,
+};
+
+enum uac_control_selector {
+ UAC_SAMPLING_FREQ_CONTROL = 0x0100,
+};
+
+enum tascam_vendor_request {
+ VENDOR_REQ_REGISTER_WRITE = 0x41,
+ VENDOR_REQ_DEEP_SLEEP = 0x44,
+ VENDOR_REQ_MODE_CONTROL = 0x49,
+};
+
+enum tascam_mode_value {
+ MODE_VAL_HANDSHAKE_READ = 0x0000,
+ MODE_VAL_CONFIG = 0x0010,
+ MODE_VAL_STREAM_START = 0x0030,
+};
+
+#define HANDSHAKE_SUCCESS_VAL 0x12
+
+enum tascam_register {
+ REG_ADDR_UNKNOWN_0D = 0x0d04,
+ REG_ADDR_UNKNOWN_0E = 0x0e00,
+ REG_ADDR_UNKNOWN_0F = 0x0f00,
+ REG_ADDR_RATE_44100 = 0x1000,
+ REG_ADDR_RATE_48000 = 0x1002,
+ REG_ADDR_RATE_88200 = 0x1008,
+ REG_ADDR_RATE_96000 = 0x100a,
+ REG_ADDR_UNKNOWN_11 = 0x110b,
+};
+
+#define REG_VAL_ENABLE 0x0101
+
+/* --- URB Configuration --- */
+#define NUM_PLAYBACK_URBS 4
+#define PLAYBACK_URB_PACKETS 8
+#define NUM_FEEDBACK_URBS 4
+#define FEEDBACK_URB_PACKETS 1
+#define FEEDBACK_PACKET_SIZE 3
+#define NUM_CAPTURE_URBS 8
+#define CAPTURE_URB_SIZE 512
+#define CAPTURE_RING_BUFFER_SIZE (CAPTURE_URB_SIZE * NUM_CAPTURE_URBS * 4)
+#define NUM_MIDI_IN_URBS 4
+#define MIDI_IN_BUF_SIZE 64
+#define MIDI_IN_FIFO_SIZE (MIDI_IN_BUF_SIZE * NUM_MIDI_IN_URBS)
+#define MIDI_OUT_BUF_SIZE 64
+#define NUM_MIDI_OUT_URBS 4
+#define USB_CTRL_TIMEOUT_MS 1000
+#define FEEDBACK_SYNC_LOSS_THRESHOLD 41
+
+/* --- Audio Format Configuration --- */
+#define BYTES_PER_SAMPLE 3
+#define NUM_CHANNELS 4
+#define BYTES_PER_FRAME (NUM_CHANNELS * BYTES_PER_SAMPLE)
+#define FEEDBACK_ACCUMULATOR_SIZE 128
+
+/* --- Capture Decoding Defines --- */
+#define DECODED_CHANNELS_PER_FRAME 4
+#define DECODED_SAMPLE_SIZE 4
+#define FRAMES_PER_DECODE_BLOCK 8
+#define RAW_BYTES_PER_DECODE_BLOCK 512
+
+/**
+ * struct us144mkii_frame_pattern_observer - State for dynamic feedback
+ * patterns.
+ * @sample_rate_khz: The current sample rate in kHz.
+ * @base_feedback_value: The nominal feedback value for the current rate.
+ * @feedback_offset: An offset to align the feedback value range.
+ * @full_frame_patterns: A 2D array of pre-calculated packet size patterns.
+ * @current_index: The current index into the pattern array.
+ * @previous_index: The previous index, used for state tracking.
+ * @sync_locked: A flag indicating if the pattern has locked to the stream.
+ */
+struct us144mkii_frame_pattern_observer {
+ unsigned int sample_rate_khz;
+ unsigned int base_feedback_value;
+ int feedback_offset;
+ unsigned int full_frame_patterns[5][8];
+ unsigned int current_index;
+ unsigned int previous_index;
+ bool sync_locked;
+};
+
+/**
+ * struct tascam_card - Main driver data structure for the TASCAM US-144MKII.
+ * @dev: Pointer to the USB device.
+ * @iface0: Pointer to USB interface 0 (audio).
+ * @iface1: Pointer to USB interface 1 (MIDI).
+ * @card: Pointer to the ALSA sound card instance.
+ * @pcm: Pointer to the ALSA PCM device.
+ * @rmidi: Pointer to the ALSA rawmidi device.
+ *
+ * @playback_substream: Pointer to the active playback PCM substream.
+ * @playback_urbs: Array of URBs for playback.
+ * @playback_urb_alloc_size: Size of allocated buffer for each playback URB.
+ * @feedback_urbs: Array of URBs for feedback.
+ * @feedback_urb_alloc_size: Size of allocated buffer for each feedback URB.
+ * @playback_active: Atomic flag indicating if playback is active.
+ * @playback_frames_consumed: Total frames consumed by playback.
+ * @driver_playback_pos: Current position in the ALSA playback buffer (frames).
+ * @last_period_pos: Last reported period position for playback.
+ *
+ * @capture_substream: Pointer to the active capture PCM substream.
+ * @capture_urbs: Array of URBs for capture.
+ * @capture_urb_alloc_size: Size of allocated buffer for each capture URB.
+ * @capture_active: Atomic flag indicating if capture is active.
+ * @driver_capture_pos: Current position in the ALSA capture buffer (frames).
+ * @capture_frames_processed: Total frames processed for capture.
+ * @last_capture_period_pos: Last reported period position for capture.
+ * @capture_ring_buffer: Ring buffer for raw capture data from USB.
+ * @capture_ring_buffer_read_ptr: Read pointer for the capture ring buffer.
+ * @capture_ring_buffer_write_ptr: Write pointer for the capture ring buffer.
+ * @capture_decode_raw_block: Buffer for a raw 512-byte capture block.
+ * @capture_decode_dst_block: Buffer for decoded 32-bit capture samples.
+ * @capture_routing_buffer: Intermediate buffer for capture routing.
+ * @capture_work: Work struct for deferred capture processing.
+ * @stop_work: Work struct for deferred stream stopping.
+ * @stop_pcm_work: Work struct for stopping PCM due to a fatal error (e.g.
+ * xrun).
+ *
+ * @midi_in_substream: Pointer to the active MIDI input substream.
+ * @midi_out_substream: Pointer to the active MIDI output substream.
+ * @midi_in_urbs: Array of URBs for MIDI input.
+ * @midi_out_urbs: Array of URBs for MIDI output.
+ * @midi_in_active: Atomic flag indicating if MIDI input is active.
+ * @midi_out_active: Atomic flag indicating if MIDI output is active.
+ * @midi_in_fifo: FIFO for raw MIDI input data.
+ * @midi_in_work: Work struct for deferred MIDI input processing.
+ * @midi_out_work: Work struct for deferred MIDI output processing.
+ * @midi_in_lock: Spinlock for MIDI input FIFO.
+ * @midi_out_lock: Spinlock for MIDI output.
+ * @midi_out_urbs_in_flight: Bitmap of MIDI output URBs currently in flight.
+ * @midi_running_status: Stores the last MIDI status byte for running status.
+ * @error_timer: Timer for MIDI error retry logic.
+ *
+ * @lock: Main spinlock for protecting shared driver state.
+ * @active_urbs: Atomic counter for active URBs.
+ * @current_rate: Currently configured sample rate of the device.
+ * @line_out_source: Source for Line Outputs (0: Playback 1-2, 1: Playback 3-4).
+ * @digital_out_source: Source for Digital Outputs (0: Playback 1-2, 1: Playback
+ * 3-4).
+ * @capture_12_source: Source for Capture channels 1-2 (0: Analog In, 1: Digital
+ * In).
+ * @capture_34_source: Source for Capture channels 3-4 (0: Analog In, 1: Digital
+ * In).
+ *
+ * @feedback_accumulator_pattern: Stores the calculated frames per packet for
+ * feedback.
+ * @feedback_pattern_out_idx: Read index for feedback_accumulator_pattern.
+ * @feedback_pattern_in_idx: Write index for feedback_accumulator_pattern.
+ * @feedback_synced: Flag indicating if feedback is synced.
+ * @feedback_consecutive_errors: Counter for consecutive feedback errors.
+ * @feedback_urb_skip_count: Number of feedback URBs to skip initially for
+ * stabilization.
+ * @fpo: Holds the state for the dynamic feedback pattern generation.
+ *
+ * @playback_anchor: USB anchor for playback URBs.
+ * @capture_anchor: USB anchor for capture URBs.
+ * @feedback_anchor: USB anchor for feedback URBs.
+ * @midi_in_anchor: USB anchor for MIDI input URBs.
+ * @midi_out_anchor: USB anchor for MIDI output URBs.
+ */
+struct tascam_card {
+ /* --- Core device pointers --- */
+ struct usb_device *dev;
+ struct usb_interface *iface0;
+ struct usb_interface *iface1;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_rawmidi *rmidi;
+
+ /* --- PCM Substreams --- */
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ /* --- URBs and Anchors --- */
+ struct urb *playback_urbs[NUM_PLAYBACK_URBS];
+ size_t playback_urb_alloc_size;
+ struct urb *feedback_urbs[NUM_FEEDBACK_URBS];
+ size_t feedback_urb_alloc_size;
+ struct urb *capture_urbs[NUM_CAPTURE_URBS];
+ size_t capture_urb_alloc_size;
+ struct urb *midi_in_urbs[NUM_MIDI_IN_URBS];
+ struct urb *midi_out_urbs[NUM_MIDI_OUT_URBS];
+ struct usb_anchor playback_anchor;
+ struct usb_anchor capture_anchor;
+ struct usb_anchor feedback_anchor;
+ struct usb_anchor midi_in_anchor;
+ struct usb_anchor midi_out_anchor;
+
+ /* --- Stream State --- */
+ spinlock_t lock;
+ atomic_t playback_active;
+ atomic_t capture_active;
+ atomic_t active_urbs;
+ int current_rate;
+
+ /* --- Playback State --- */
+ u64 playback_frames_consumed;
+ snd_pcm_uframes_t driver_playback_pos;
+ u64 last_period_pos;
+
+ /* --- Capture State --- */
+ u64 capture_frames_processed;
+ snd_pcm_uframes_t driver_capture_pos;
+ u64 last_capture_period_pos;
+ u8 *capture_ring_buffer;
+ size_t capture_ring_buffer_read_ptr;
+ size_t capture_ring_buffer_write_ptr;
+ u8 *capture_decode_raw_block;
+ s32 *capture_decode_dst_block;
+ s32 *capture_routing_buffer;
+
+ /* --- MIDI State --- */
+ struct snd_rawmidi_substream *midi_in_substream;
+ struct snd_rawmidi_substream *midi_out_substream;
+ atomic_t midi_in_active;
+ atomic_t midi_out_active;
+ struct kfifo midi_in_fifo;
+ spinlock_t midi_in_lock;
+ spinlock_t midi_out_lock;
+ unsigned long midi_out_urbs_in_flight;
+ u8 midi_running_status;
+ struct timer_list error_timer;
+ struct completion midi_out_drain_completion;
+
+ /* --- Feedback Sync State --- */
+ unsigned int feedback_accumulator_pattern[FEEDBACK_ACCUMULATOR_SIZE];
+ unsigned int feedback_pattern_out_idx;
+ unsigned int feedback_pattern_in_idx;
+ bool feedback_synced;
+ unsigned int feedback_consecutive_errors;
+ unsigned int feedback_urb_skip_count;
+ struct us144mkii_frame_pattern_observer fpo;
+
+ /* --- Workqueues --- */
+ struct work_struct stop_work;
+ struct work_struct stop_pcm_work;
+ struct work_struct capture_work;
+ struct work_struct midi_in_work;
+ struct work_struct midi_out_work;
+
+ /* --- Mixer/Routing State --- */
+ unsigned int line_out_source;
+ unsigned int digital_out_source;
+ unsigned int capture_12_source;
+ unsigned int capture_34_source;
+};
+
+/* main.c */
+/**
+ * tascam_free_urbs() - Free all allocated URBs and associated buffers.
+ * @tascam: the tascam_card instance
+ *
+ * This function kills, unlinks, and frees all playback, feedback, capture,
+ * and MIDI URBs, along with their transfer buffers and the capture
+ * ring/decode buffers.
+ */
+void tascam_free_urbs(struct tascam_card *tascam);
+
+/**
+ * tascam_alloc_urbs() - Allocate all URBs and associated buffers.
+ * @tascam: the tascam_card instance
+ *
+ * This function allocates and initializes all URBs for playback, feedback,
+ * capture, and MIDI, as well as the necessary buffers for data processing.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int tascam_alloc_urbs(struct tascam_card *tascam);
+
+/**
+ * tascam_stop_work_handler() - Work handler to stop all active streams.
+ * @work: Pointer to the work_struct.
+ *
+ * This function is scheduled to stop all active URBs (playback, feedback,
+ * capture) and reset the active_urbs counter.
+ */
+void tascam_stop_work_handler(struct work_struct *work);
+
+/* us144mkii_pcm.h */
+#include "us144mkii_pcm.h"
+
+/* us144mkii_midi.c */
+/**
+ * tascam_midi_in_urb_complete() - Completion handler for MIDI IN URBs
+ * @urb: The completed URB.
+ *
+ * This function runs in interrupt context. It places the raw data from the
+ * USB endpoint into a kfifo and schedules a work item to process it later,
+ * ensuring the interrupt handler remains fast.
+ */
+void tascam_midi_in_urb_complete(struct urb *urb);
+
+/**
+ * tascam_midi_out_urb_complete() - Completion handler for MIDI OUT bulk URB.
+ * @urb: The completed URB.
+ *
+ * This function runs in interrupt context. It marks the output URB as no
+ * longer in-flight. It then re-schedules the work handler to check for and
+ * send any more data waiting in the ALSA buffer. This is a safe, non-blocking
+ * way to continue the data transmission chain.
+ */
+void tascam_midi_out_urb_complete(struct urb *urb);
+
+/**
+ * tascam_create_midi() - Create and initialize the ALSA rawmidi device.
+ * @tascam: The driver instance.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int tascam_create_midi(struct tascam_card *tascam);
+
+/* us144mkii_controls.c */
+/**
+ * tascam_create_controls() - Creates and adds ALSA mixer controls for the
+ * device.
+ * @tascam: The driver instance.
+ *
+ * This function registers custom ALSA controls for managing audio routing
+ * (line out source, digital out source, capture 1-2 source, capture 3-4 source)
+ * and displaying the current sample rate.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int tascam_create_controls(struct tascam_card *tascam);
+
+#endif /* __US144MKII_H */
diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c
new file mode 100644
index 000000000000..00188ff6cd51
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii_capture.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#include "us144mkii.h"
+
+/**
+ * tascam_capture_open() - Opens the PCM capture substream.
+ * @substream: The ALSA PCM substream to open.
+ *
+ * This function sets the hardware parameters for the capture substream
+ * and stores a reference to the substream in the driver's private data.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_capture_open(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+
+ substream->runtime->hw = tascam_pcm_hw;
+ tascam->capture_substream = substream;
+ atomic_set(&tascam->capture_active, 0);
+
+ return 0;
+}
+
+/**
+ * tascam_capture_close() - Closes the PCM capture substream.
+ * @substream: The ALSA PCM substream to close.
+ *
+ * This function clears the reference to the capture substream in the
+ * driver's private data.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_capture_close(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+
+ tascam->capture_substream = NULL;
+
+ return 0;
+}
+
+/**
+ * tascam_capture_prepare() - Prepares the PCM capture substream for use.
+ * @substream: The ALSA PCM substream to prepare.
+ *
+ * This function initializes capture-related counters and ring buffer pointers.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+
+ tascam->driver_capture_pos = 0;
+ tascam->capture_frames_processed = 0;
+ tascam->last_capture_period_pos = 0;
+ tascam->capture_ring_buffer_read_ptr = 0;
+ tascam->capture_ring_buffer_write_ptr = 0;
+
+ return 0;
+}
+
+/**
+ * tascam_capture_pointer() - Returns the current capture pointer position.
+ * @substream: The ALSA PCM substream.
+ *
+ * This function returns the current position of the capture pointer within
+ * the ALSA ring buffer, in frames.
+ *
+ * Return: The current capture pointer position in frames.
+ */
+static snd_pcm_uframes_t
+tascam_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u64 pos;
+
+ if (!atomic_read(&tascam->capture_active))
+ return 0;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ pos = tascam->capture_frames_processed;
+ }
+
+ if (runtime->buffer_size == 0)
+ return 0;
+
+ return do_div(pos, runtime->buffer_size);
+}
+
+/**
+ * tascam_capture_ops - ALSA PCM operations for capture.
+ *
+ * This structure defines the callback functions for capture stream operations,
+ * including open, close, ioctl, hardware parameters, hardware free, prepare,
+ * trigger, and pointer.
+ */
+const struct snd_pcm_ops tascam_capture_ops = {
+ .open = tascam_capture_open,
+ .close = tascam_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = tascam_pcm_hw_params,
+ .hw_free = tascam_pcm_hw_free,
+ .prepare = tascam_capture_prepare,
+ .trigger = tascam_pcm_trigger,
+ .pointer = tascam_capture_pointer,
+};
+
+/**
+ * decode_tascam_capture_block() - Decodes a raw 512-byte block from the device.
+ * @src_block: Pointer to the 512-byte raw source block.
+ * @dst_block: Pointer to the destination buffer for decoded audio frames.
+ *
+ * The device sends audio data in a complex, multiplexed format. This function
+ * demultiplexes the bits from the raw block into 8 frames of 4-channel,
+ * 24-bit audio (stored in 32-bit containers).
+ */
+static void decode_tascam_capture_block(const u8 *src_block, s32 *dst_block)
+{
+ int frame, bit;
+
+ memset(dst_block, 0,
+ FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
+ DECODED_SAMPLE_SIZE);
+
+ for (frame = 0; frame < FRAMES_PER_DECODE_BLOCK; ++frame) {
+ const u8 *p_src_frame_base = src_block + frame * 64;
+ s32 *p_dst_frame = dst_block + frame * 4;
+
+ s32 ch[4] = { 0 };
+
+ for (bit = 0; bit < 24; ++bit) {
+ u8 byte1 = p_src_frame_base[bit];
+ u8 byte2 = p_src_frame_base[bit + 32];
+
+ ch[0] = (ch[0] << 1) | (byte1 & 1);
+ ch[2] = (ch[2] << 1) | ((byte1 >> 1) & 1);
+
+ ch[1] = (ch[1] << 1) | (byte2 & 1);
+ ch[3] = (ch[3] << 1) | ((byte2 >> 1) & 1);
+ }
+
+ /*
+ * The result is a 24-bit sample. Shift left by 8 to align it to
+ * the most significant bits of a 32-bit integer (S32_LE format).
+ */
+ p_dst_frame[0] = ch[0] << 8;
+ p_dst_frame[1] = ch[1] << 8;
+ p_dst_frame[2] = ch[2] << 8;
+ p_dst_frame[3] = ch[3] << 8;
+ }
+}
+
+void tascam_capture_work_handler(struct work_struct *work)
+{
+ struct tascam_card *tascam =
+ container_of(work, struct tascam_card, capture_work);
+ struct snd_pcm_substream *substream = tascam->capture_substream;
+ struct snd_pcm_runtime *runtime;
+ u8 *raw_block = tascam->capture_decode_raw_block;
+ s32 *decoded_block = tascam->capture_decode_dst_block;
+ s32 *routed_block = tascam->capture_routing_buffer;
+
+ if (!substream || !substream->runtime)
+ return;
+ runtime = substream->runtime;
+
+ if (!raw_block || !decoded_block || !routed_block) {
+ dev_err(tascam->card->dev,
+ "Capture decode/routing buffers not allocated!\n");
+ return;
+ }
+
+ while (atomic_read(&tascam->capture_active)) {
+ size_t write_ptr, read_ptr, available_data;
+ bool can_process;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ write_ptr = tascam->capture_ring_buffer_write_ptr;
+ read_ptr = tascam->capture_ring_buffer_read_ptr;
+ available_data = (write_ptr >= read_ptr) ?
+ (write_ptr - read_ptr) :
+ (CAPTURE_RING_BUFFER_SIZE -
+ read_ptr + write_ptr);
+ can_process =
+ (available_data >= RAW_BYTES_PER_DECODE_BLOCK);
+
+ if (can_process) {
+ size_t bytes_to_end =
+ CAPTURE_RING_BUFFER_SIZE - read_ptr;
+ if (bytes_to_end >=
+ RAW_BYTES_PER_DECODE_BLOCK) {
+ memcpy(raw_block,
+ tascam->capture_ring_buffer +
+ read_ptr,
+ RAW_BYTES_PER_DECODE_BLOCK);
+ } else {
+ memcpy(raw_block,
+ tascam->capture_ring_buffer +
+ read_ptr,
+ bytes_to_end);
+ memcpy(raw_block + bytes_to_end,
+ tascam->capture_ring_buffer,
+ RAW_BYTES_PER_DECODE_BLOCK -
+ bytes_to_end);
+ }
+ tascam->capture_ring_buffer_read_ptr =
+ (read_ptr +
+ RAW_BYTES_PER_DECODE_BLOCK) %
+ CAPTURE_RING_BUFFER_SIZE;
+ }
+ }
+
+ if (!can_process)
+ break;
+
+ decode_tascam_capture_block(raw_block, decoded_block);
+ process_capture_routing_us144mkii(tascam, decoded_block,
+ routed_block);
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (atomic_read(&tascam->capture_active)) {
+ int f;
+
+ for (f = 0; f < FRAMES_PER_DECODE_BLOCK; ++f) {
+ u8 *dst_frame_start =
+ runtime->dma_area +
+ frames_to_bytes(
+ runtime,
+ tascam->driver_capture_pos);
+ s32 *routed_frame_start =
+ routed_block +
+ (f * NUM_CHANNELS);
+ int c;
+
+ for (c = 0; c < NUM_CHANNELS; c++) {
+ u8 *dst_channel =
+ dst_frame_start +
+ (c * BYTES_PER_SAMPLE);
+ s32 *src_channel_s32 =
+ routed_frame_start + c;
+
+ memcpy(dst_channel,
+ ((char *)src_channel_s32) +
+ 1,
+ 3);
+ }
+
+ tascam->driver_capture_pos =
+ (tascam->driver_capture_pos +
+ 1) %
+ runtime->buffer_size;
+ }
+ }
+ }
+ }
+}
+
+void capture_urb_complete(struct urb *urb)
+{
+ struct tascam_card *tascam = urb->context;
+ int ret;
+
+ if (urb->status) {
+ if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
+ urb->status != -ESHUTDOWN && urb->status != -ENODEV &&
+ urb->status != -EPROTO)
+ dev_err_ratelimited(tascam->card->dev,
+ "Capture URB failed: %d\n",
+ urb->status);
+ goto out;
+ }
+ if (!tascam || !atomic_read(&tascam->capture_active))
+ goto out;
+
+ if (urb->actual_length > 0) {
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ size_t write_ptr = tascam->capture_ring_buffer_write_ptr;
+ size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - write_ptr;
+
+ if (urb->actual_length > bytes_to_end) {
+ memcpy(tascam->capture_ring_buffer + write_ptr,
+ urb->transfer_buffer, bytes_to_end);
+ memcpy(tascam->capture_ring_buffer,
+ urb->transfer_buffer + bytes_to_end,
+ urb->actual_length - bytes_to_end);
+ } else {
+ memcpy(tascam->capture_ring_buffer + write_ptr,
+ urb->transfer_buffer,
+ urb->actual_length);
+ }
+
+ tascam->capture_ring_buffer_write_ptr =
+ (write_ptr + urb->actual_length) %
+ CAPTURE_RING_BUFFER_SIZE;
+ }
+
+ schedule_work(&tascam->capture_work);
+ }
+
+ usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->capture_anchor);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err_ratelimited(tascam->card->dev,
+ "Failed to resubmit capture URB: %d\n",
+ ret);
+ usb_unanchor_urb(urb);
+ usb_put_urb(urb);
+ atomic_dec(
+ &tascam->active_urbs); /* Decrement on failed resubmission */
+ }
+out:
+ usb_put_urb(urb);
+}
+
diff --git a/sound/usb/usx2y/us144mkii_controls.c b/sound/usb/usx2y/us144mkii_controls.c
new file mode 100644
index 000000000000..5d69441ef414
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii_controls.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#include "us144mkii.h"
+
+/**
+ * @brief Text descriptions for playback output source options.
+ *
+ * Used by ALSA kcontrol elements to provide user-friendly names for
+ * the playback routing options (e.g., "Playback 1-2", "Playback 3-4").
+ */
+static const char *const playback_source_texts[] = { "Playback 1-2",
+ "Playback 3-4" };
+
+/**
+ * @brief Text descriptions for capture input source options.
+ *
+ * Used by ALSA kcontrol elements to provide user-friendly names for
+ * the capture routing options (e.g., "Analog In", "Digital In").
+ */
+static const char *const capture_source_texts[] = { "Analog In", "Digital In" };
+
+/**
+ * tascam_playback_source_info() - ALSA control info callback for playback
+ * source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @uinfo: The ALSA control element info structure to fill.
+ *
+ * This function provides information about the enumerated playback source
+ * control, including its type, count, and available items (Playback 1-2,
+ * Playback 3-4).
+ *
+ * Return: 0 on success.
+ */
+static int tascam_playback_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1, 2, playback_source_texts);
+}
+
+/**
+ * tascam_line_out_get() - ALSA control get callback for Line Outputs Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure to fill.
+ *
+ * This function retrieves the current selection for the Line Outputs source
+ * (Playback 1-2 or Playback 3-4) from the driver's private data and populates
+ * the ALSA control element value.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_line_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ ucontrol->value.enumerated.item[0] = tascam->line_out_source;
+ }
+ return 0;
+}
+
+/**
+ * tascam_line_out_put() - ALSA control put callback for Line Outputs Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure containing the new value.
+ *
+ * This function sets the Line Outputs source (Playback 1-2 or Playback 3-4)
+ * based on the user's selection from the ALSA control element. It validates
+ * the input and updates the driver's private data.
+ *
+ * Return: 1 if the value was changed, 0 if unchanged, or a negative error code.
+ */
+static int tascam_line_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (tascam->line_out_source != ucontrol->value.enumerated.item[0]) {
+ tascam->line_out_source = ucontrol->value.enumerated.item[0];
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/**
+ * tascam_line_out_control - ALSA kcontrol definition for Line Outputs Source.
+ *
+ * This defines a new ALSA mixer control named "Line OUTPUTS Source" that allows
+ * the user to select between "Playback 1-2" and "Playback 3-4" for the analog
+ * line outputs of the device. It uses the `tascam_playback_source_info` for
+ * information and `tascam_line_out_get`/`tascam_line_out_put` for value
+ * handling.
+ */
+static const struct snd_kcontrol_new tascam_line_out_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Playback Source",
+ .info = tascam_playback_source_info,
+ .get = tascam_line_out_get,
+ .put = tascam_line_out_put,
+};
+
+/**
+ * tascam_digital_out_get() - ALSA control get callback for Digital Outputs
+ * Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure to fill.
+ *
+ * This function retrieves the current selection for the Digital Outputs source
+ * (Playback 1-2 or Playback 3-4) from the driver's private data and populates
+ * the ALSA control element value.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_digital_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ ucontrol->value.enumerated.item[0] = tascam->digital_out_source;
+ }
+ return 0;
+}
+
+/**
+ * tascam_digital_out_put() - ALSA control put callback for Digital Outputs
+ * Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure containing the new value.
+ *
+ * This function sets the Digital Outputs source (Playback 1-2 or Playback 3-4)
+ * based on the user's selection from the ALSA control element. It validates
+ * the input and updates the driver's private data.
+ *
+ * Return: 1 if the value was changed, 0 if unchanged, or a negative error code.
+ */
+static int tascam_digital_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (tascam->digital_out_source != ucontrol->value.enumerated.item[0]) {
+ tascam->digital_out_source = ucontrol->value.enumerated.item[0];
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/**
+ * tascam_digital_out_control - ALSA kcontrol definition for Digital Outputs
+ * Source.
+ *
+ * This defines a new ALSA mixer control named "Digital OUTPUTS Source" that
+ * allows the user to select between "Playback 1-2" and "Playback 3-4" for the
+ * digital outputs of the device. It uses the `tascam_playback_source_info` for
+ * information and `tascam_digital_out_get`/`tascam_digital_out_put` for value
+ * handling.
+ */
+static const struct snd_kcontrol_new tascam_digital_out_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Playback Source",
+ .info = tascam_playback_source_info,
+ .get = tascam_digital_out_get,
+ .put = tascam_digital_out_put,
+};
+
+/**
+ * tascam_capture_source_info() - ALSA control info callback for capture source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @uinfo: The ALSA control element info structure to fill.
+ *
+ * This function provides information about the enumerated capture source
+ * control, including its type, count, and available items (Analog In, Digital
+ * In).
+ *
+ * Return: 0 on success.
+ */
+static int tascam_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1, 2, capture_source_texts);
+}
+
+/**
+ * tascam_capture_12_get() - ALSA control get callback for Capture channels 1
+ * and 2 Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure to fill.
+ *
+ * This function retrieves the current selection for the Capture channels 1 and
+ * 2 source (Analog In or Digital In) from the driver's private data and
+ * populates the ALSA control element value.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_capture_12_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ ucontrol->value.enumerated.item[0] = tascam->capture_12_source;
+ }
+ return 0;
+}
+
+/**
+ * tascam_capture_12_put() - ALSA control put callback for Capture channels 1
+ * and 2 Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure containing the new value.
+ *
+ * This function sets the Capture channels 1 and 2 source (Analog In or Digital
+ * In) based on the user's selection from the ALSA control element. It validates
+ * the input and updates the driver's private data.
+ *
+ * Return: 1 if the value was changed, 0 if unchanged, or a negative error code.
+ */
+static int tascam_capture_12_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (tascam->capture_12_source != ucontrol->value.enumerated.item[0]) {
+ tascam->capture_12_source = ucontrol->value.enumerated.item[0];
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/**
+ * tascam_capture_12_control - ALSA kcontrol definition for Capture channels 1
+ * and 2 Source.
+ *
+ * This defines a new ALSA mixer control named "ch1 and ch2 Source" that allows
+ * the user to select between "Analog In" and "Digital In" for the first two
+ * capture channels of the device. It uses the `tascam_capture_source_info` for
+ * information and `tascam_capture_12_get`/`tascam_capture_12_put` for value
+ * handling.
+ */
+static const struct snd_kcontrol_new tascam_capture_12_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Ch1/2 Capture Source",
+ .info = tascam_capture_source_info,
+ .get = tascam_capture_12_get,
+ .put = tascam_capture_12_put,
+};
+
+/**
+ * tascam_capture_34_get() - ALSA control get callback for Capture channels 3
+ * and 4 Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure to fill.
+ *
+ * This function retrieves the current selection for the Capture channels 3 and
+ * 4 source (Analog In or Digital In) from the driver's private data and
+ * populates the ALSA control element value.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_capture_34_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ ucontrol->value.enumerated.item[0] = tascam->capture_34_source;
+ }
+ return 0;
+}
+
+/**
+ * tascam_capture_34_put() - ALSA control put callback for Capture channels 3
+ * and 4 Source.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure containing the new value.
+ *
+ * This function sets the Capture channels 3 and 4 source (Analog In or Digital
+ * In) based on the user's selection from the ALSA control element. It validates
+ * the input and updates the driver's private data.
+ *
+ * Return: 1 if the value was changed, 0 if unchanged, or a negative error code.
+ */
+static int tascam_capture_34_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (tascam->capture_34_source != ucontrol->value.enumerated.item[0]) {
+ tascam->capture_34_source = ucontrol->value.enumerated.item[0];
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/**
+ * tascam_capture_34_control - ALSA kcontrol definition for Capture channels 3
+ * and 4 Source.
+ *
+ * This defines a new ALSA mixer control named "ch3 and ch4 Source" that allows
+ * the user to select between "Analog In" and "Digital In" for the third and
+ * fourth capture channels of the device. It uses the
+ * `tascam_capture_source_info` for information and
+ * `tascam_capture_34_get`/`tascam_capture_34_put` for value handling.
+ */
+static const struct snd_kcontrol_new tascam_capture_34_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Ch3/4 Capture Source",
+ .info = tascam_capture_source_info,
+ .get = tascam_capture_34_get,
+ .put = tascam_capture_34_put,
+};
+
+/**
+ * tascam_samplerate_info() - ALSA control info callback for Sample Rate.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @uinfo: The ALSA control element info structure to fill.
+ *
+ * This function provides information about the Sample Rate control, defining
+ * it as an integer type with a minimum value of 0 and a maximum of 96000.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_samplerate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 96000;
+ return 0;
+}
+
+/**
+ * tascam_samplerate_get() - ALSA control get callback for Sample Rate.
+ * @kcontrol: The ALSA kcontrol instance.
+ * @ucontrol: The ALSA control element value structure to fill.
+ *
+ * This function retrieves the current sample rate from the device via a USB
+ * control message and populates the ALSA control element value. If the rate
+ * is already known (i.e., `current_rate` is set), it returns that value
+ * directly.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int tascam_samplerate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tascam_card *tascam =
+ (struct tascam_card *)snd_kcontrol_chip(kcontrol);
+ u8 *buf __free(kfree) = NULL;
+ int err;
+ u32 rate = 0;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (tascam->current_rate > 0) {
+ ucontrol->value.integer.value[0] = tascam->current_rate;
+ return 0;
+ }
+ }
+
+ buf = kmalloc(3, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_control_msg(tascam->dev, usb_rcvctrlpipe(tascam->dev, 0),
+ UAC_GET_CUR, RT_D2H_CLASS_EP,
+ UAC_SAMPLING_FREQ_CONTROL, EP_AUDIO_IN, buf, 3,
+ USB_CTRL_TIMEOUT_MS);
+
+ if (err >= 3)
+ rate = buf[0] | (buf[1] << 8) | (buf[2] << 16);
+
+ ucontrol->value.integer.value[0] = rate;
+ return 0;
+}
+
+/**
+ * tascam_samplerate_control - ALSA kcontrol definition for Sample Rate.
+ *
+ * This defines a new ALSA mixer control named "Sample Rate" that displays
+ * the current sample rate of the device. It is a read-only control.
+ */
+static const struct snd_kcontrol_new tascam_samplerate_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sample Rate",
+ .info = tascam_samplerate_info,
+ .get = tascam_samplerate_get,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+};
+
+int tascam_create_controls(struct tascam_card *tascam)
+{
+ int err;
+
+ err = snd_ctl_add(tascam->card,
+ snd_ctl_new1(&tascam_line_out_control, tascam));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(tascam->card,
+ snd_ctl_new1(&tascam_digital_out_control, tascam));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(tascam->card,
+ snd_ctl_new1(&tascam_capture_12_control, tascam));
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(tascam->card,
+ snd_ctl_new1(&tascam_capture_34_control, tascam));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(tascam->card,
+ snd_ctl_new1(&tascam_samplerate_control, tascam));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
diff --git a/sound/usb/usx2y/us144mkii_midi.c b/sound/usb/usx2y/us144mkii_midi.c
new file mode 100644
index 000000000000..ed2afec2a89a
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii_midi.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#include "us144mkii.h"
+
+/**
+ * tascam_midi_in_work_handler() - Deferred work for processing MIDI input.
+ * @work: The work_struct instance.
+ *
+ * This function runs in a thread context. It safely reads raw USB data from
+ * the kfifo, processes it by stripping protocol-specific padding bytes, and
+ * passes the clean MIDI data to the ALSA rawmidi subsystem.
+ */
+static void tascam_midi_in_work_handler(struct work_struct *work)
+{
+ struct tascam_card *tascam =
+ container_of(work, struct tascam_card, midi_in_work);
+ u8 buf[9];
+ u8 clean_buf[8];
+ unsigned int count, clean_count;
+
+ if (!tascam->midi_in_substream)
+ return;
+
+ while (kfifo_out_spinlocked(&tascam->midi_in_fifo, buf, sizeof(buf),
+ &tascam->midi_in_lock) == sizeof(buf)) {
+ clean_count = 0;
+ for (count = 0; count < 8; ++count) {
+ if (buf[count] != 0xfd)
+ clean_buf[clean_count++] = buf[count];
+ }
+
+ if (clean_count > 0)
+ snd_rawmidi_receive(tascam->midi_in_substream,
+ clean_buf, clean_count);
+ }
+}
+
+void tascam_midi_in_urb_complete(struct urb *urb)
+{
+ struct tascam_card *tascam = urb->context;
+ int ret;
+
+ if (!tascam)
+ goto out;
+
+ if (urb->status) {
+ if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
+ urb->status != -ESHUTDOWN && urb->status != -EPROTO) {
+ dev_err_ratelimited(tascam->card->dev,
+ "MIDI IN URB failed: status %d\n",
+ urb->status);
+ }
+ goto out;
+ }
+
+ if (atomic_read(&tascam->midi_in_active) &&
+ urb->actual_length > 0) {
+ kfifo_in_spinlocked(&tascam->midi_in_fifo, urb->transfer_buffer,
+ urb->actual_length, &tascam->midi_in_lock);
+ schedule_work(&tascam->midi_in_work);
+ }
+
+ usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->midi_in_anchor);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(tascam->card->dev,
+ "Failed to resubmit MIDI IN URB: error %d\n", ret);
+ usb_unanchor_urb(urb);
+ goto out;
+ }
+
+out:
+ usb_put_urb(urb);
+}
+
+/**
+ * tascam_midi_in_open() - Opens the MIDI input substream.
+ * @substream: The ALSA rawmidi substream to open.
+ *
+ * This function stores a reference to the MIDI input substream in the
+ * driver's private data.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_midi_in_open(struct snd_rawmidi_substream *substream)
+{
+ struct tascam_card *tascam = substream->rmidi->private_data;
+
+ tascam->midi_in_substream = substream;
+ return 0;
+}
+
+/**
+ * tascam_midi_in_close() - Closes the MIDI input substream.
+ * @substream: The ALSA rawmidi substream to close.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_midi_in_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+/**
+ * tascam_midi_in_trigger() - Triggers MIDI input stream activity.
+ * @substream: The ALSA rawmidi substream.
+ * @up: Boolean indicating whether to start (1) or stop (0) the stream.
+ *
+ * This function starts or stops the MIDI input URBs based on the 'up'
+ * parameter. When starting, it resets the kfifo and submits all MIDI input
+ * URBs. When stopping, it kills all anchored MIDI input URBs and cancels the
+ * associated workqueue.
+ */
+static void tascam_midi_in_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct tascam_card *tascam = substream->rmidi->private_data;
+ int i, err;
+
+ if (up) {
+ if (atomic_xchg(&tascam->midi_in_active, 1) == 0) {
+ scoped_guard(spinlock_irqsave, &tascam->midi_in_lock)
+ {
+ kfifo_reset(&tascam->midi_in_fifo);
+ }
+
+ for (i = 0; i < NUM_MIDI_IN_URBS; i++) {
+ usb_get_urb(tascam->midi_in_urbs[i]);
+ usb_anchor_urb(tascam->midi_in_urbs[i],
+ &tascam->midi_in_anchor);
+ err = usb_submit_urb(tascam->midi_in_urbs[i],
+ GFP_KERNEL);
+ if (err < 0) {
+ dev_err(tascam->card->dev,
+ "Failed to submit MIDI IN URB %d: %d\n",
+ i, err);
+ usb_unanchor_urb(
+ tascam->midi_in_urbs[i]);
+ usb_put_urb(tascam->midi_in_urbs[i]);
+ }
+ }
+ }
+ } else {
+ if (atomic_xchg(&tascam->midi_in_active, 0) == 1) {
+ usb_kill_anchored_urbs(&tascam->midi_in_anchor);
+ cancel_work_sync(&tascam->midi_in_work);
+ }
+ }
+}
+
+/**
+ * tascam_midi_in_ops - ALSA rawmidi operations for MIDI input.
+ *
+ * This structure defines the callback functions for MIDI input stream
+ * operations, including open, close, and trigger.
+ */
+static const struct snd_rawmidi_ops tascam_midi_in_ops = {
+ .open = tascam_midi_in_open,
+ .close = tascam_midi_in_close,
+ .trigger = tascam_midi_in_trigger,
+};
+
+void tascam_midi_out_urb_complete(struct urb *urb)
+{
+ struct tascam_card *tascam = urb->context;
+ int i, urb_index = -1;
+
+ if (urb->status) {
+ if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
+ urb->status != -ESHUTDOWN) {
+ dev_err_ratelimited(tascam->card->dev,
+ "MIDI OUT URB failed: %d\n",
+ urb->status);
+ }
+ goto out;
+ }
+
+ if (!tascam)
+ goto out;
+
+ for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
+ if (tascam->midi_out_urbs[i] == urb) {
+ urb_index = i;
+ break;
+ }
+ }
+
+ if (urb_index < 0) {
+ dev_err_ratelimited(tascam->card->dev,
+ "Unknown MIDI OUT URB completed!\n");
+ goto out;
+ }
+
+ scoped_guard(spinlock_irqsave, &tascam->midi_out_lock)
+ {
+ clear_bit(urb_index, &tascam->midi_out_urbs_in_flight);
+ }
+
+ if (atomic_read(&tascam->midi_out_active))
+ schedule_work(&tascam->midi_out_work);
+
+out:
+ usb_put_urb(urb);
+}
+
+/**
+ * tascam_midi_out_work_handler() - Deferred work for sending MIDI data
+ * @work: The work_struct instance.
+ *
+ * This function handles the proprietary output protocol: take the raw MIDI
+ * message bytes from the application, place them at the start of a 9-byte
+ * buffer, pad the rest with 0xFD, and add a terminator byte (0x00).
+ * This function pulls as many bytes as will fit into one packet from the
+ * ALSA buffer and sends them.
+ */
+static void tascam_midi_out_work_handler(struct work_struct *work)
+{
+ struct tascam_card *tascam =
+ container_of(work, struct tascam_card, midi_out_work);
+ struct snd_rawmidi_substream *substream = tascam->midi_out_substream;
+ int i;
+
+ if (!substream || !atomic_read(&tascam->midi_out_active))
+ return;
+
+ while (snd_rawmidi_transmit_peek(substream, (u8[]){ 0 }, 1) == 1) {
+ int urb_index;
+ struct urb *urb;
+ u8 *buf;
+ int bytes_to_send;
+
+ scoped_guard(spinlock_irqsave, &tascam->midi_out_lock) {
+ urb_index = -1;
+ for (i = 0; i < NUM_MIDI_OUT_URBS; i++) {
+ if (!test_bit(
+ i,
+ &tascam->midi_out_urbs_in_flight)) {
+ urb_index = i;
+ break;
+ }
+ }
+
+ if (urb_index < 0)
+ return; /* No free URBs, will be rescheduled by
+ * completion handler
+ */
+
+ urb = tascam->midi_out_urbs[urb_index];
+ buf = urb->transfer_buffer;
+ bytes_to_send = snd_rawmidi_transmit(substream, buf, 8);
+
+ if (bytes_to_send <= 0)
+ break; /* No more data */
+
+ if (bytes_to_send < 9)
+ memset(buf + bytes_to_send, 0xfd,
+ 9 - bytes_to_send);
+ buf[8] = 0xe0;
+
+ set_bit(urb_index, &tascam->midi_out_urbs_in_flight);
+ urb->transfer_buffer_length = 9;
+ }
+
+ usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->midi_out_anchor);
+ if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
+ dev_err_ratelimited(
+ tascam->card->dev,
+ "Failed to submit MIDI OUT URB %d\n",
+ urb_index);
+ scoped_guard(spinlock_irqsave, &tascam->midi_out_lock)
+ {
+ clear_bit(urb_index,
+ &tascam->midi_out_urbs_in_flight);
+ }
+ usb_unanchor_urb(urb);
+ usb_put_urb(urb);
+ break; /* Stop on error */
+ }
+ }
+}
+
+/**
+ * tascam_midi_out_open() - Opens the MIDI output substream.
+ * @substream: The ALSA rawmidi substream to open.
+ *
+ * This function stores a reference to the MIDI output substream in the
+ * driver's private data and initializes the MIDI running status.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_midi_out_open(struct snd_rawmidi_substream *substream)
+{
+ struct tascam_card *tascam = substream->rmidi->private_data;
+
+ tascam->midi_out_substream = substream;
+ /* Initialize the running status state for the packet packer. */
+ tascam->midi_running_status = 0;
+ return 0;
+}
+
+/**
+ * tascam_midi_out_close() - Closes the MIDI output substream.
+ * @substream: The ALSA rawmidi substream to close.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_midi_out_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+/**
+ * tascam_midi_out_drain() - Drains the MIDI output stream.
+ * @substream: The ALSA rawmidi substream.
+ *
+ * This function cancels any pending MIDI output work and kills all
+ * anchored MIDI output URBs, ensuring all data is sent or discarded.
+ */
+static void tascam_midi_out_drain(struct snd_rawmidi_substream *substream)
+{
+ struct tascam_card *tascam = substream->rmidi->private_data;
+ bool in_flight = true;
+
+ while (in_flight) {
+ in_flight = false;
+ for (int i = 0; i < NUM_MIDI_OUT_URBS; i++) {
+ if (test_bit(i, &tascam->midi_out_urbs_in_flight)) {
+ in_flight = true;
+ break;
+ }
+ }
+ if (in_flight)
+ schedule_timeout_uninterruptible(1);
+ }
+
+ cancel_work_sync(&tascam->midi_out_work);
+ usb_kill_anchored_urbs(&tascam->midi_out_anchor);
+}
+
+/**
+ * tascam_midi_out_trigger() - Triggers MIDI output stream activity.
+ * @substream: The ALSA rawmidi substream.
+ * @up: Boolean indicating whether to start (1) or stop (0) the stream.
+ *
+ * This function starts or stops the MIDI output workqueue based on the
+ * 'up' parameter.
+ */
+static void tascam_midi_out_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct tascam_card *tascam = substream->rmidi->private_data;
+
+ if (up) {
+ atomic_set(&tascam->midi_out_active, 1);
+ schedule_work(&tascam->midi_out_work);
+ } else {
+ atomic_set(&tascam->midi_out_active, 0);
+ }
+}
+
+/**
+ * tascam_midi_out_ops - ALSA rawmidi operations for MIDI output.
+ *
+ * This structure defines the callback functions for MIDI output stream
+ * operations, including open, close, trigger, and drain.
+ */
+static const struct snd_rawmidi_ops tascam_midi_out_ops = {
+ .open = tascam_midi_out_open,
+ .close = tascam_midi_out_close,
+ .trigger = tascam_midi_out_trigger,
+ .drain = tascam_midi_out_drain,
+};
+
+int tascam_create_midi(struct tascam_card *tascam)
+{
+ int err;
+
+ err = snd_rawmidi_new(tascam->card, "US144MKII MIDI", 0, 1, 1,
+ &tascam->rmidi);
+ if (err < 0)
+ return err;
+
+ strscpy(tascam->rmidi->name, "US144MKII MIDI",
+ sizeof(tascam->rmidi->name));
+ tascam->rmidi->private_data = tascam;
+
+ snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &tascam_midi_in_ops);
+ snd_rawmidi_set_ops(tascam->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &tascam_midi_out_ops);
+
+ tascam->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ INIT_WORK(&tascam->midi_in_work, tascam_midi_in_work_handler);
+ INIT_WORK(&tascam->midi_out_work, tascam_midi_out_work_handler);
+
+ return 0;
+}
diff --git a/sound/usb/usx2y/us144mkii_pcm.c b/sound/usb/usx2y/us144mkii_pcm.c
new file mode 100644
index 000000000000..0c84304d4624
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii_pcm.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#include "us144mkii.h"
+
+/**
+ * fpo_init_pattern() - Generates a packet distribution pattern.
+ * @size: The number of elements in the pattern array (e.g., 8).
+ * @pattern_array: Pointer to the array to be populated.
+ * @initial_value: The base value to initialize each element with.
+ * @target_sum: The desired sum of all elements in the final array.
+ *
+ * This function initializes an array with a base value and then iteratively
+ * adjusts the elements to match a target sum, distributing the difference
+ * as evenly as possible.
+ */
+static void fpo_init_pattern(unsigned int size, unsigned int *pattern_array,
+ unsigned int initial_value, int target_sum)
+{
+ int diff, i;
+
+ if (!size)
+ return;
+
+ for (i = 0; i < size; ++i)
+ pattern_array[i] = initial_value;
+
+ diff = target_sum - (size * initial_value);
+ for (i = 0; i < abs(diff); ++i) {
+ if (diff > 0)
+ pattern_array[i]++;
+ else
+ pattern_array[i]--;
+ }
+}
+
+const struct snd_pcm_hardware tascam_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .channels_min = NUM_CHANNELS,
+ .channels_max = NUM_CHANNELS,
+ .buffer_bytes_max = 1024 * 1024,
+ .period_bytes_min = 48 * BYTES_PER_FRAME,
+ .period_bytes_max = 1024 * BYTES_PER_FRAME,
+ .periods_min = 2,
+ .periods_max = 1024,
+};
+
+void process_playback_routing_us144mkii(struct tascam_card *tascam,
+ const u8 *src_buffer, u8 *dst_buffer,
+ size_t frames)
+{
+ size_t f;
+ const u8 *src_12, *src_34;
+ u8 *dst_line, *dst_digital;
+
+ for (f = 0; f < frames; ++f) {
+ src_12 = src_buffer + f * BYTES_PER_FRAME;
+ src_34 = src_12 + (2 * BYTES_PER_SAMPLE);
+ dst_line = dst_buffer + f * BYTES_PER_FRAME;
+ dst_digital = dst_line + (2 * BYTES_PER_SAMPLE);
+
+ /* LINE OUTPUTS (ch1/2 on device) */
+ if (tascam->line_out_source == 0) /* "ch1 and ch2" */
+ memcpy(dst_line, src_12, 2 * BYTES_PER_SAMPLE);
+ else /* "ch3 and ch4" */
+ memcpy(dst_line, src_34, 2 * BYTES_PER_SAMPLE);
+
+ /* DIGITAL OUTPUTS (ch3/4 on device) */
+ if (tascam->digital_out_source == 0) /* "ch1 and ch2" */
+ memcpy(dst_digital, src_12, 2 * BYTES_PER_SAMPLE);
+ else /* "ch3 and ch4" */
+ memcpy(dst_digital, src_34, 2 * BYTES_PER_SAMPLE);
+ }
+}
+
+void process_capture_routing_us144mkii(struct tascam_card *tascam,
+ const s32 *decoded_block,
+ s32 *routed_block)
+{
+ int f;
+ const s32 *src_frame;
+ s32 *dst_frame;
+
+ for (f = 0; f < FRAMES_PER_DECODE_BLOCK; f++) {
+ src_frame = decoded_block + (f * DECODED_CHANNELS_PER_FRAME);
+ dst_frame = routed_block + (f * DECODED_CHANNELS_PER_FRAME);
+
+ /* ch1 and ch2 Source */
+ if (tascam->capture_12_source == 0) { /* analog inputs */
+ dst_frame[0] = src_frame[0]; /* Analog L */
+ dst_frame[1] = src_frame[1]; /* Analog R */
+ } else { /* digital inputs */
+ dst_frame[0] = src_frame[2]; /* Digital L */
+ dst_frame[1] = src_frame[3]; /* Digital R */
+ }
+
+ /* ch3 and ch4 Source */
+ if (tascam->capture_34_source == 0) { /* analog inputs */
+ dst_frame[2] = src_frame[0]; /* Analog L (Duplicate) */
+ dst_frame[3] = src_frame[1]; /* Analog R (Duplicate) */
+ } else { /* digital inputs */
+ dst_frame[2] = src_frame[2]; /* Digital L */
+ dst_frame[3] = src_frame[3]; /* Digital R */
+ }
+ }
+}
+
+int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate)
+{
+ struct usb_device *dev = tascam->dev;
+ u8 *rate_payload_buf __free(kfree) = NULL;
+ u16 rate_vendor_wValue;
+ int err = 0;
+ const u8 *current_payload_src;
+
+ static const u8 payload_44100[] = { 0x44, 0xac, 0x00 };
+ static const u8 payload_48000[] = { 0x80, 0xbb, 0x00 };
+ static const u8 payload_88200[] = { 0x88, 0x58, 0x01 };
+ static const u8 payload_96000[] = { 0x00, 0x77, 0x01 };
+
+ switch (rate) {
+ case 44100:
+ current_payload_src = payload_44100;
+ rate_vendor_wValue = REG_ADDR_RATE_44100;
+ break;
+ case 48000:
+ current_payload_src = payload_48000;
+ rate_vendor_wValue = REG_ADDR_RATE_48000;
+ break;
+ case 88200:
+ current_payload_src = payload_88200;
+ rate_vendor_wValue = REG_ADDR_RATE_88200;
+ break;
+ case 96000:
+ current_payload_src = payload_96000;
+ rate_vendor_wValue = REG_ADDR_RATE_96000;
+ break;
+ default:
+ dev_err(&dev->dev,
+ "Unsupported sample rate %d for configuration\n", rate);
+ return -EINVAL;
+ }
+
+ rate_payload_buf = kmemdup(current_payload_src, 3, GFP_KERNEL);
+ if (!rate_payload_buf)
+ return -ENOMEM;
+
+ dev_info(&dev->dev, "Configuring device for %d Hz\n", rate);
+
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
+ MODE_VAL_CONFIG, 0x0000, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+ RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
+ EP_AUDIO_IN, rate_payload_buf, 3,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+ RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL,
+ EP_AUDIO_OUT, rate_payload_buf, 3,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
+ REG_ADDR_UNKNOWN_0D, REG_VAL_ENABLE, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
+ REG_ADDR_UNKNOWN_0E, REG_VAL_ENABLE, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
+ REG_ADDR_UNKNOWN_0F, REG_VAL_ENABLE, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
+ rate_vendor_wValue, REG_VAL_ENABLE, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_REGISTER_WRITE, RT_H2D_VENDOR_DEV,
+ REG_ADDR_UNKNOWN_11, REG_VAL_ENABLE, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV,
+ MODE_VAL_STREAM_START, 0x0000, NULL, 0,
+ USB_CTRL_TIMEOUT_MS);
+ if (err < 0)
+ goto fail;
+ return 0;
+
+fail:
+ dev_err(&dev->dev,
+ "Device configuration failed at rate %d with error %d\n", rate,
+ err);
+ return err;
+}
+
+int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+ int err;
+ unsigned int rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tascam->fpo.sample_rate_khz = rate / 1000;
+ tascam->fpo.base_feedback_value = tascam->fpo.sample_rate_khz;
+ tascam->fpo.feedback_offset = 2;
+ tascam->fpo.current_index = 0;
+ tascam->fpo.previous_index = 0;
+ tascam->fpo.sync_locked = false;
+
+ unsigned int initial_value = tascam->fpo.sample_rate_khz / 8;
+
+ for (int i = 0; i < 5; i++) {
+ int target_sum = tascam->fpo.sample_rate_khz -
+ tascam->fpo.feedback_offset + i;
+ fpo_init_pattern(8, tascam->fpo.full_frame_patterns[i],
+ initial_value, target_sum);
+ }
+ }
+
+ if (tascam->current_rate != rate) {
+ err = us144mkii_configure_device_for_rate(tascam, rate);
+ if (err < 0) {
+ tascam->current_rate = 0;
+ return err;
+ }
+ tascam->current_rate = rate;
+ }
+
+ return 0;
+}
+
+int tascam_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+ int err = 0;
+ int i;
+ bool do_start = false;
+ bool do_stop = false;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (!atomic_read(&tascam->playback_active)) {
+ atomic_set(&tascam->playback_active, 1);
+ atomic_set(&tascam->capture_active, 1);
+ do_start = true;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (atomic_read(&tascam->playback_active)) {
+ atomic_set(&tascam->playback_active, 0);
+ atomic_set(&tascam->capture_active, 0);
+ do_stop = true;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (do_start) {
+ if (atomic_read(&tascam->active_urbs) > 0) {
+ dev_warn(tascam->card->dev,
+ "Cannot start, URBs still active.\n");
+ return -EAGAIN;
+ }
+
+ for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
+ usb_get_urb(tascam->feedback_urbs[i]);
+ usb_anchor_urb(tascam->feedback_urbs[i],
+ &tascam->feedback_anchor);
+ err = usb_submit_urb(tascam->feedback_urbs[i],
+ GFP_ATOMIC);
+ if (err < 0) {
+ usb_unanchor_urb(tascam->feedback_urbs[i]);
+ usb_put_urb(tascam->feedback_urbs[i]);
+ atomic_dec(&tascam->active_urbs);
+ goto start_rollback;
+ }
+ atomic_inc(&tascam->active_urbs);
+ }
+ for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
+ usb_get_urb(tascam->playback_urbs[i]);
+ usb_anchor_urb(tascam->playback_urbs[i],
+ &tascam->playback_anchor);
+ err = usb_submit_urb(tascam->playback_urbs[i],
+ GFP_ATOMIC);
+ if (err < 0) {
+ usb_unanchor_urb(tascam->playback_urbs[i]);
+ usb_put_urb(tascam->playback_urbs[i]);
+ atomic_dec(&tascam->active_urbs);
+ goto start_rollback;
+ }
+ atomic_inc(&tascam->active_urbs);
+ }
+ for (i = 0; i < NUM_CAPTURE_URBS; i++) {
+ usb_get_urb(tascam->capture_urbs[i]);
+ usb_anchor_urb(tascam->capture_urbs[i],
+ &tascam->capture_anchor);
+ err = usb_submit_urb(tascam->capture_urbs[i],
+ GFP_ATOMIC);
+ if (err < 0) {
+ usb_unanchor_urb(tascam->capture_urbs[i]);
+ usb_put_urb(tascam->capture_urbs[i]);
+ atomic_dec(&tascam->active_urbs);
+ goto start_rollback;
+ }
+ atomic_inc(&tascam->active_urbs);
+ }
+
+ return 0;
+start_rollback:
+ dev_err(tascam->card->dev,
+ "Failed to submit URBs to start stream: %d\n", err);
+ do_stop = true;
+ }
+
+ if (do_stop)
+ schedule_work(&tascam->stop_work);
+
+ return err;
+}
+
+int tascam_init_pcm(struct snd_pcm *pcm)
+{
+ struct tascam_card *tascam = pcm->private_data;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &tascam_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tascam_capture_ops);
+
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ tascam->dev->dev.parent, 64 * 1024,
+ tascam_pcm_hw.buffer_bytes_max);
+
+ return 0;
+}
diff --git a/sound/usb/usx2y/us144mkii_pcm.h b/sound/usb/usx2y/us144mkii_pcm.h
new file mode 100644
index 000000000000..74da8564431b
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii_pcm.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#ifndef __US144MKII_PCM_H
+#define __US144MKII_PCM_H
+
+#include "us144mkii.h"
+
+/**
+ * tascam_pcm_hw - Hardware capabilities for TASCAM US-144MKII PCM.
+ *
+ * Defines the supported PCM formats, rates, channels, and buffer/period sizes
+ * for the TASCAM US-144MKII audio interface.
+ */
+extern const struct snd_pcm_hardware tascam_pcm_hw;
+
+/**
+ * tascam_playback_ops - ALSA PCM operations for playback.
+ *
+ * This structure defines the callback functions for playback stream operations.
+ */
+extern const struct snd_pcm_ops tascam_playback_ops;
+
+/**
+ * tascam_capture_ops - ALSA PCM operations for capture.
+ *
+ * This structure defines the callback functions for capture stream operations.
+ */
+extern const struct snd_pcm_ops tascam_capture_ops;
+
+/**
+ * playback_urb_complete() - Completion handler for playback isochronous URBs.
+ * @urb: the completed URB
+ *
+ * This function runs in interrupt context. It calculates the number of bytes
+ * to send in the next set of packets based on the feedback-driven clock,
+ * copies the audio data from the ALSA ring buffer, and resubmits the URB.
+ */
+void playback_urb_complete(struct urb *urb);
+
+/**
+ * feedback_urb_complete() - Completion handler for feedback isochronous URBs.
+ * @urb: the completed URB
+ *
+ * This is the master clock for the driver. It runs in interrupt context.
+ * It reads the feedback value from the device, which indicates how many
+ * samples the device has consumed. This information is used to adjust the
+ * playback rate and to advance the capture stream pointer, keeping both
+ * streams in sync. It then calls snd_pcm_period_elapsed if necessary and
+ * resubmits itself.
+ */
+void feedback_urb_complete(struct urb *urb);
+
+/**
+ * capture_urb_complete() - Completion handler for capture bulk URBs.
+ * @urb: the completed URB
+ *
+ * This function runs in interrupt context. It copies the received raw data
+ * into an intermediate ring buffer and then schedules the workqueue to process
+ * it. It then resubmits the URB to receive more data.
+ */
+void capture_urb_complete(struct urb *urb);
+
+/**
+ * tascam_stop_pcm_work_handler() - Work handler to stop PCM streams.
+ * @work: Pointer to the work_struct.
+ *
+ * This function is scheduled to stop PCM streams (playback and capture)
+ * from a workqueue context, avoiding blocking operations in interrupt context.
+ */
+void tascam_stop_pcm_work_handler(struct work_struct *work);
+
+/**
+ * tascam_init_pcm() - Initializes the ALSA PCM device.
+ * @pcm: Pointer to the ALSA PCM device to initialize.
+ *
+ * This function sets up the PCM operations, adds ALSA controls for routing
+ * and sample rate, and preallocates pages for the PCM buffer.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int tascam_init_pcm(struct snd_pcm *pcm);
+
+/**
+ * us144mkii_configure_device_for_rate() - Set sample rate via USB control msgs
+ * @tascam: the tascam_card instance
+ * @rate: the target sample rate (e.g., 44100, 96000)
+ *
+ * This function sends a sequence of vendor-specific and UAC control messages
+ * to configure the device hardware for the specified sample rate.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate);
+
+/**
+ * process_playback_routing_us144mkii() - Apply playback routing matrix
+ * @tascam: The driver instance.
+ * @src_buffer: Buffer containing 4 channels of S24_3LE audio from ALSA.
+ * @dst_buffer: Buffer to be filled for the USB device.
+ * @frames: Number of frames to process.
+ */
+void process_playback_routing_us144mkii(struct tascam_card *tascam,
+ const u8 *src_buffer, u8 *dst_buffer,
+ size_t frames);
+
+/**
+ * process_capture_routing_us144mkii() - Apply capture routing matrix
+ * @tascam: The driver instance.
+ * @decoded_block: Buffer containing 4 channels of S32LE decoded audio.
+ * @routed_block: Buffer to be filled for ALSA.
+ */
+void process_capture_routing_us144mkii(struct tascam_card *tascam,
+ const s32 *decoded_block,
+ s32 *routed_block);
+
+/**
+ * tascam_pcm_hw_params() - Configures hardware parameters for PCM streams.
+ * @substream: The ALSA PCM substream.
+ * @params: The hardware parameters to apply.
+ *
+ * This function allocates pages for the PCM buffer and, for playback streams,
+ * selects the appropriate feedback patterns based on the requested sample rate.
+ * It also configures the device hardware for the selected sample rate if it
+ * has changed.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+
+/**
+ * tascam_pcm_hw_free() - Frees hardware parameters for PCM streams.
+ * @substream: The ALSA PCM substream.
+ *
+ * This function is a stub for freeing hardware-related resources.
+ *
+ * Return: 0 on success.
+ */
+int tascam_pcm_hw_free(struct snd_pcm_substream *substream);
+
+/**
+ * tascam_pcm_trigger() - Triggers the start or stop of PCM streams.
+ * @substream: The ALSA PCM substream.
+ * @cmd: The trigger command (e.g., SNDRV_PCM_TRIGGER_START).
+ *
+ * This function handles starting and stopping of playback and capture streams
+ * by submitting or killing the associated URBs.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int tascam_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/**
+ * tascam_capture_work_handler() - Deferred work for processing capture data.
+ * @work: the work_struct instance
+ *
+ * This function runs in a kernel thread context, not an IRQ context. It reads
+ * raw data from the capture ring buffer, decodes it, applies routing, and
+ * copies the final audio data into the ALSA capture ring buffer. This offloads
+ * the CPU-intensive decoding from the time-sensitive URB completion handlers.
+ */
+void tascam_capture_work_handler(struct work_struct *work);
+
+#endif /* __US144MKII_PCM_H */
diff --git a/sound/usb/usx2y/us144mkii_playback.c b/sound/usb/usx2y/us144mkii_playback.c
new file mode 100644
index 000000000000..0cb9699ec211
--- /dev/null
+++ b/sound/usb/usx2y/us144mkii_playback.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com>
+
+#include "us144mkii.h"
+
+/**
+ * tascam_playback_open() - Opens the PCM playback substream.
+ * @substream: The ALSA PCM substream to open.
+ *
+ * This function sets the hardware parameters for the playback substream
+ * and stores a reference to the substream in the driver's private data.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_playback_open(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+
+ substream->runtime->hw = tascam_pcm_hw;
+ tascam->playback_substream = substream;
+ atomic_set(&tascam->playback_active, 0);
+
+ return 0;
+}
+
+/**
+ * tascam_playback_close() - Closes the PCM playback substream.
+ * @substream: The ALSA PCM substream to close.
+ *
+ * This function clears the reference to the playback substream in the
+ * driver's private data.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_playback_close(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+
+ tascam->playback_substream = NULL;
+
+ return 0;
+}
+
+/**
+ * tascam_playback_prepare() - Prepares the PCM playback substream for use.
+ * @substream: The ALSA PCM substream to prepare.
+ *
+ * This function initializes playback-related counters and flags, and configures
+ * the playback URBs with appropriate packet sizes based on the nominal frame
+ * rate.
+ *
+ * Return: 0 on success.
+ */
+static int tascam_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int i, u;
+ size_t nominal_frames_per_packet, nominal_bytes_per_packet;
+ size_t total_bytes_in_urb;
+
+ tascam->driver_playback_pos = 0;
+ tascam->playback_frames_consumed = 0;
+ tascam->last_period_pos = 0;
+ tascam->feedback_pattern_in_idx = 0;
+ tascam->feedback_pattern_out_idx = 0;
+ tascam->feedback_synced = false;
+ tascam->feedback_consecutive_errors = 0;
+ tascam->feedback_urb_skip_count = NUM_FEEDBACK_URBS;
+
+ nominal_frames_per_packet = runtime->rate / 8000;
+ for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++)
+ tascam->feedback_accumulator_pattern[i] =
+ nominal_frames_per_packet;
+
+ for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
+ struct urb *f_urb = tascam->feedback_urbs[i];
+ int j;
+
+ f_urb->number_of_packets = FEEDBACK_URB_PACKETS;
+ f_urb->transfer_buffer_length =
+ FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE;
+ for (j = 0; j < FEEDBACK_URB_PACKETS; j++) {
+ f_urb->iso_frame_desc[j].offset =
+ j * FEEDBACK_PACKET_SIZE;
+ f_urb->iso_frame_desc[j].length = FEEDBACK_PACKET_SIZE;
+ }
+ }
+
+ nominal_bytes_per_packet = nominal_frames_per_packet * BYTES_PER_FRAME;
+ total_bytes_in_urb = nominal_bytes_per_packet * PLAYBACK_URB_PACKETS;
+
+ for (u = 0; u < NUM_PLAYBACK_URBS; u++) {
+ struct urb *urb = tascam->playback_urbs[u];
+
+ memset(urb->transfer_buffer, 0,
+ tascam->playback_urb_alloc_size);
+ urb->transfer_buffer_length = total_bytes_in_urb;
+ urb->number_of_packets = PLAYBACK_URB_PACKETS;
+ for (i = 0; i < PLAYBACK_URB_PACKETS; i++) {
+ urb->iso_frame_desc[i].offset =
+ i * nominal_bytes_per_packet;
+ urb->iso_frame_desc[i].length =
+ nominal_bytes_per_packet;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * tascam_playback_pointer() - Returns the current playback pointer position.
+ * @substream: The ALSA PCM substream.
+ *
+ * This function returns the current position of the playback pointer within
+ * the ALSA ring buffer, in frames.
+ *
+ * Return: The current playback pointer position in frames.
+ */
+static snd_pcm_uframes_t
+tascam_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct tascam_card *tascam = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u64 pos;
+
+ if (!atomic_read(&tascam->playback_active))
+ return 0;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ pos = tascam->playback_frames_consumed;
+ }
+
+ if (runtime->buffer_size == 0)
+ return 0;
+
+ return do_div(pos, runtime->buffer_size);
+}
+
+/**
+ * tascam_playback_ops - ALSA PCM operations for playback.
+ *
+ * This structure defines the callback functions for playback stream operations,
+ * including open, close, ioctl, hardware parameters, hardware free, prepare,
+ * trigger, and pointer.
+ */
+const struct snd_pcm_ops tascam_playback_ops = {
+ .open = tascam_playback_open,
+ .close = tascam_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = tascam_pcm_hw_params,
+ .hw_free = tascam_pcm_hw_free,
+ .prepare = tascam_playback_prepare,
+ .trigger = tascam_pcm_trigger,
+ .pointer = tascam_playback_pointer,
+};
+
+void playback_urb_complete(struct urb *urb)
+{
+ struct tascam_card *tascam = urb->context;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ size_t total_bytes_for_urb = 0;
+ snd_pcm_uframes_t offset_frames;
+ snd_pcm_uframes_t frames_to_copy;
+ int ret, i;
+
+ if (urb->status) {
+ if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
+ urb->status != -ESHUTDOWN && urb->status != -ENODEV)
+ dev_err_ratelimited(tascam->card->dev,
+ "Playback URB failed: %d\n",
+ urb->status);
+ goto out;
+ }
+ if (!tascam || !atomic_read(&tascam->playback_active))
+ goto out;
+
+ substream = tascam->playback_substream;
+ if (!substream || !substream->runtime)
+ goto out;
+ runtime = substream->runtime;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int frames_for_packet;
+ size_t bytes_for_packet;
+
+ if (tascam->feedback_synced) {
+ frames_for_packet =
+ tascam->feedback_accumulator_pattern
+ [tascam->feedback_pattern_out_idx];
+ tascam->feedback_pattern_out_idx =
+ (tascam->feedback_pattern_out_idx + 1) %
+ FEEDBACK_ACCUMULATOR_SIZE;
+ } else {
+ frames_for_packet = runtime->rate / 8000;
+ }
+ bytes_for_packet = frames_for_packet * BYTES_PER_FRAME;
+
+ urb->iso_frame_desc[i].offset = total_bytes_for_urb;
+ urb->iso_frame_desc[i].length = bytes_for_packet;
+ total_bytes_for_urb += bytes_for_packet;
+ }
+ urb->transfer_buffer_length = total_bytes_for_urb;
+
+ offset_frames = tascam->driver_playback_pos;
+ frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
+ tascam->driver_playback_pos =
+ (offset_frames + frames_to_copy) % runtime->buffer_size;
+ }
+
+ if (total_bytes_for_urb > 0) {
+ u8 *dst_buf = urb->transfer_buffer;
+
+ /* Handle ring buffer wrap-around */
+ if (offset_frames + frames_to_copy > runtime->buffer_size) {
+ size_t first_chunk_bytes = frames_to_bytes(
+ runtime, runtime->buffer_size - offset_frames);
+ size_t second_chunk_bytes =
+ total_bytes_for_urb - first_chunk_bytes;
+
+ memcpy(dst_buf,
+ runtime->dma_area +
+ frames_to_bytes(runtime, offset_frames),
+ first_chunk_bytes);
+ memcpy(dst_buf + first_chunk_bytes, runtime->dma_area,
+ second_chunk_bytes);
+ } else {
+ memcpy(dst_buf,
+ runtime->dma_area +
+ frames_to_bytes(runtime, offset_frames),
+ total_bytes_for_urb);
+ }
+
+ process_playback_routing_us144mkii(tascam, dst_buf, dst_buf,
+ frames_to_copy);
+ }
+
+ urb->dev = tascam->dev;
+ usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->playback_anchor);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err_ratelimited(tascam->card->dev,
+ "Failed to resubmit playback URB: %d\n",
+ ret);
+ usb_unanchor_urb(urb);
+ usb_put_urb(urb);
+ atomic_dec(
+ &tascam->active_urbs); /* Decrement on failed resubmission */
+ }
+out:
+ usb_put_urb(urb);
+}
+
+void feedback_urb_complete(struct urb *urb)
+{
+ struct tascam_card *tascam = urb->context;
+ struct snd_pcm_substream *playback_ss, *capture_ss;
+ struct snd_pcm_runtime *playback_rt, *capture_rt;
+ u64 total_frames_in_urb = 0;
+ int ret, p;
+ unsigned int old_in_idx, new_in_idx;
+ bool playback_period_elapsed = false;
+ bool capture_period_elapsed = false;
+
+ if (urb->status) {
+ if (urb->status != -ENOENT && urb->status != -ECONNRESET &&
+ urb->status != -ESHUTDOWN && urb->status != -ENODEV) {
+ dev_err_ratelimited(tascam->card->dev,
+ "Feedback URB failed: %d\n",
+ urb->status);
+ atomic_dec(
+ &tascam->active_urbs); /* Decrement on failed resubmission */
+ }
+ goto out;
+ }
+ if (!tascam || !atomic_read(&tascam->playback_active))
+ goto out;
+
+ playback_ss = tascam->playback_substream;
+ if (!playback_ss || !playback_ss->runtime)
+ goto out;
+ playback_rt = playback_ss->runtime;
+
+ capture_ss = tascam->capture_substream;
+ capture_rt = capture_ss ? capture_ss->runtime : NULL;
+
+ scoped_guard(spinlock_irqsave, &tascam->lock) {
+ if (tascam->feedback_urb_skip_count > 0) {
+ tascam->feedback_urb_skip_count--;
+ break;
+ }
+
+ old_in_idx = tascam->feedback_pattern_in_idx;
+
+ for (p = 0; p < urb->number_of_packets; p++) {
+ u8 feedback_value = 0;
+ const unsigned int *pattern;
+ bool packet_ok =
+ (urb->iso_frame_desc[p].status == 0 &&
+ urb->iso_frame_desc[p].actual_length >= 1);
+
+ if (packet_ok)
+ feedback_value =
+ *((u8 *)urb->transfer_buffer +
+ urb->iso_frame_desc[p].offset);
+
+ if (packet_ok) {
+ int delta = feedback_value -
+ tascam->fpo.base_feedback_value +
+ tascam->fpo.feedback_offset;
+ int pattern_idx;
+
+ if (delta < 0) {
+ pattern_idx =
+ 0; // Clamp to the lowest pattern
+ } else if (delta >= 5) {
+ pattern_idx =
+ 4; // Clamp to the highest pattern
+ } else {
+ pattern_idx = delta;
+ }
+
+ pattern =
+ tascam->fpo
+ .full_frame_patterns[pattern_idx];
+ tascam->feedback_consecutive_errors = 0;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ unsigned int in_idx =
+ (tascam->feedback_pattern_in_idx +
+ i) %
+ FEEDBACK_ACCUMULATOR_SIZE;
+
+ tascam->feedback_accumulator_pattern
+ [in_idx] = pattern[i];
+ total_frames_in_urb += pattern[i];
+ }
+ } else {
+ unsigned int nominal_frames =
+ playback_rt->rate / 8000;
+ int i;
+
+ if (tascam->feedback_synced) {
+ tascam->feedback_consecutive_errors++;
+ if (tascam->feedback_consecutive_errors >
+ FEEDBACK_SYNC_LOSS_THRESHOLD) {
+ dev_err(tascam->card->dev,
+ "Fatal: Feedback sync lost. Stopping stream.\n");
+ schedule_work(
+ &tascam->stop_pcm_work);
+ tascam->feedback_synced = false;
+ break;
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ unsigned int in_idx =
+ (tascam->feedback_pattern_in_idx +
+ i) %
+ FEEDBACK_ACCUMULATOR_SIZE;
+
+ tascam->feedback_accumulator_pattern
+ [in_idx] = nominal_frames;
+ total_frames_in_urb += nominal_frames;
+ }
+ }
+ tascam->feedback_pattern_in_idx =
+ (tascam->feedback_pattern_in_idx + 8) %
+ FEEDBACK_ACCUMULATOR_SIZE;
+ }
+
+ new_in_idx = tascam->feedback_pattern_in_idx;
+
+ if (!tascam->feedback_synced) {
+ unsigned int out_idx = tascam->feedback_pattern_out_idx;
+ bool is_ahead = (new_in_idx - out_idx) %
+ FEEDBACK_ACCUMULATOR_SIZE <
+ (FEEDBACK_ACCUMULATOR_SIZE / 2);
+ bool was_behind = (old_in_idx - out_idx) %
+ FEEDBACK_ACCUMULATOR_SIZE >=
+ (FEEDBACK_ACCUMULATOR_SIZE / 2);
+
+ if (is_ahead && was_behind) {
+ dev_dbg(tascam->card->dev,
+ "Sync Acquired! (in: %u, out: %u)\n",
+ new_in_idx, out_idx);
+ tascam->feedback_synced = true;
+ tascam->feedback_consecutive_errors = 0;
+ }
+ }
+
+ if (total_frames_in_urb > 0) {
+ tascam->playback_frames_consumed += total_frames_in_urb;
+ if (atomic_read(&tascam->capture_active))
+ tascam->capture_frames_processed +=
+ total_frames_in_urb;
+ }
+
+ if (playback_rt->period_size > 0) {
+ u64 current_period =
+ div_u64(tascam->playback_frames_consumed,
+ playback_rt->period_size);
+
+ if (current_period > tascam->last_period_pos) {
+ tascam->last_period_pos = current_period;
+ playback_period_elapsed = true;
+ }
+ }
+
+ if (atomic_read(&tascam->capture_active) && capture_rt &&
+ capture_rt->period_size > 0) {
+ u64 current_capture_period =
+ div_u64(tascam->capture_frames_processed,
+ capture_rt->period_size);
+
+ if (current_capture_period >
+ tascam->last_capture_period_pos) {
+ tascam->last_capture_period_pos =
+ current_capture_period;
+ capture_period_elapsed = true;
+ }
+ }
+ }
+ if (playback_period_elapsed)
+ snd_pcm_period_elapsed(playback_ss);
+ if (capture_period_elapsed)
+ snd_pcm_period_elapsed(capture_ss);
+
+ urb->dev = tascam->dev;
+ usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->feedback_anchor);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err_ratelimited(tascam->card->dev,
+ "Failed to resubmit feedback URB: %d\n",
+ ret);
+ usb_unanchor_urb(urb);
+ usb_put_urb(urb);
+ }
+out:
+ usb_put_urb(urb);
+}
+
+void tascam_stop_pcm_work_handler(struct work_struct *work)
+{
+ struct tascam_card *tascam =
+ container_of(work, struct tascam_card, stop_pcm_work);
+
+ if (tascam->playback_substream)
+ snd_pcm_stop(tascam->playback_substream, SNDRV_PCM_STATE_XRUN);
+ if (tascam->capture_substream)
+ snd_pcm_stop(tascam->capture_substream, SNDRV_PCM_STATE_XRUN);
+}
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index acca8bead82e..c7c7ec9c228b 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -751,7 +751,6 @@ static int usx2y_format_set(struct usx2ydev *usx2y, snd_pcm_format_t format)
static int snd_usx2y_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- int err = 0;
unsigned int rate = params_rate(hw_params);
snd_pcm_format_t format = params_format(hw_params);
struct snd_card *card = substream->pstr->pcm->card;
@@ -760,7 +759,7 @@ static int snd_usx2y_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_substream *test_substream;
int i;
- mutex_lock(&usx2y(card)->pcm_mutex);
+ guard(mutex)(&usx2y(card)->pcm_mutex);
dev_dbg(&dev->dev->dev, "%s(%p, %p)\n", __func__, substream, hw_params);
/* all pcm substreams off one usx2y have to operate at the same
* rate & format
@@ -777,14 +776,11 @@ static int snd_usx2y_pcm_hw_params(struct snd_pcm_substream *substream,
test_substream->runtime->format != format) ||
(test_substream->runtime->rate &&
test_substream->runtime->rate != rate)) {
- err = -EINVAL;
- goto error;
+ return -EINVAL;
}
}
- error:
- mutex_unlock(&usx2y(card)->pcm_mutex);
- return err;
+ return 0;
}
/*
@@ -796,7 +792,7 @@ static int snd_usx2y_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_usx2y_substream *subs = runtime->private_data;
struct snd_usx2y_substream *cap_subs, *playback_subs;
- mutex_lock(&subs->usx2y->pcm_mutex);
+ guard(mutex)(&subs->usx2y->pcm_mutex);
dev_dbg(&subs->usx2y->dev->dev, "%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -816,7 +812,6 @@ static int snd_usx2y_pcm_hw_free(struct snd_pcm_substream *substream)
usx2y_urbs_release(subs);
}
}
- mutex_unlock(&subs->usx2y->pcm_mutex);
return 0;
}
@@ -835,7 +830,7 @@ static int snd_usx2y_pcm_prepare(struct snd_pcm_substream *substream)
dev_dbg(&usx2y->dev->dev, "%s(%p)\n", __func__, substream);
- mutex_lock(&usx2y->pcm_mutex);
+ guard(mutex)(&usx2y->pcm_mutex);
usx2y_subs_prepare(subs);
// Start hardware streams
// SyncStream first....
@@ -843,25 +838,23 @@ static int snd_usx2y_pcm_prepare(struct snd_pcm_substream *substream)
if (usx2y->format != runtime->format) {
err = usx2y_format_set(usx2y, runtime->format);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
if (usx2y->rate != runtime->rate) {
err = usx2y_rate_set(usx2y, runtime->rate);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
dev_dbg(&usx2y->dev->dev, "%s: starting capture pipe for %s\n",
__func__, subs == capsubs ? "self" : "playpipe");
err = usx2y_urbs_start(capsubs);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
if (subs != capsubs && atomic_read(&subs->state) < STATE_PREPARED)
err = usx2y_urbs_start(subs);
- up_prepare_mutex:
- mutex_unlock(&usx2y->pcm_mutex);
return err;
}
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 1b1496adb47e..7c90214485d9 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -365,7 +365,7 @@ static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream)
struct snd_usx2y_substream *playback_subs;
struct snd_usx2y_substream *cap_subs2;
- mutex_lock(&subs->usx2y->pcm_mutex);
+ guard(mutex)(&subs->usx2y->pcm_mutex);
dev_dbg(&subs->usx2y->dev->dev, "%s(%p)\n", __func__, substream);
cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
@@ -394,7 +394,6 @@ static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream)
usx2y_usbpcm_urbs_release(cap_subs2);
}
}
- mutex_unlock(&subs->usx2y->pcm_mutex);
return 0;
}
@@ -504,15 +503,13 @@ static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
dev_dbg(&usx2y->dev->dev, "snd_usx2y_pcm_prepare(%p)\n", substream);
- mutex_lock(&usx2y->pcm_mutex);
+ guard(mutex)(&usx2y->pcm_mutex);
if (!usx2y->hwdep_pcm_shm) {
usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES,
GFP_KERNEL);
- if (!usx2y->hwdep_pcm_shm) {
- err = -ENOMEM;
- goto up_prepare_mutex;
- }
+ if (!usx2y->hwdep_pcm_shm)
+ return -ENOMEM;
memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES);
}
@@ -523,19 +520,19 @@ static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
if (usx2y->format != runtime->format) {
err = usx2y_format_set(usx2y, runtime->format);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
if (usx2y->rate != runtime->rate) {
err = usx2y_rate_set(usx2y, runtime->rate);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
dev_dbg(&usx2y->dev->dev,
"starting capture pipe for %s\n", subs == capsubs ?
"self" : "playpipe");
err = usx2y_usbpcm_urbs_start(capsubs);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
if (subs != capsubs) {
@@ -547,14 +544,12 @@ static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
"Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
usx2y_iso_frames_per_buffer(runtime, usx2y),
usx2y->hwdep_pcm_shm->captured_iso_frames);
- if (msleep_interruptible(10)) {
- err = -ERESTARTSYS;
- goto up_prepare_mutex;
- }
+ if (msleep_interruptible(10))
+ return -ERESTARTSYS;
}
err = usx2y_usbpcm_urbs_start(subs);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
dev_dbg(&usx2y->dev->dev,
"Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
@@ -564,8 +559,6 @@ static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
usx2y->hwdep_pcm_shm->capture_iso_start = -1;
}
- up_prepare_mutex:
- mutex_unlock(&usx2y->pcm_mutex);
return err;
}
@@ -646,11 +639,10 @@ static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
struct snd_card *card = hw->card;
int err;
- mutex_lock(&usx2y(card)->pcm_mutex);
+ guard(mutex)(&usx2y(card)->pcm_mutex);
err = usx2y_pcms_busy_check(card);
if (!err)
usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
- mutex_unlock(&usx2y(card)->pcm_mutex);
return err;
}
@@ -659,11 +651,10 @@ static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
struct snd_card *card = hw->card;
int err;
- mutex_lock(&usx2y(card)->pcm_mutex);
+ guard(mutex)(&usx2y(card)->pcm_mutex);
err = usx2y_pcms_busy_check(card);
if (!err)
usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
- mutex_unlock(&usx2y(card)->pcm_mutex);
return err;
}
diff --git a/sound/usb/validate.c b/sound/usb/validate.c
index a0d55b77c994..4bb4893f6e74 100644
--- a/sound/usb/validate.c
+++ b/sound/usb/validate.c
@@ -266,7 +266,11 @@ static const struct usb_desc_validator audio_validators[] = {
FUNC(UAC_VERSION_2, UAC_MIXER_UNIT, validate_mixer_unit),
FUNC(UAC_VERSION_2, UAC_SELECTOR_UNIT, validate_selector_unit),
FUNC(UAC_VERSION_2, UAC_FEATURE_UNIT, validate_uac2_feature_unit),
- /* UAC_VERSION_2, UAC2_EFFECT_UNIT: not implemented yet */
+ /* just a stop-gap, it should be a proper function for the array
+ * once if the unit is really parsed/used
+ */
+ FIXED(UAC_VERSION_2, UAC2_EFFECT_UNIT,
+ struct uac2_effect_unit_descriptor),
FUNC(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2, validate_processing_unit),
FUNC(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2, validate_processing_unit),
FIXED(UAC_VERSION_2, UAC2_CLOCK_SOURCE,
@@ -286,7 +290,8 @@ static const struct usb_desc_validator audio_validators[] = {
FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit),
FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit),
FUNC(UAC_VERSION_3, UAC3_FEATURE_UNIT, validate_uac3_feature_unit),
- /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */
+ FIXED(UAC_VERSION_3, UAC3_EFFECT_UNIT,
+ struct uac2_effect_unit_descriptor), /* sharing the same struct */
FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit),
FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit),
FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE,