summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/6fire/chip.c54
-rw-r--r--sound/usb/6fire/midi.c23
-rw-r--r--sound/usb/6fire/pcm.c85
-rw-r--r--sound/usb/Kconfig27
-rw-r--r--sound/usb/Makefile3
-rw-r--r--sound/usb/bcd2000/bcd2000.c16
-rw-r--r--sound/usb/caiaq/audio.c64
-rw-r--r--sound/usb/caiaq/audio.h1
-rw-r--r--sound/usb/caiaq/device.c19
-rw-r--r--sound/usb/caiaq/input.c12
-rw-r--r--sound/usb/caiaq/input.h1
-rw-r--r--sound/usb/card.c229
-rw-r--r--sound/usb/card.h17
-rw-r--r--sound/usb/clock.c86
-rw-r--r--sound/usb/endpoint.c154
-rw-r--r--sound/usb/fcp.c1127
-rw-r--r--sound/usb/fcp.h7
-rw-r--r--sound/usb/format.c64
-rw-r--r--sound/usb/helper.c35
-rw-r--r--sound/usb/helper.h10
-rw-r--r--sound/usb/hiface/chip.c11
-rw-r--r--sound/usb/hiface/pcm.c60
-rw-r--r--sound/usb/implicit.c1
-rw-r--r--sound/usb/line6/capture.c8
-rw-r--r--sound/usb/line6/capture.h2
-rw-r--r--sound/usb/line6/driver.c44
-rw-r--r--sound/usb/line6/driver.h2
-rw-r--r--sound/usb/line6/midi.c16
-rw-r--r--sound/usb/line6/midi.h2
-rw-r--r--sound/usb/line6/midibuf.c2
-rw-r--r--sound/usb/line6/midibuf.h2
-rw-r--r--sound/usb/line6/pcm.c89
-rw-r--r--sound/usb/line6/pcm.h2
-rw-r--r--sound/usb/line6/playback.c2
-rw-r--r--sound/usb/line6/playback.h2
-rw-r--r--sound/usb/line6/pod.c2
-rw-r--r--sound/usb/line6/podhd.c18
-rw-r--r--sound/usb/line6/toneport.c6
-rw-r--r--sound/usb/line6/variax.c2
-rw-r--r--sound/usb/media.c6
-rw-r--r--sound/usb/midi.c188
-rw-r--r--sound/usb/midi2.c55
-rw-r--r--sound/usb/misc/ua101.c262
-rw-r--r--sound/usb/mixer.c254
-rw-r--r--sound/usb/mixer.h1
-rw-r--r--sound/usb/mixer_maps.c22
-rw-r--r--sound/usb/mixer_quirks.c1864
-rw-r--r--sound/usb/mixer_s1810c.c318
-rw-r--r--sound/usb/mixer_scarlett.c35
-rw-r--r--sound/usb/mixer_scarlett2.c1647
-rw-r--r--sound/usb/mixer_us16x08.c37
-rw-r--r--sound/usb/pcm.c390
-rw-r--r--sound/usb/pcm.h11
-rw-r--r--sound/usb/power.c3
-rw-r--r--sound/usb/power.h1
-rw-r--r--sound/usb/proc.c5
-rw-r--r--sound/usb/qcom/Makefile4
-rw-r--r--sound/usb/qcom/mixer_usb_offload.c155
-rw-r--r--sound/usb/qcom/mixer_usb_offload.h11
-rw-r--r--sound/usb/qcom/qc_audio_offload.c1993
-rw-r--r--sound/usb/qcom/usb_audio_qmi_v01.c863
-rw-r--r--sound/usb/qcom/usb_audio_qmi_v01.h164
-rw-r--r--sound/usb/quirks-table.h2585
-rw-r--r--sound/usb/quirks.c372
-rw-r--r--sound/usb/quirks.h11
-rw-r--r--sound/usb/stream.c64
-rw-r--r--sound/usb/usbaudio.h122
-rw-r--r--sound/usb/usx2y/Makefile2
-rw-r--r--sound/usb/usx2y/us122l.c118
-rw-r--r--sound/usb/usx2y/us122l.h2
-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/usX2Yhwdep.c27
-rw-r--r--sound/usb/usx2y/usb_stream.c95
-rw-r--r--sound/usb/usx2y/usb_stream.h1
-rw-r--r--sound/usb/usx2y/usbusx2y.c22
-rw-r--r--sound/usb/usx2y/usbusx2y.h26
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c117
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c87
-rw-r--r--sound/usb/validate.c23
86 files changed, 12967 insertions, 4447 deletions
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
index 33e962178c93..5ff78814e687 100644
--- a/sound/usb/6fire/chip.c
+++ b/sound/usb/6fire/chip.c
@@ -61,8 +61,10 @@ static void usb6fire_chip_abort(struct sfire_chip *chip)
}
}
-static void usb6fire_chip_destroy(struct sfire_chip *chip)
+static void usb6fire_card_free(struct snd_card *card)
{
+ struct sfire_chip *chip = card->private_data;
+
if (chip) {
if (chip->pcm)
usb6fire_pcm_destroy(chip);
@@ -72,8 +74,6 @@ static void usb6fire_chip_destroy(struct sfire_chip *chip)
usb6fire_comm_destroy(chip);
if (chip->control)
usb6fire_control_destroy(chip);
- if (chip->card)
- snd_card_free(chip->card);
}
}
@@ -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);
@@ -125,8 +123,8 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
dev_err(&intf->dev, "cannot create alsa card.\n");
return ret;
}
- strcpy(card->driver, "6FireUSB");
- strcpy(card->shortname, "TerraTec DMX6FireUSB");
+ strscpy(card->driver, "6FireUSB");
+ strscpy(card->shortname, "TerraTec DMX6FireUSB");
sprintf(card->longname, "%s at %d:%d", card->shortname,
device->bus->busnum, device->devnum);
@@ -136,6 +134,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
chip->regidx = regidx;
chip->intf_count = 1;
chip->card = card;
+ card->private_free = usb6fire_card_free;
ret = usb6fire_comm_init(chip);
if (ret < 0)
@@ -162,7 +161,7 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
return 0;
destroy_chip:
- usb6fire_chip_destroy(chip);
+ snd_card_free(card);
return ret;
}
@@ -174,14 +173,13 @@ 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);
- usb6fire_chip_destroy(chip);
}
}
}
diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c
index de2691d58de6..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 = {
@@ -183,7 +172,7 @@ int usb6fire_midi_init(struct sfire_chip *chip)
return ret;
}
rt->instance->private_data = rt;
- strcpy(rt->instance->name, "DMX6FireUSB MIDI");
+ strscpy(rt->instance->name, "DMX6FireUSB MIDI");
rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
index 32c39d8bd2e5..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;
}
@@ -640,7 +627,7 @@ int usb6fire_pcm_init(struct sfire_chip *chip)
}
pcm->private_data = rt;
- strcpy(pcm->name, "DMX 6Fire USB");
+ strscpy(pcm->name, "DMX 6Fire USB");
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops);
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 4a9569a3a39a..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
@@ -176,6 +188,21 @@ config SND_BCD2000
To compile this driver as a module, choose M here: the module
will be called snd-bcd2000.
+config SND_USB_AUDIO_QMI
+ tristate "Qualcomm Audio Offload driver"
+ depends on QCOM_QMI_HELPERS && SND_USB_AUDIO && SND_SOC_USB
+ depends on USB_XHCI_HCD && USB_XHCI_SIDEBAND
+ help
+ Say Y here to enable the Qualcomm USB audio offloading feature.
+
+ This module sets up the required QMI stream enable/disable
+ responses to requests generated by the audio DSP. It passes the
+ USB transfer resource references, so that the audio DSP can issue
+ USB transfers to the host controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-audio-qmi.
+
source "sound/usb/line6/Kconfig"
endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 0532499dbc6d..e62794a87e73 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -6,6 +6,7 @@
snd-usb-audio-y := card.o \
clock.o \
endpoint.o \
+ fcp.o \
format.o \
helper.o \
implicit.o \
@@ -34,5 +35,5 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/ qcom/
obj-$(CONFIG_SND_USB_LINE6) += line6/
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 4981753652a7..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
@@ -174,17 +169,9 @@ static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
return 0;
}
-/* this should probably go upstream */
-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
-#error "Change this table"
-#endif
-
-static const unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
-
static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
{
- int bytes_per_sample, bpp, ret, i;
+ int bytes_per_sample, bpp, ret;
int index = substream->number;
struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -233,10 +220,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
/* the first client that opens a stream defines the sample rate
* setting for all subsequent calls, until the last client closed. */
- for (i=0; i < ARRAY_SIZE(rates); i++)
- if (runtime->rate == rates[i])
- cdev->pcm_info.rates = 1 << i;
-
+ cdev->pcm_info.rates = snd_pcm_rate_to_rate_bit(runtime->rate);
snd_pcm_limit_hw_rates(runtime);
bytes_per_sample = BYTES_PER_SAMPLE;
@@ -296,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 */
@@ -612,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)
@@ -649,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;
@@ -869,14 +845,20 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev)
return 0;
}
-void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)
+void snd_usb_caiaq_audio_disconnect(struct snd_usb_caiaqdev *cdev)
{
struct device *dev = caiaqdev_to_dev(cdev);
dev_dbg(dev, "%s(%p)\n", __func__, cdev);
stream_stop(cdev);
+}
+
+void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)
+{
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
free_urbs(cdev->data_urbs_in);
free_urbs(cdev->data_urbs_out);
kfree(cdev->data_cb_info);
}
-
diff --git a/sound/usb/caiaq/audio.h b/sound/usb/caiaq/audio.h
index 869bf6264d6a..07f5d064456c 100644
--- a/sound/usb/caiaq/audio.h
+++ b/sound/usb/caiaq/audio.h
@@ -3,6 +3,7 @@
#define CAIAQ_AUDIO_H
int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_audio_disconnect(struct snd_usb_caiaqdev *cdev);
void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev);
#endif /* CAIAQ_AUDIO_H */
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index b5cbf1f195c4..dfd820483849 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -376,6 +376,17 @@ static void setup_card(struct snd_usb_caiaqdev *cdev)
dev_err(dev, "Unable to set up control system (ret=%d)\n", ret);
}
+static void card_free(struct snd_card *card)
+{
+ struct snd_usb_caiaqdev *cdev = caiaqdev(card);
+
+#ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ snd_usb_caiaq_input_free(cdev);
+#endif
+ snd_usb_caiaq_audio_free(cdev);
+ usb_reset_device(cdev->chip.dev);
+}
+
static int create_card(struct usb_device *usb_dev,
struct usb_interface *intf,
struct snd_card **cardp)
@@ -489,6 +500,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)
cdev->vendor_name, cdev->product_name, usbpath);
setup_card(cdev);
+ card->private_free = card_free;
return 0;
err_kill_urb:
@@ -534,15 +546,14 @@ static void snd_disconnect(struct usb_interface *intf)
snd_card_disconnect(card);
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
- snd_usb_caiaq_input_free(cdev);
+ snd_usb_caiaq_input_disconnect(cdev);
#endif
- snd_usb_caiaq_audio_free(cdev);
+ snd_usb_caiaq_audio_disconnect(cdev);
usb_kill_urb(&cdev->ep1_in_urb);
usb_kill_urb(&cdev->midi_out_urb);
- snd_card_free(card);
- usb_reset_device(interface_to_usbdev(intf));
+ snd_card_free_when_closed(card);
}
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index 84f26dce7f5d..a9130891bb69 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -829,15 +829,21 @@ exit_free_idev:
return ret;
}
-void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev)
+void snd_usb_caiaq_input_disconnect(struct snd_usb_caiaqdev *cdev)
{
if (!cdev || !cdev->input_dev)
return;
usb_kill_urb(cdev->ep4_in_urb);
+ input_unregister_device(cdev->input_dev);
+}
+
+void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev)
+{
+ if (!cdev || !cdev->input_dev)
+ return;
+
usb_free_urb(cdev->ep4_in_urb);
cdev->ep4_in_urb = NULL;
-
- input_unregister_device(cdev->input_dev);
cdev->input_dev = NULL;
}
diff --git a/sound/usb/caiaq/input.h b/sound/usb/caiaq/input.h
index c42891e7be88..fbe267f85d02 100644
--- a/sound/usb/caiaq/input.h
+++ b/sound/usb/caiaq/input.h
@@ -4,6 +4,7 @@
void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *cdev, char *buf, unsigned int len);
int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_input_disconnect(struct snd_usb_caiaqdev *cdev);
void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev);
#endif
diff --git a/sound/usb/card.c b/sound/usb/card.c
index bdb04fa37a71..270dad84d825 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.
@@ -118,6 +137,95 @@ MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no)
static DEFINE_MUTEX(register_mutex);
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
static struct usb_driver usb_audio_driver;
+static struct snd_usb_platform_ops *platform_ops;
+
+/*
+ * Register platform specific operations that will be notified on events
+ * which occur in USB SND. The platform driver can utilize this path to
+ * enable features, such as USB audio offloading, which allows for audio data
+ * to be queued by an audio DSP.
+ *
+ * Only one set of platform operations can be registered to USB SND. The
+ * platform register operation is protected by the register_mutex.
+ */
+int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops)
+{
+ guard(mutex)(&register_mutex);
+ if (platform_ops)
+ return -EEXIST;
+
+ platform_ops = ops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_usb_register_platform_ops);
+
+/*
+ * Unregisters the current set of platform operations. This allows for
+ * a new set to be registered if required.
+ *
+ * The platform unregister operation is protected by the register_mutex.
+ */
+int snd_usb_unregister_platform_ops(void)
+{
+ guard(mutex)(&register_mutex);
+ platform_ops = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_usb_unregister_platform_ops);
+
+/*
+ * in case the platform driver was not ready at the time of USB SND
+ * device connect, expose an API to discover all connected USB devices
+ * so it can populate any dependent resources/structures.
+ */
+void snd_usb_rediscover_devices(void)
+{
+ int i;
+
+ guard(mutex)(&register_mutex);
+
+ if (!platform_ops || !platform_ops->connect_cb)
+ return;
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (usb_chip[i])
+ platform_ops->connect_cb(usb_chip[i]);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_usb_rediscover_devices);
+
+/*
+ * Checks to see if requested audio profile, i.e sample rate, # of
+ * channels, etc... is supported by the substream associated to the
+ * USB audio device.
+ */
+struct snd_usb_stream *
+snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params,
+ int direction)
+{
+ struct snd_usb_audio *chip;
+ struct snd_usb_substream *subs;
+ struct snd_usb_stream *as;
+
+ /*
+ * Register mutex is held when populating and clearing usb_chip
+ * array.
+ */
+ guard(mutex)(&register_mutex);
+ chip = usb_chip[card_idx];
+
+ if (chip && enable[card_idx]) {
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ subs = &as->substream[direction];
+ if (snd_usb_find_substream_format(subs, params))
+ return as;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_usb_find_suppported_substream);
/*
* disconnect streams
@@ -206,6 +314,8 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
return -EINVAL;
}
+ snd_usb_add_ctrl_interface_link(chip, interface, ctrlif);
+
if (! snd_usb_parse_audio_interface(chip, interface)) {
usb_set_interface(dev, interface, 0); /* reset the current interface */
return usb_driver_claim_interface(&usb_audio_driver, iface,
@@ -382,6 +492,12 @@ static const struct usb_audio_device_name usb_audio_names[] = {
/* Creative/Toshiba Multimedia Center SB-0500 */
DEVICE_NAME(0x041e, 0x3048, "Toshiba", "SB-0500"),
+ /* Logitech Audio Devices */
+ DEVICE_NAME(0x046d, 0x0867, "Logitech, Inc.", "Logi-MeetUp"),
+ DEVICE_NAME(0x046d, 0x0874, "Logitech, Inc.", "Logi-Tap-Audio"),
+ DEVICE_NAME(0x046d, 0x087c, "Logitech, Inc.", "Logi-Huddle"),
+ DEVICE_NAME(0x046d, 0x0898, "Logitech, Inc.", "Logi-RB-Audio"),
+ DEVICE_NAME(0x046d, 0x08d2, "Logitech, Inc.", "Logi-RBM-Audio"),
DEVICE_NAME(0x046d, 0x0990, "Logitech, Inc.", "QuickCam Pro 9000"),
DEVICE_NAME(0x05e1, 0x0408, "Syntek", "STK1160"),
@@ -519,9 +635,10 @@ static void usb_audio_make_shortname(struct usb_device *dev,
usb_string(dev, dev->descriptor.iProduct,
card->shortname, sizeof(card->shortname)) <= 0) {
/* no name available from anywhere, so use ID */
- sprintf(card->shortname, "USB Device %#04x:%#04x",
- USB_ID_VENDOR(chip->usb_id),
- USB_ID_PRODUCT(chip->usb_id));
+ scnprintf(card->shortname, sizeof(card->shortname),
+ "USB Device %#04x:%#04x",
+ USB_ID_VENDOR(chip->usb_id),
+ USB_ID_PRODUCT(chip->usb_id));
}
strim(card->shortname);
@@ -594,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.
*/
@@ -652,16 +794,13 @@ 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;
- strcpy(card->driver, "USB-Audio");
- sprintf(component, "USB%04x:%04x",
- USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ strscpy(card->driver, "USB-Audio");
+ scnprintf(component, sizeof(component), "USB%04x:%04x",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
snd_component_add(card, component);
usb_audio_make_shortname(dev, chip, quirk);
@@ -752,10 +891,16 @@ get_alias_quirk(struct usb_device *dev, unsigned int id)
*/
static int try_to_register_card(struct snd_usb_audio *chip, int ifnum)
{
+ struct usb_interface *iface;
+
if (check_delayed_register_option(chip) == ifnum ||
- chip->last_iface == ifnum ||
- usb_interface_claimed(usb_ifnum_to_if(chip->dev, chip->last_iface)))
+ chip->last_iface == ifnum)
+ return snd_card_register(chip->card);
+
+ iface = usb_ifnum_to_if(chip->dev, chip->last_iface);
+ if (iface && usb_interface_claimed(iface))
return snd_card_register(chip->card);
+
return 0;
}
@@ -802,7 +947,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)) {
@@ -914,7 +1059,10 @@ static int usb_audio_probe(struct usb_interface *intf,
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
atomic_dec(&chip->active);
- mutex_unlock(&register_mutex);
+
+ if (platform_ops && platform_ops->connect_cb)
+ platform_ops->connect_cb(chip);
+
return 0;
__error:
@@ -931,7 +1079,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;
}
@@ -939,18 +1086,17 @@ 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;
+ guard(mutex)(&register_mutex);
- card = chip->card;
+ if (platform_ops && platform_ops->disconnect_cb)
+ platform_ops->disconnect_cb(chip);
- mutex_lock(&register_mutex);
if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
struct snd_usb_endpoint *ep;
@@ -993,13 +1139,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 */
@@ -1022,6 +1179,7 @@ int snd_usb_lock_shutdown(struct snd_usb_audio *chip)
wake_up(&chip->shutdown_wait);
return err;
}
+EXPORT_SYMBOL_GPL(snd_usb_lock_shutdown);
/* autosuspend and unlock the shutdown */
void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
@@ -1030,6 +1188,7 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip)
if (atomic_dec_and_test(&chip->usage_count))
wake_up(&chip->shutdown_wait);
}
+EXPORT_SYMBOL_GPL(snd_usb_unlock_shutdown);
int snd_usb_autoresume(struct snd_usb_audio *chip)
{
@@ -1052,6 +1211,7 @@ int snd_usb_autoresume(struct snd_usb_audio *chip)
}
return 0;
}
+EXPORT_SYMBOL_GPL(snd_usb_autoresume);
void snd_usb_autosuspend(struct snd_usb_audio *chip)
{
@@ -1065,6 +1225,7 @@ void snd_usb_autosuspend(struct snd_usb_audio *chip)
for (i = 0; i < chip->num_interfaces; i++)
usb_autopm_put_interface(chip->intf[i]);
}
+EXPORT_SYMBOL_GPL(snd_usb_autosuspend);
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
{
@@ -1094,6 +1255,9 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
chip->system_suspend = chip->num_suspended_intf;
}
+ if (platform_ops && platform_ops->suspend_cb)
+ platform_ops->suspend_cb(intf, message);
+
return 0;
}
@@ -1134,6 +1298,9 @@ static int usb_audio_resume(struct usb_interface *intf)
snd_usb_midi_v2_resume_all(chip);
+ if (platform_ops && platform_ops->resume_cb)
+ platform_ops->resume_cb(intf);
+
out:
if (chip->num_suspended_intf == chip->system_suspend) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 6ec95b2edf86..94404c24d240 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -15,6 +15,7 @@ struct audioformat {
unsigned int channels; /* # channels */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int fmt_bits; /* number of significant bits */
+ unsigned int fmt_sz; /* overall audio sub frame/slot size */
unsigned int frame_size; /* samples per frame for non-audio */
unsigned char iface; /* interface number */
unsigned char altsetting; /* corresponding alternate setting */
@@ -164,6 +165,7 @@ struct snd_usb_substream {
unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
unsigned int stream_offset_adj; /* Bytes to drop from beginning of stream (for non-compliant devices) */
+ unsigned int opened:1; /* pcm device opened */
unsigned int running: 1; /* running status */
unsigned int period_elapsed_pending; /* delay period handling */
@@ -207,4 +209,19 @@ struct snd_usb_stream {
struct list_head list;
};
+struct snd_usb_platform_ops {
+ void (*connect_cb)(struct snd_usb_audio *chip);
+ void (*disconnect_cb)(struct snd_usb_audio *chip);
+ void (*suspend_cb)(struct usb_interface *intf, pm_message_t message);
+ void (*resume_cb)(struct usb_interface *intf);
+};
+
+struct snd_usb_stream *
+snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params,
+ int direction);
+
+int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops);
+int snd_usb_unregister_platform_ops(void);
+
+void snd_usb_rediscover_devices(void);
#endif /* __USBAUDIO_CARD_H */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 60fcb872a80b..842ba5b801ea 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -36,6 +36,12 @@ union uac23_clock_multiplier_desc {
struct uac_clock_multiplier_descriptor v3;
};
+/* check whether the descriptor bLength has the minimal length */
+#define DESC_LENGTH_CHECK(p, proto) \
+ ((proto) == UAC_VERSION_3 ? \
+ ((p)->v3.bLength >= sizeof((p)->v3)) : \
+ ((p)->v2.bLength >= sizeof((p)->v2)))
+
#define GET_VAL(p, proto, field) \
((proto) == UAC_VERSION_3 ? (p)->v3.field : (p)->v2.field)
@@ -58,6 +64,8 @@ static bool validate_clock_source(void *p, int id, int proto)
{
union uac23_clock_source_desc *cs = p;
+ if (!DESC_LENGTH_CHECK(cs, proto))
+ return false;
return GET_VAL(cs, proto, bClockID) == id;
}
@@ -65,22 +73,39 @@ static bool validate_clock_selector(void *p, int id, int proto)
{
union uac23_clock_selector_desc *cs = p;
- return GET_VAL(cs, proto, bClockID) == id;
+ if (!DESC_LENGTH_CHECK(cs, proto))
+ return false;
+ if (GET_VAL(cs, proto, bClockID) != id)
+ return false;
+ /* additional length check for baCSourceID array (in bNrInPins size)
+ * and two more fields (which sizes depend on the protocol)
+ */
+ if (proto == UAC_VERSION_3)
+ return cs->v3.bLength >= sizeof(cs->v3) + cs->v3.bNrInPins +
+ 4 /* bmControls */ + 2 /* wCSelectorDescrStr */;
+ else
+ return cs->v2.bLength >= sizeof(cs->v2) + cs->v2.bNrInPins +
+ 1 /* bmControls */ + 1 /* iClockSelector */;
}
static bool validate_clock_multiplier(void *p, int id, int proto)
{
union uac23_clock_multiplier_desc *cs = p;
+ if (!DESC_LENGTH_CHECK(cs, proto))
+ return false;
return GET_VAL(cs, proto, bClockID) == id;
}
#define DEFINE_FIND_HELPER(name, obj, validator, type2, type3) \
-static obj *name(struct snd_usb_audio *chip, int id, int proto) \
+static obj *name(struct snd_usb_audio *chip, int id, \
+ const struct audioformat *fmt) \
{ \
- return find_uac_clock_desc(chip->ctrl_intf, id, validator, \
- proto == UAC_VERSION_3 ? (type3) : (type2), \
- proto); \
+ struct usb_host_interface *ctrl_intf = \
+ snd_usb_find_ctrl_interface(chip, fmt->iface); \
+ return find_uac_clock_desc(ctrl_intf, id, validator, \
+ fmt->protocol == UAC_VERSION_3 ? (type3) : (type2), \
+ fmt->protocol); \
}
DEFINE_FIND_HELPER(snd_usb_find_clock_source,
@@ -93,16 +118,19 @@ DEFINE_FIND_HELPER(snd_usb_find_clock_multiplier,
union uac23_clock_multiplier_desc, validate_clock_multiplier,
UAC2_CLOCK_MULTIPLIER, UAC3_CLOCK_MULTIPLIER);
-static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_id)
+static int uac_clock_selector_get_val(struct snd_usb_audio *chip,
+ int selector_id, int iface_no)
{
+ struct usb_host_interface *ctrl_intf;
unsigned char buf;
int ret;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, iface_no);
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
UAC2_CS_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
UAC2_CX_CLOCK_SELECTOR << 8,
- snd_usb_ctrl_intf(chip) | (selector_id << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (selector_id << 8),
&buf, sizeof(buf));
if (ret < 0)
@@ -111,16 +139,18 @@ static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_i
return buf;
}
-static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id,
- unsigned char pin)
+static int uac_clock_selector_set_val(struct snd_usb_audio *chip,
+ int selector_id, unsigned char pin, int iface_no)
{
+ struct usb_host_interface *ctrl_intf;
int ret;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, iface_no);
ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
UAC2_CS_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
UAC2_CX_CLOCK_SELECTOR << 8,
- snd_usb_ctrl_intf(chip) | (selector_id << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (selector_id << 8),
&pin, sizeof(pin));
if (ret < 0)
return ret;
@@ -132,7 +162,7 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i
return -EINVAL;
}
- ret = uac_clock_selector_get_val(chip, selector_id);
+ ret = uac_clock_selector_get_val(chip, selector_id, iface_no);
if (ret < 0)
return ret;
@@ -155,8 +185,10 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
unsigned char data;
struct usb_device *dev = chip->dev;
union uac23_clock_source_desc *cs_desc;
+ struct usb_host_interface *ctrl_intf;
- cs_desc = snd_usb_find_clock_source(chip, source_id, fmt->protocol);
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, fmt->iface);
+ cs_desc = snd_usb_find_clock_source(chip, source_id, fmt);
if (!cs_desc)
return false;
@@ -191,7 +223,7 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip,
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_CLOCK_VALID << 8,
- snd_usb_ctrl_intf(chip) | (source_id << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (source_id << 8),
&data, sizeof(data));
if (err < 0) {
dev_warn(&dev->dev,
@@ -217,8 +249,10 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
struct usb_device *dev = chip->dev;
u32 bmControls;
union uac23_clock_source_desc *cs_desc;
+ struct usb_host_interface *ctrl_intf;
- cs_desc = snd_usb_find_clock_source(chip, source_id, fmt->protocol);
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, fmt->iface);
+ cs_desc = snd_usb_find_clock_source(chip, source_id, fmt);
if (!cs_desc)
return false;
@@ -235,7 +269,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip,
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_CLOCK_VALID << 8,
- snd_usb_ctrl_intf(chip) | (source_id << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (source_id << 8),
&data, sizeof(data));
if (err < 0) {
@@ -274,7 +308,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
}
/* first, see if the ID we're looking at is a clock source already */
- source = snd_usb_find_clock_source(chip, entity_id, proto);
+ source = snd_usb_find_clock_source(chip, entity_id, fmt);
if (source) {
entity_id = GET_VAL(source, proto, bClockID);
if (validate && !uac_clock_source_is_valid(chip, fmt,
@@ -287,7 +321,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
return entity_id;
}
- selector = snd_usb_find_clock_selector(chip, entity_id, proto);
+ selector = snd_usb_find_clock_selector(chip, entity_id, fmt);
if (selector) {
pins = GET_VAL(selector, proto, bNrInPins);
clock_id = GET_VAL(selector, proto, bClockID);
@@ -317,7 +351,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
/* the entity ID we are looking at is a selector.
* find out what it currently selects */
- ret = uac_clock_selector_get_val(chip, clock_id);
+ ret = uac_clock_selector_get_val(chip, clock_id, fmt->iface);
if (ret < 0) {
if (!chip->autoclock)
return ret;
@@ -346,7 +380,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
if (chip->quirk_flags & QUIRK_FLAG_SKIP_CLOCK_SELECTOR ||
!writeable)
return ret;
- err = uac_clock_selector_set_val(chip, entity_id, cur);
+ err = uac_clock_selector_set_val(chip, entity_id, cur, fmt->iface);
if (err < 0) {
if (pins == 1) {
usb_audio_dbg(chip,
@@ -377,7 +411,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
if (ret < 0)
continue;
- err = uac_clock_selector_set_val(chip, entity_id, i);
+ err = uac_clock_selector_set_val(chip, entity_id, i, fmt->iface);
if (err < 0)
continue;
@@ -391,7 +425,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
}
/* FIXME: multipliers only act as pass-thru element for now */
- multiplier = snd_usb_find_clock_multiplier(chip, entity_id, proto);
+ multiplier = snd_usb_find_clock_multiplier(chip, entity_id, fmt);
if (multiplier)
return __uac_clock_find_source(chip, fmt,
GET_VAL(multiplier, proto, bCSourceID),
@@ -491,11 +525,13 @@ static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
struct usb_device *dev = chip->dev;
__le32 data;
int err;
+ struct usb_host_interface *ctrl_intf;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, iface);
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (clock << 8),
&data, sizeof(data));
if (err < 0) {
dev_warn(&dev->dev, "%d:%d: cannot get freq (v2/v3): err %d\n",
@@ -524,8 +560,10 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
__le32 data;
int err;
union uac23_clock_source_desc *cs_desc;
+ struct usb_host_interface *ctrl_intf;
- cs_desc = snd_usb_find_clock_source(chip, clock, fmt->protocol);
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, fmt->iface);
+ cs_desc = snd_usb_find_clock_source(chip, clock, fmt);
if (!cs_desc)
return 0;
@@ -544,7 +582,7 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (clock << 8),
&data, sizeof(data));
if (err < 0)
return err;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 8f65349a06d3..cc15624ecaff 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;
}
@@ -403,10 +400,15 @@ static int prepare_inbound_urb(struct snd_usb_endpoint *ep,
static void notify_xrun(struct snd_usb_endpoint *ep)
{
struct snd_usb_substream *data_subs;
+ struct snd_pcm_substream *psubs;
data_subs = READ_ONCE(ep->data_subs);
- if (data_subs && data_subs->pcm_substream)
- snd_pcm_stop_xrun(data_subs->pcm_substream);
+ if (!data_subs)
+ return;
+ psubs = data_subs->pcm_substream;
+ if (psubs && psubs->runtime &&
+ psubs->runtime->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_stop_xrun(psubs);
}
static struct snd_usb_packet_info *
@@ -435,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);
}
/*
@@ -461,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;
@@ -562,7 +559,10 @@ static void snd_complete_urb(struct urb *urb)
push_back_to_ready_list(ep, ctx);
clear_bit(ctx->index, &ep->active_mask);
snd_usb_queue_pending_output_urbs(ep, false);
- atomic_dec(&ep->submitted_urbs); /* decrement at last */
+ /* decrement at last, and check xrun */
+ if (atomic_dec_and_test(&ep->submitted_urbs) &&
+ !snd_usb_endpoint_implicit_feedback_sink(ep))
+ notify_xrun(ep);
return;
}
@@ -760,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);
}
/*
@@ -791,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) {
@@ -812,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++;
}
@@ -851,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",
@@ -871,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;
}
@@ -918,14 +904,24 @@ static int endpoint_set_interface(struct snd_usb_audio *chip,
{
int altset = set ? ep->altsetting : 0;
int err;
+ int retries = 0;
+ const int max_retries = 5;
if (ep->iface_ref->altset == altset)
return 0;
+ /* already disconnected? */
+ if (unlikely(atomic_read(&chip->shutdown)))
+ return -ENODEV;
usb_audio_dbg(chip, "Setting usb interface %d:%d for EP 0x%x\n",
ep->iface, altset, ep->ep_num);
+retry:
err = usb_set_interface(chip->dev, ep->iface, altset);
if (err < 0) {
+ if (err == -EPROTO && ++retries <= max_retries) {
+ msleep(5 * (1 << (retries - 1)));
+ goto retry;
+ }
usb_audio_err_ratelimited(
chip, "%d:%d: usb_set_interface failed (%d)\n",
ep->iface, altset, err);
@@ -946,7 +942,7 @@ static int endpoint_set_interface(struct snd_usb_audio *chip,
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);
@@ -967,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 */
@@ -1029,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;
@@ -1037,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;
@@ -1342,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;
@@ -1368,6 +1362,11 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
ep->sample_rem = ep->cur_rate % ep->pps;
ep->packsize[0] = ep->cur_rate / ep->pps;
ep->packsize[1] = (ep->cur_rate + (ep->pps - 1)) / ep->pps;
+ if (ep->packsize[1] > ep->maxpacksize) {
+ usb_audio_dbg(chip, "Too small maxpacksize %u for rate %u / pps %u\n",
+ ep->maxpacksize, ep->cur_rate, ep->pps);
+ return -EINVAL;
+ }
/* calculate the frequency in 16.16 format */
ep->freqm = ep->freqn;
@@ -1389,7 +1388,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;
@@ -1401,8 +1400,6 @@ int snd_usb_endpoint_set_params(struct snd_usb_audio *chip,
err = 0;
}
- unlock:
- mutex_unlock(&chip->mutex);
return err;
}
@@ -1449,11 +1446,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) {
@@ -1463,7 +1460,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;
}
@@ -1481,38 +1478,35 @@ 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);
/* get the current rate set to the given clock by any endpoint */
int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
@@ -1522,14 +1516,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;
}
@@ -1879,9 +1872,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
new file mode 100644
index 000000000000..11e9a96b46ff
--- /dev/null
+++ b/sound/usb/fcp.c
@@ -0,0 +1,1127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Focusrite Control Protocol Driver for ALSA
+ *
+ * Copyright (c) 2024-2025 by Geoffrey D. Bennett <g at b4.vu>
+ */
+/*
+ * DOC: Theory of Operation
+ *
+ * The Focusrite Control Protocol (FCP) driver provides a minimal
+ * kernel interface that allows a user-space driver (primarily
+ * fcp-server) to communicate with Focusrite USB audio interfaces
+ * using their vendor-specific protocol. This protocol is used by
+ * Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, Clarett+, and
+ * Vocaster series devices.
+ *
+ * Unlike the existing scarlett2 driver which implements all controls
+ * in kernel space, this driver takes a lighter-weight approach by
+ * moving most functionality to user space. The only control
+ * implemented in kernel space is the Level Meter, since it requires
+ * frequent polling of volatile data.
+ *
+ * The driver provides an hwdep interface that allows the user-space
+ * driver to:
+ * - Initialise the protocol
+ * - Send arbitrary FCP commands to the device
+ * - Receive notifications from the device
+ * - Configure the Level Meter control
+ *
+ * Usage Flow
+ * ----------
+ * 1. Open the hwdep device (requires CAP_SYS_RAWIO)
+ * 2. Get protocol version using FCP_IOCTL_PVERSION
+ * 3. Initialise protocol using FCP_IOCTL_INIT
+ * 4. Send commands using FCP_IOCTL_CMD
+ * 5. Receive notifications using read()
+ * 6. Optionally set up the Level Meter control using
+ * FCP_IOCTL_SET_METER_MAP
+ * 7. Optionally add labels to the Level Meter control using
+ * FCP_IOCTL_SET_METER_LABELS
+ *
+ * Level Meter
+ * -----------
+ * The Level Meter is implemented as an ALSA control that provides
+ * real-time level monitoring. When the control is read, the driver
+ * requests the current meter levels from the device, translates the
+ * levels using the configured mapping, and returns the result to the
+ * user. The mapping between device meters and the ALSA control's
+ * channels is configured with FCP_IOCTL_SET_METER_MAP.
+ *
+ * Labels for the Level Meter channels can be set using
+ * FCP_IOCTL_SET_METER_LABELS and read by applications through the
+ * control's TLV data. The labels are transferred as a sequence of
+ * null-terminated strings.
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <sound/control.h>
+#include <sound/hwdep.h>
+#include <sound/tlv.h>
+
+#include <uapi/sound/fcp.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "helper.h"
+
+#include "fcp.h"
+
+/* notify waiting to send to *file */
+struct fcp_notify {
+ wait_queue_head_t queue;
+ u32 event;
+ spinlock_t lock;
+};
+
+struct fcp_data {
+ struct usb_mixer_interface *mixer;
+
+ struct mutex mutex; /* serialise access to the device */
+ struct completion cmd_done; /* wait for command completion */
+ struct file *file; /* hwdep file */
+
+ struct fcp_notify notify;
+
+ u8 bInterfaceNumber;
+ u8 bEndpointAddress;
+ u16 wMaxPacketSize;
+ u8 bInterval;
+
+ uint16_t step0_resp_size;
+ uint16_t step2_resp_size;
+ uint32_t init1_opcode;
+ uint32_t init2_opcode;
+
+ u8 init;
+ u16 seq;
+
+ u8 num_meter_slots;
+ s16 *meter_level_map;
+ __le32 *meter_levels;
+ struct snd_kcontrol *meter_ctl;
+
+ unsigned int *meter_labels_tlv;
+ int meter_labels_tlv_size;
+};
+
+/*** USB Interactions ***/
+
+/* FCP Command ACK notification bit */
+#define FCP_NOTIFY_ACK 1
+
+/* Vendor-specific USB control requests */
+#define FCP_USB_REQ_STEP0 0
+#define FCP_USB_REQ_CMD_TX 2
+#define FCP_USB_REQ_CMD_RX 3
+
+/* Focusrite Control Protocol opcodes that the kernel side needs to
+ * know about
+ */
+#define FCP_USB_REBOOT 0x00000003
+#define FCP_USB_GET_METER 0x00001001
+#define FCP_USB_FLASH_ERASE 0x00004002
+#define FCP_USB_FLASH_WRITE 0x00004004
+
+#define FCP_USB_METER_LEVELS_GET_MAGIC 1
+
+#define FCP_SEGMENT_APP_GOLD 0
+
+/* Forward declarations */
+static int fcp_init(struct usb_mixer_interface *mixer,
+ void *step0_resp, void *step2_resp);
+
+/* FCP command request/response format */
+struct fcp_usb_packet {
+ __le32 opcode;
+ __le16 size;
+ __le16 seq;
+ __le32 error;
+ __le32 pad;
+ u8 data[];
+};
+
+static void fcp_fill_request_header(struct fcp_data *private,
+ struct fcp_usb_packet *req,
+ u32 opcode, u16 req_size)
+{
+ /* sequence must go up by 1 for each request */
+ u16 seq = private->seq++;
+
+ req->opcode = cpu_to_le32(opcode);
+ req->size = cpu_to_le16(req_size);
+ req->seq = cpu_to_le16(seq);
+ req->error = 0;
+ req->pad = 0;
+}
+
+static int fcp_usb_tx(struct usb_device *dev, int interface,
+ void *buf, u16 size)
+{
+ return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ FCP_USB_REQ_CMD_TX,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ 0, interface, buf, size);
+}
+
+static int fcp_usb_rx(struct usb_device *dev, int interface,
+ void *buf, u16 size)
+{
+ return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ FCP_USB_REQ_CMD_RX,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ 0, interface, buf, size);
+}
+
+/* Send an FCP command and get the response */
+static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
+ const void *req_data, u16 req_size,
+ void *resp_data, u16 resp_size)
+{
+ struct fcp_data *private = mixer->private_data;
+ struct usb_device *dev = mixer->chip->dev;
+ struct fcp_usb_packet *req __free(kfree) = NULL;
+ struct fcp_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;
+ const int max_retries = 5;
+ int err;
+
+ if (!mixer->urb)
+ return -ENODEV;
+
+ req = kmalloc(req_buf_size, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kmalloc(resp_buf_size, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ /* build request message */
+ fcp_fill_request_header(private, req, opcode, req_size);
+ if (req_size)
+ memcpy(req->data, req_data, req_size);
+
+ /* send the request and retry on EPROTO */
+retry:
+ err = fcp_usb_tx(dev, private->bInterfaceNumber, req, req_buf_size);
+ if (err == -EPROTO && ++retries <= max_retries) {
+ msleep(1 << (retries - 1));
+ goto retry;
+ }
+
+ if (err != req_buf_size) {
+ usb_audio_err(mixer->chip,
+ "FCP request %08x failed: %d\n", opcode, err);
+ return -EINVAL;
+ }
+
+ if (!wait_for_completion_timeout(&private->cmd_done,
+ msecs_to_jiffies(1000))) {
+ usb_audio_err(mixer->chip,
+ "FCP request %08x timed out\n", opcode);
+
+ return -ETIMEDOUT;
+ }
+
+ /* send a second message to get the response */
+ err = fcp_usb_rx(dev, private->bInterfaceNumber, resp, resp_buf_size);
+
+ /* validate the response */
+
+ if (err < 0) {
+
+ /* ESHUTDOWN and EPROTO are valid responses to a
+ * reboot request
+ */
+ if (opcode == FCP_USB_REBOOT &&
+ (err == -ESHUTDOWN || err == -EPROTO))
+ return 0;
+
+ usb_audio_err(mixer->chip,
+ "FCP read response %08x failed: %d\n",
+ opcode, err);
+ return -EINVAL;
+ }
+
+ if (err < sizeof(*resp)) {
+ usb_audio_err(mixer->chip,
+ "FCP response %08x too short: %d\n",
+ opcode, err);
+ return -EINVAL;
+ }
+
+ if (req->seq != resp->seq) {
+ usb_audio_err(mixer->chip,
+ "FCP response %08x seq mismatch %d/%d\n",
+ opcode,
+ le16_to_cpu(req->seq), le16_to_cpu(resp->seq));
+ return -EINVAL;
+ }
+
+ if (req->opcode != resp->opcode) {
+ usb_audio_err(mixer->chip,
+ "FCP response %08x opcode mismatch %08x\n",
+ opcode, le32_to_cpu(resp->opcode));
+ return -EINVAL;
+ }
+
+ if (resp->error) {
+ usb_audio_err(mixer->chip,
+ "FCP response %08x error %d\n",
+ opcode, le32_to_cpu(resp->error));
+ return -EINVAL;
+ }
+
+ if (err != resp_buf_size) {
+ usb_audio_err(mixer->chip,
+ "FCP response %08x buffer size mismatch %d/%zu\n",
+ opcode, err, resp_buf_size);
+ return -EINVAL;
+ }
+
+ if (resp_size != le16_to_cpu(resp->size)) {
+ usb_audio_err(mixer->chip,
+ "FCP response %08x size mismatch %d/%d\n",
+ opcode, resp_size, le16_to_cpu(resp->size));
+ return -EINVAL;
+ }
+
+ if (resp_data && resp_size > 0)
+ memcpy(resp_data, resp->data, resp_size);
+
+ return 0;
+}
+
+static int fcp_reinit(struct usb_mixer_interface *mixer)
+{
+ struct fcp_data *private = mixer->private_data;
+ void *step0_resp __free(kfree) = NULL;
+ void *step2_resp __free(kfree) = NULL;
+
+ if (mixer->urb)
+ return 0;
+
+ step0_resp = kmalloc(private->step0_resp_size, GFP_KERNEL);
+ if (!step0_resp)
+ return -ENOMEM;
+ step2_resp = kmalloc(private->step2_resp_size, GFP_KERNEL);
+ if (!step2_resp)
+ return -ENOMEM;
+
+ return fcp_init(mixer, step0_resp, step2_resp);
+}
+
+/*** Control Functions ***/
+
+/* helper function to create a new control */
+static int fcp_add_new_ctl(struct usb_mixer_interface *mixer,
+ const struct snd_kcontrol_new *ncontrol,
+ int index, int channels, const char *name,
+ struct snd_kcontrol **kctl_return)
+{
+ struct snd_kcontrol *kctl;
+ struct usb_mixer_elem_info *elem;
+ int err;
+
+ elem = kzalloc(sizeof(*elem), GFP_KERNEL);
+ if (!elem)
+ return -ENOMEM;
+
+ /* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code
+ * ignores them for resume and other operations.
+ * Also, the head.id field is set to 0, as we don't use this field.
+ */
+ elem->head.mixer = mixer;
+ elem->control = index;
+ elem->head.id = 0;
+ elem->channels = channels;
+ elem->val_type = USB_MIXER_BESPOKEN;
+
+ kctl = snd_ctl_new1(ncontrol, elem);
+ if (!kctl) {
+ kfree(elem);
+ return -ENOMEM;
+ }
+ kctl->private_free = snd_usb_mixer_elem_free;
+
+ strscpy(kctl->id.name, name, sizeof(kctl->id.name));
+
+ err = snd_usb_mixer_add_control(&elem->head, kctl);
+ if (err < 0)
+ return err;
+
+ if (kctl_return)
+ *kctl_return = kctl;
+
+ return 0;
+}
+
+/*** Level Meter Control ***/
+
+static int fcp_meter_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 4095;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int fcp_meter_ctl_get(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 fcp_data *private = mixer->private_data;
+ int num_meter_slots, resp_size;
+ __le32 *resp = private->meter_levels;
+ int i, err = 0;
+
+ struct {
+ __le16 pad;
+ __le16 num_meters;
+ __le32 magic;
+ } __packed req;
+
+ guard(mutex)(&private->mutex);
+
+ err = fcp_reinit(mixer);
+ if (err < 0)
+ return err;
+
+ num_meter_slots = private->num_meter_slots;
+ resp_size = num_meter_slots * sizeof(u32);
+
+ req.pad = 0;
+ req.num_meters = cpu_to_le16(num_meter_slots);
+ req.magic = cpu_to_le32(FCP_USB_METER_LEVELS_GET_MAGIC);
+ err = fcp_usb(mixer, FCP_USB_GET_METER,
+ &req, sizeof(req), resp, resp_size);
+ if (err < 0)
+ return err;
+
+ /* copy & translate from resp[] using meter_level_map[] */
+ for (i = 0; i < elem->channels; i++) {
+ int idx = private->meter_level_map[i];
+ int value = idx < 0 ? 0 : le32_to_cpu(resp[idx]);
+
+ ucontrol->value.integer.value[i] = value;
+ }
+
+ return 0;
+}
+
+static int fcp_meter_tlv_callback(struct snd_kcontrol *kctl,
+ int op_flag, unsigned int size,
+ unsigned int __user *tlv)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct fcp_data *private = mixer->private_data;
+
+ guard(mutex)(&private->mutex);
+
+ if (op_flag == SNDRV_CTL_TLV_OP_READ) {
+ if (private->meter_labels_tlv_size == 0)
+ return 0;
+
+ if (size > private->meter_labels_tlv_size)
+ size = private->meter_labels_tlv_size;
+
+ if (copy_to_user(tlv, private->meter_labels_tlv, size))
+ return -EFAULT;
+
+ return size;
+ }
+
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new fcp_meter_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fcp_meter_ctl_info,
+ .get = fcp_meter_ctl_get,
+ .tlv = { .c = fcp_meter_tlv_callback },
+};
+
+/*** hwdep interface ***/
+
+/* FCP initialisation */
+static int fcp_ioctl_init(struct usb_mixer_interface *mixer,
+ struct fcp_init __user *arg)
+{
+ struct fcp_init init;
+ struct usb_device *dev = mixer->chip->dev;
+ struct fcp_data *private = mixer->private_data;
+ void *resp __free(kfree) = NULL;
+ void *step2_resp;
+ int err, buf_size;
+
+ if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
+ return -EINVAL;
+
+ /* Get initialisation parameters */
+ if (copy_from_user(&init, arg, sizeof(init)))
+ return -EFAULT;
+
+ /* Validate the response sizes */
+ if (init.step0_resp_size < 1 ||
+ init.step0_resp_size > 255 ||
+ init.step2_resp_size < 1 ||
+ init.step2_resp_size > 255)
+ return -EINVAL;
+
+ /* Allocate response buffer */
+ buf_size = init.step0_resp_size + init.step2_resp_size;
+
+ resp = kmalloc(buf_size, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ private->step0_resp_size = init.step0_resp_size;
+ private->step2_resp_size = init.step2_resp_size;
+ private->init1_opcode = init.init1_opcode;
+ private->init2_opcode = init.init2_opcode;
+
+ step2_resp = resp + private->step0_resp_size;
+
+ err = fcp_init(mixer, resp, step2_resp);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(arg->resp, resp, buf_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* Check that the command is allowed
+ * Don't permit erasing/writing segment 0 (App_Gold)
+ */
+static int fcp_validate_cmd(u32 opcode, void *data, u16 size)
+{
+ if (opcode == FCP_USB_FLASH_ERASE) {
+ struct {
+ __le32 segment_num;
+ __le32 pad;
+ } __packed *req = data;
+
+ if (size != sizeof(*req))
+ return -EINVAL;
+
+ if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD)
+ return -EPERM;
+
+ if (req->pad != 0)
+ return -EINVAL;
+
+ } else if (opcode == FCP_USB_FLASH_WRITE) {
+ struct {
+ __le32 segment_num;
+ __le32 offset;
+ __le32 pad;
+ u8 data[];
+ } __packed *req = data;
+
+ if (size < sizeof(*req))
+ return -EINVAL;
+
+ if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD)
+ return -EPERM;
+
+ if (req->pad != 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Execute an FCP command specified by the user */
+static int fcp_ioctl_cmd(struct usb_mixer_interface *mixer,
+ struct fcp_cmd __user *arg)
+{
+ struct fcp_cmd cmd;
+ int err, buf_size;
+ void *data __free(kfree) = NULL;
+
+ /* get opcode and request/response size */
+ if (copy_from_user(&cmd, arg, sizeof(cmd)))
+ return -EFAULT;
+
+ /* validate request and response sizes */
+ if (cmd.req_size > 4096 || cmd.resp_size > 4096)
+ return -EINVAL;
+
+ /* reinit if needed */
+ err = fcp_reinit(mixer);
+ if (err < 0)
+ return err;
+
+ /* allocate request/response buffer */
+ buf_size = max(cmd.req_size, cmd.resp_size);
+
+ if (buf_size > 0) {
+ data = kmalloc(buf_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ }
+
+ /* copy request from user */
+ if (cmd.req_size > 0)
+ if (copy_from_user(data, arg->data, cmd.req_size))
+ return -EFAULT;
+
+ /* check that the command is allowed */
+ err = fcp_validate_cmd(cmd.opcode, data, cmd.req_size);
+ if (err < 0)
+ return err;
+
+ /* send request, get response */
+ err = fcp_usb(mixer, cmd.opcode,
+ data, cmd.req_size, data, cmd.resp_size);
+ if (err < 0)
+ return err;
+
+ /* copy response to user */
+ if (cmd.resp_size > 0)
+ if (copy_to_user(arg->data, data, cmd.resp_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* Validate the Level Meter map passed by the user */
+static int validate_meter_map(const s16 *map, int map_size, int meter_slots)
+{
+ int i;
+
+ for (i = 0; i < map_size; i++)
+ if (map[i] < -1 || map[i] >= meter_slots)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Set the Level Meter map and add the control */
+static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
+ struct fcp_meter_map __user *arg)
+{
+ struct fcp_meter_map map;
+ struct fcp_data *private = mixer->private_data;
+ s16 *tmp_map __free(kfree) = NULL;
+ int err;
+
+ if (copy_from_user(&map, arg, sizeof(map)))
+ return -EFAULT;
+
+ /* Don't allow changing the map size or meter slots once set */
+ if (private->meter_ctl) {
+ struct usb_mixer_elem_info *elem =
+ private->meter_ctl->private_data;
+
+ if (map.map_size != elem->channels ||
+ map.meter_slots != private->num_meter_slots)
+ return -EINVAL;
+ }
+
+ /* Validate the map size */
+ if (map.map_size < 1 || map.map_size > 255 ||
+ map.meter_slots < 1 || map.meter_slots > 255)
+ return -EINVAL;
+
+ /* Allocate and copy the map data */
+ tmp_map = memdup_array_user(arg->map, map.map_size, sizeof(s16));
+ if (IS_ERR(tmp_map))
+ return PTR_ERR(tmp_map);
+
+ err = validate_meter_map(tmp_map, map.map_size, map.meter_slots);
+ if (err < 0)
+ return err;
+
+ /* If the control doesn't exist, create it */
+ if (!private->meter_ctl) {
+ s16 *new_map __free(kfree) = NULL;
+ __le32 *meter_levels __free(kfree) = NULL;
+
+ /* Allocate buffer for the map */
+ new_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ /* Allocate buffer for reading meter levels */
+ meter_levels = kmalloc_array(map.meter_slots, sizeof(__le32),
+ GFP_KERNEL);
+ if (!meter_levels)
+ return -ENOMEM;
+
+ /* Create the Level Meter control */
+ err = fcp_add_new_ctl(mixer, &fcp_meter_ctl, 0, map.map_size,
+ "Level Meter", &private->meter_ctl);
+ if (err < 0)
+ return err;
+
+ /* Success; save the pointers in private and don't free them */
+ private->meter_level_map = new_map;
+ private->meter_levels = meter_levels;
+ private->num_meter_slots = map.meter_slots;
+ new_map = NULL;
+ meter_levels = NULL;
+ }
+
+ /* Install the new map */
+ memcpy(private->meter_level_map, tmp_map, map.map_size * sizeof(s16));
+
+ return 0;
+}
+
+/* Set the Level Meter labels */
+static int fcp_ioctl_set_meter_labels(struct usb_mixer_interface *mixer,
+ struct fcp_meter_labels __user *arg)
+{
+ struct fcp_meter_labels labels;
+ struct fcp_data *private = mixer->private_data;
+ unsigned int *tlv_data;
+ unsigned int tlv_size, data_size;
+
+ if (copy_from_user(&labels, arg, sizeof(labels)))
+ return -EFAULT;
+
+ /* Remove existing labels if size is zero */
+ if (!labels.labels_size) {
+
+ /* Clear TLV read/callback bits if labels were present */
+ if (private->meter_labels_tlv) {
+ private->meter_ctl->vd[0].access &=
+ ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
+ snd_ctl_notify(mixer->chip->card,
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &private->meter_ctl->id);
+ }
+
+ kfree(private->meter_labels_tlv);
+ private->meter_labels_tlv = NULL;
+ private->meter_labels_tlv_size = 0;
+
+ return 0;
+ }
+
+ /* Validate size */
+ if (labels.labels_size > 4096)
+ return -EINVAL;
+
+ /* Calculate padded data size */
+ data_size = ALIGN(labels.labels_size, sizeof(unsigned int));
+
+ /* Calculate total TLV size including header */
+ tlv_size = sizeof(unsigned int) * 2 + data_size;
+
+ /* Allocate, set up TLV header, and copy the labels data */
+ tlv_data = kzalloc(tlv_size, GFP_KERNEL);
+ if (!tlv_data)
+ return -ENOMEM;
+ tlv_data[0] = SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS;
+ tlv_data[1] = data_size;
+ if (copy_from_user(&tlv_data[2], arg->labels, labels.labels_size)) {
+ kfree(tlv_data);
+ return -EFAULT;
+ }
+
+ /* Set TLV read/callback bits if labels weren't present */
+ if (!private->meter_labels_tlv) {
+ private->meter_ctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ snd_ctl_notify(mixer->chip->card,
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &private->meter_ctl->id);
+ }
+
+ /* Swap in the new labels */
+ kfree(private->meter_labels_tlv);
+ private->meter_labels_tlv = tlv_data;
+ private->meter_labels_tlv_size = tlv_size;
+
+ return 0;
+}
+
+static int fcp_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct fcp_data *private = mixer->private_data;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ private->file = file;
+
+ return 0;
+}
+
+static int fcp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct fcp_data *private = mixer->private_data;
+ void __user *argp = (void __user *)arg;
+
+ guard(mutex)(&private->mutex);
+
+ switch (cmd) {
+
+ case FCP_IOCTL_PVERSION:
+ return put_user(FCP_HWDEP_VERSION,
+ (int __user *)argp) ? -EFAULT : 0;
+ break;
+
+ case FCP_IOCTL_INIT:
+ return fcp_ioctl_init(mixer, argp);
+
+ case FCP_IOCTL_CMD:
+ if (!private->init)
+ return -EINVAL;
+ return fcp_ioctl_cmd(mixer, argp);
+
+ case FCP_IOCTL_SET_METER_MAP:
+ if (!private->init)
+ return -EINVAL;
+ return fcp_ioctl_set_meter_map(mixer, argp);
+
+ case FCP_IOCTL_SET_METER_LABELS:
+ if (!private->init)
+ return -EINVAL;
+ if (!private->meter_ctl)
+ return -EINVAL;
+ return fcp_ioctl_set_meter_labels(mixer, argp);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ /* not reached */
+}
+
+static long fcp_hwdep_read(struct snd_hwdep *hw, char __user *buf,
+ long count, loff_t *offset)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct fcp_data *private = mixer->private_data;
+ long ret = 0;
+ u32 event;
+
+ if (count < sizeof(event))
+ return -EINVAL;
+
+ ret = wait_event_interruptible(private->notify.queue,
+ private->notify.event);
+ if (ret)
+ return ret;
+
+ 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;
+
+ return sizeof(event);
+}
+
+static __poll_t fcp_hwdep_poll(struct snd_hwdep *hw,
+ struct file *file,
+ poll_table *wait)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct fcp_data *private = mixer->private_data;
+ __poll_t mask = 0;
+
+ poll_wait(file, &private->notify.queue, wait);
+
+ if (private->notify.event)
+ mask |= EPOLLIN | EPOLLRDNORM;
+
+ return mask;
+}
+
+static int fcp_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct fcp_data *private = mixer->private_data;
+
+ if (!private)
+ return 0;
+
+ private->file = NULL;
+
+ return 0;
+}
+
+static int fcp_hwdep_init(struct usb_mixer_interface *mixer)
+{
+ struct snd_hwdep *hw;
+ int err;
+
+ err = snd_hwdep_new(mixer->chip->card, "Focusrite Control", 0, &hw);
+ if (err < 0)
+ return err;
+
+ hw->private_data = mixer;
+ hw->exclusive = 1;
+ hw->ops.open = fcp_hwdep_open;
+ hw->ops.ioctl = fcp_hwdep_ioctl;
+ hw->ops.ioctl_compat = fcp_hwdep_ioctl;
+ hw->ops.read = fcp_hwdep_read;
+ hw->ops.poll = fcp_hwdep_poll;
+ hw->ops.release = fcp_hwdep_release;
+
+ return 0;
+}
+
+/*** Cleanup ***/
+
+static void fcp_cleanup_urb(struct usb_mixer_interface *mixer)
+{
+ if (!mixer->urb)
+ return;
+
+ usb_kill_urb(mixer->urb);
+ kfree(mixer->urb->transfer_buffer);
+ usb_free_urb(mixer->urb);
+ mixer->urb = NULL;
+}
+
+static void fcp_private_free(struct usb_mixer_interface *mixer)
+{
+ struct fcp_data *private = mixer->private_data;
+
+ fcp_cleanup_urb(mixer);
+
+ kfree(private->meter_level_map);
+ kfree(private->meter_levels);
+ kfree(private->meter_labels_tlv);
+ kfree(private);
+ mixer->private_data = NULL;
+}
+
+static void fcp_private_suspend(struct usb_mixer_interface *mixer)
+{
+ fcp_cleanup_urb(mixer);
+}
+
+/*** Callbacks ***/
+
+static void fcp_notify(struct urb *urb)
+{
+ struct usb_mixer_interface *mixer = urb->context;
+ struct fcp_data *private = mixer->private_data;
+ int len = urb->actual_length;
+ int ustatus = urb->status;
+ u32 data;
+
+ if (ustatus != 0 || len != 8)
+ goto requeue;
+
+ data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
+
+ /* Handle command acknowledgement */
+ if (data & FCP_NOTIFY_ACK) {
+ complete(&private->cmd_done);
+ data &= ~FCP_NOTIFY_ACK;
+ }
+
+ if (data) {
+ scoped_guard(spinlock_irqsave, &private->notify.lock) {
+ private->notify.event |= data;
+ }
+
+ wake_up_interruptible(&private->notify.queue);
+ }
+
+requeue:
+ if (ustatus != -ENOENT &&
+ ustatus != -ECONNRESET &&
+ ustatus != -ESHUTDOWN) {
+ urb->dev = mixer->chip->dev;
+ usb_submit_urb(urb, GFP_ATOMIC);
+ } else {
+ complete(&private->cmd_done);
+ }
+}
+
+/* Submit a URB to receive notifications from the device */
+static int fcp_init_notify(struct usb_mixer_interface *mixer)
+{
+ struct usb_device *dev = mixer->chip->dev;
+ struct fcp_data *private = mixer->private_data;
+ unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress);
+ void *transfer_buffer;
+ int err;
+
+ /* Already set up */
+ if (mixer->urb)
+ return 0;
+
+ if (usb_pipe_type_check(dev, pipe))
+ return -EINVAL;
+
+ mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mixer->urb)
+ return -ENOMEM;
+
+ transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL);
+ if (!transfer_buffer) {
+ usb_free_urb(mixer->urb);
+ mixer->urb = NULL;
+ return -ENOMEM;
+ }
+
+ usb_fill_int_urb(mixer->urb, dev, pipe,
+ transfer_buffer, private->wMaxPacketSize,
+ fcp_notify, mixer, private->bInterval);
+
+ init_completion(&private->cmd_done);
+
+ err = usb_submit_urb(mixer->urb, GFP_KERNEL);
+ if (err) {
+ usb_audio_err(mixer->chip,
+ "%s: usb_submit_urb failed: %d\n",
+ __func__, err);
+ kfree(transfer_buffer);
+ usb_free_urb(mixer->urb);
+ mixer->urb = NULL;
+ }
+
+ return err;
+}
+
+/*** Initialisation ***/
+
+static int fcp_init(struct usb_mixer_interface *mixer,
+ void *step0_resp, void *step2_resp)
+{
+ struct fcp_data *private = mixer->private_data;
+ struct usb_device *dev = mixer->chip->dev;
+ int err;
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ FCP_USB_REQ_STEP0,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ 0, private->bInterfaceNumber,
+ step0_resp, private->step0_resp_size);
+ if (err < 0)
+ return err;
+
+ err = fcp_init_notify(mixer);
+ if (err < 0)
+ return err;
+
+ private->seq = 0;
+ private->init = 1;
+
+ err = fcp_usb(mixer, private->init1_opcode, NULL, 0, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = fcp_usb(mixer, private->init2_opcode,
+ NULL, 0, step2_resp, private->step2_resp_size);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int fcp_init_private(struct usb_mixer_interface *mixer)
+{
+ struct fcp_data *private =
+ kzalloc(sizeof(struct fcp_data), GFP_KERNEL);
+
+ if (!private)
+ return -ENOMEM;
+
+ mutex_init(&private->mutex);
+ init_waitqueue_head(&private->notify.queue);
+ spin_lock_init(&private->notify.lock);
+
+ mixer->private_data = private;
+ mixer->private_free = fcp_private_free;
+ mixer->private_suspend = fcp_private_suspend;
+
+ private->mixer = mixer;
+
+ return 0;
+}
+
+/* Look through the interface descriptors for the Focusrite Control
+ * interface (bInterfaceClass = 255 Vendor Specific Class) and set
+ * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
+ * in private
+ */
+static int fcp_find_fc_interface(struct usb_mixer_interface *mixer)
+{
+ struct snd_usb_audio *chip = mixer->chip;
+ struct fcp_data *private = mixer->private_data;
+ struct usb_host_config *config = chip->dev->actconfig;
+ int i;
+
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ struct usb_interface *intf = config->interface[i];
+ struct usb_interface_descriptor *desc =
+ &intf->altsetting[0].desc;
+ struct usb_endpoint_descriptor *epd;
+
+ if (desc->bInterfaceClass != 255)
+ continue;
+
+ epd = get_endpoint(intf->altsetting, 0);
+ private->bInterfaceNumber = desc->bInterfaceNumber;
+ private->bEndpointAddress = usb_endpoint_num(epd);
+ private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
+ private->bInterval = epd->bInterval;
+ return 0;
+ }
+
+ usb_audio_err(chip, "Focusrite vendor-specific interface not found\n");
+ return -EINVAL;
+}
+
+int snd_fcp_init(struct usb_mixer_interface *mixer)
+{
+ struct snd_usb_audio *chip = mixer->chip;
+ int err;
+
+ /* only use UAC_VERSION_2 */
+ if (!mixer->protocol)
+ return 0;
+
+ err = fcp_init_private(mixer);
+ if (err < 0)
+ return err;
+
+ err = fcp_find_fc_interface(mixer);
+ if (err < 0)
+ return err;
+
+ err = fcp_hwdep_init(mixer);
+ if (err < 0)
+ return err;
+
+ usb_audio_info(chip,
+ "Focusrite Control Protocol Driver ready (pid=0x%04x); "
+ "report any issues to "
+ "https://github.com/geoffreybennett/fcp-support/issues",
+ USB_ID_PRODUCT(chip->usb_id));
+
+ return err;
+}
diff --git a/sound/usb/fcp.h b/sound/usb/fcp.h
new file mode 100644
index 000000000000..d58cc7db75b1
--- /dev/null
+++ b/sound/usb/fcp.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USBAUDIO_FCP_H
+#define __USBAUDIO_FCP_H
+
+int snd_fcp_init(struct usb_mixer_interface *mixer);
+
+#endif /* __USBAUDIO_FCP_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 3b45d0ee7693..ec95a063beb1 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -60,6 +60,8 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
/* flag potentially raw DSD capable altsettings */
fp->dsd_raw = true;
+ /* clear special format bit to avoid "unsupported format" msg below */
+ format &= ~UAC2_FORMAT_TYPE_I_RAW_DATA;
}
format <<= 1;
@@ -71,8 +73,11 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
sample_width = as->bBitResolution;
sample_bytes = as->bSubslotSize;
- if (format & UAC3_FORMAT_TYPE_I_RAW_DATA)
+ if (format & UAC3_FORMAT_TYPE_I_RAW_DATA) {
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+ /* clear special format bit to avoid "unsupported format" msg below */
+ format &= ~UAC3_FORMAT_TYPE_I_RAW_DATA;
+ }
format <<= 1;
break;
@@ -80,15 +85,16 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
}
fp->fmt_bits = sample_width;
+ fp->fmt_sz = sample_bytes;
if ((pcm_formats == 0) &&
- (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) {
+ (format == 0 || format == BIT(UAC_FORMAT_TYPE_I_UNDEFINED))) {
/* some devices don't define this correctly... */
usb_audio_info(chip, "%u:%d : format type 0 is detected, processed as PCM\n",
fp->iface, fp->altsetting);
- format = 1 << UAC_FORMAT_TYPE_I_PCM;
+ format = BIT(UAC_FORMAT_TYPE_I_PCM);
}
- if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_PCM)) {
if (((chip->usb_id == USB_ID(0x0582, 0x0016)) ||
/* Edirol SD-90 */
(chip->usb_id == USB_ID(0x0582, 0x000c))) &&
@@ -128,7 +134,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
break;
}
}
- if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_PCM8)) {
/* Dallas DS4201 workaround: it advertises U8 format, but really
supports S8. */
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
@@ -136,15 +142,12 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
else
pcm_formats |= SNDRV_PCM_FMTBIT_U8;
}
- if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_IEEE_FLOAT))
pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- }
- if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_ALAW))
pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
- }
- if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_MULAW))
pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
- }
if (format & ~0x3f) {
usb_audio_info(chip,
"%u:%d : unsupported format bits %#llx\n",
@@ -258,7 +261,8 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
}
/* Jabra Evolve 65 headset */
- if (chip->usb_id == USB_ID(0x0b0e, 0x030b)) {
+ if (chip->usb_id == USB_ID(0x0b0e, 0x030b) ||
+ chip->usb_id == USB_ID(0x0b0e, 0x030c)) {
/* only 48kHz for playback while keeping 16kHz for capture */
if (fp->nr_rates != 1)
return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000);
@@ -306,16 +310,14 @@ static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
struct audioformat *fp,
unsigned int rate)
{
- struct usb_interface *iface;
struct usb_host_interface *alts;
unsigned char *fmt;
unsigned int max_rate;
- iface = usb_ifnum_to_if(chip->dev, fp->iface);
- if (!iface)
+ alts = snd_usb_get_host_interface(chip, fp->iface, fp->altsetting);
+ if (!alts)
return true;
- alts = &iface->altsetting[fp->altset_idx];
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen,
NULL, UAC_FORMAT_TYPE);
if (!fmt)
@@ -324,20 +326,24 @@ static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
if (fmt[0] == 10) { /* bLength */
max_rate = combine_quad(&fmt[6]);
- /* Validate max rate */
- if (max_rate != 48000 &&
- max_rate != 96000 &&
- max_rate != 192000 &&
- max_rate != 384000) {
-
+ switch (max_rate) {
+ case 192000:
+ if (rate == 176400 || rate == 192000)
+ return true;
+ fallthrough;
+ case 96000:
+ if (rate == 88200 || rate == 96000)
+ return true;
+ fallthrough;
+ case 48000:
+ return (rate == 44100 || rate == 48000);
+ default:
usb_audio_info(chip,
"%u:%d : unexpected max rate: %u\n",
fp->iface, fp->altsetting, max_rate);
return true;
}
-
- return rate <= max_rate;
}
return true;
@@ -382,6 +388,10 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
if (chip->usb_id == USB_ID(0x194f, 0x010c) &&
!s1810c_valid_sample_rate(fp, rate))
goto skip_rate;
+ /* Filter out invalid rates on Presonus Studio 1824c */
+ if (chip->usb_id == USB_ID(0x194f, 0x010d) &&
+ !s1810c_valid_sample_rate(fp, rate))
+ goto skip_rate;
/* Filter out invalid rates on Focusrite devices */
if (USB_ID_VENDOR(chip->usb_id) == 0x1235 &&
@@ -548,7 +558,9 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
unsigned char tmp[2], *data;
int nr_triplets, data_size, ret = 0, ret_l6;
int clock = snd_usb_clock_find_source(chip, fp, false);
+ struct usb_host_interface *ctrl_intf;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, fp->iface);
if (clock < 0) {
dev_err(&dev->dev,
"%s(): unable to find clock source (clock %d)\n",
@@ -560,7 +572,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (clock << 8),
tmp, sizeof(tmp));
if (ret < 0) {
@@ -595,7 +607,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (clock << 8),
data, data_size);
if (ret < 0) {
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index bf80e55d013a..497d2b27fb59 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -62,6 +62,7 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype
}
return NULL;
}
+EXPORT_SYMBOL_GPL(snd_usb_find_csint_desc);
/*
* Wrapper for usb_control_msg().
@@ -130,3 +131,37 @@ snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting
return NULL;
return usb_altnum_to_altsetting(iface, altsetting);
}
+
+int snd_usb_add_ctrl_interface_link(struct snd_usb_audio *chip, int ifnum,
+ int ctrlif)
+{
+ struct usb_device *dev = chip->dev;
+ struct usb_host_interface *host_iface;
+
+ if (chip->num_intf_to_ctrl >= MAX_CARD_INTERFACES) {
+ dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n");
+ return -EINVAL;
+ }
+
+ /* find audiocontrol interface */
+ host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
+
+ chip->intf_to_ctrl[chip->num_intf_to_ctrl].interface = ifnum;
+ chip->intf_to_ctrl[chip->num_intf_to_ctrl].ctrl_intf = host_iface;
+ chip->num_intf_to_ctrl++;
+
+ return 0;
+}
+
+struct usb_host_interface *snd_usb_find_ctrl_interface(struct snd_usb_audio *chip,
+ int ifnum)
+{
+ int i;
+
+ for (i = 0; i < chip->num_intf_to_ctrl; ++i)
+ if (chip->intf_to_ctrl[i].interface == ifnum)
+ return chip->intf_to_ctrl[i].ctrl_intf;
+
+ /* Fallback to first audiocontrol interface */
+ return chip->ctrl_intf;
+}
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
index e2b51ec96ec6..0372e050b3dc 100644
--- a/sound/usb/helper.h
+++ b/sound/usb/helper.h
@@ -17,6 +17,12 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
struct usb_host_interface *
snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting);
+int snd_usb_add_ctrl_interface_link(struct snd_usb_audio *chip, int ifnum,
+ int ctrlif);
+
+struct usb_host_interface *snd_usb_find_ctrl_interface(struct snd_usb_audio *chip,
+ int ifnum);
+
/*
* retrieve usb_interface descriptor from the host interface
* (conditional for compatibility with the older API)
@@ -28,9 +34,9 @@ snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting
#define snd_usb_get_speed(dev) ((dev)->speed)
-static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
+static inline int snd_usb_ctrl_intf(struct usb_host_interface *ctrl_intf)
{
- return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
+ return get_iface_desc(ctrl_intf)->bInterfaceNumber;
}
/* in validate.c */
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/implicit.c b/sound/usb/implicit.c
index 4727043fd745..77f06da93151 100644
--- a/sound/usb/implicit.c
+++ b/sound/usb/implicit.c
@@ -57,6 +57,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */
IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */
IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */
+ IMPLICIT_FB_FIXED_DEV(0x19f7, 0x000a, 0x84, 3), /* RODE AI-1 */
IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */
IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */
IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 970c9bdce0b2..9ef4faa006a0 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
@@ -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/capture.h b/sound/usb/line6/capture.h
index 20e05a5eceb4..90572dae134e 100644
--- a/sound/usb/line6/capture.h
+++ b/sound/usb/line6/capture.h
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#ifndef CAPTURE_H
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index f4437015d43a..e97368c31417 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/kernel.h>
@@ -20,7 +20,7 @@
#include "midi.h"
#include "playback.h"
-#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>"
+#define DRIVER_AUTHOR "Markus Grabner <line6@grabner-graz.at>"
#define DRIVER_DESC "Line 6 USB Driver"
/*
@@ -292,20 +292,24 @@ static void line6_data_received(struct urb *urb)
return;
if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
- done =
- line6_midibuf_write(mb, urb->transfer_buffer, 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);
+ 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);
+ }
}
for (;;) {
- done =
- line6_midibuf_read(mb, line6->buffer_message,
- LINE6_MIDI_MESSAGE_MAXLEN,
- LINE6_MIDIBUF_READ_RX);
+ 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;
@@ -623,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 = {
@@ -673,7 +673,7 @@ static int line6_hwdep_init(struct usb_line6 *line6)
err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
if (err < 0)
goto end;
- strcpy(hwdep->name, "config");
+ strscpy(hwdep->name, "config");
hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
hwdep->ops = hwdep_ops;
hwdep->private_data = line6;
@@ -765,9 +765,9 @@ int line6_probe(struct usb_interface *interface,
line6->ifcdev = &interface->dev;
INIT_DELAYED_WORK(&line6->startup_work, line6_startup_work);
- strcpy(card->id, properties->id);
- strcpy(card->driver, driver_name);
- strcpy(card->shortname, properties->name);
+ strscpy(card->id, properties->id);
+ strscpy(card->driver, driver_name);
+ strscpy(card->shortname, properties->name);
sprintf(card->longname, "Line 6 %s at USB %s", properties->name,
dev_name(line6->ifcdev));
card->private_free = line6_destruct;
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index dbb1d90d3647..5736ad4256a5 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#ifndef DRIVER_H
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index 0838632c788e..4731293728e6 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
@@ -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)
@@ -228,8 +222,8 @@ static int snd_line6_new_midi(struct usb_line6 *line6,
return err;
rmidi = *rmidi_ret;
- strcpy(rmidi->id, line6->properties->id);
- strcpy(rmidi->name, line6->properties->name);
+ strscpy(rmidi->id, line6->properties->id);
+ strscpy(rmidi->name, line6->properties->name);
rmidi->info_flags =
SNDRV_RAWMIDI_INFO_OUTPUT |
diff --git a/sound/usb/line6/midi.h b/sound/usb/line6/midi.h
index 918754e79be4..3409c742c173 100644
--- a/sound/usb/line6/midi.h
+++ b/sound/usb/line6/midi.h
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#ifndef MIDI_H
diff --git a/sound/usb/line6/midibuf.c b/sound/usb/line6/midibuf.c
index e7f830f7526c..57fca134b337 100644
--- a/sound/usb/line6/midibuf.c
+++ b/sound/usb/line6/midibuf.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
diff --git a/sound/usb/line6/midibuf.h b/sound/usb/line6/midibuf.h
index 542e8d836f87..1dae5fac9dde 100644
--- a/sound/usb/line6/midibuf.h
+++ b/sound/usb/line6/midibuf.h
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#ifndef MIDIBUF_H
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 6a4af725aedd..f61d9f6cf754 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
@@ -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;
}
@@ -486,7 +484,7 @@ static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret)
if (err < 0)
return err;
pcm = *pcm_ret;
- strcpy(pcm->name, line6->properties->name);
+ strscpy(pcm->name, line6->properties->name);
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
@@ -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/line6/pcm.h b/sound/usb/line6/pcm.h
index 9c683042ff06..a15913bf2a7a 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
/*
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 8233c61e23f1..9f26f66e6792 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
diff --git a/sound/usb/line6/playback.h b/sound/usb/line6/playback.h
index 2ca832c83851..2e0ec0ade0bf 100644
--- a/sound/usb/line6/playback.h
+++ b/sound/usb/line6/playback.h
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#ifndef PLAYBACK_H
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index d173971e5f02..6f948c3e8f9e 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index ffd8c157a281..ea1324c22f46 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -26,7 +26,8 @@ enum {
LINE6_PODX3,
LINE6_PODX3LIVE,
LINE6_PODHD500X,
- LINE6_PODHDDESKTOP
+ LINE6_PODHDDESKTOP,
+ LINE6_PODHDPROX,
};
struct usb_line6_podhd {
@@ -440,6 +441,7 @@ static const struct usb_device_id podhd_id_table[] = {
{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
{ LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X },
{ LINE6_IF_NUM(0x4156, 0), .driver_info = LINE6_PODHDDESKTOP },
+ { LINE6_IF_NUM(0x415A, 0), .driver_info = LINE6_PODHDPROX },
{}
};
@@ -507,7 +509,7 @@ static const struct line6_properties podhd_properties_table[] = {
[LINE6_PODHD500X] = {
.id = "PODHD500X",
.name = "POD HD500X",
- .capabilities = LINE6_CAP_CONTROL
+ .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_HWMON_CTL
| LINE6_CAP_PCM | LINE6_CAP_HWMON,
.altsetting = 1,
.ep_ctrl_r = 0x81,
@@ -528,6 +530,18 @@ static const struct line6_properties podhd_properties_table[] = {
.ep_audio_r = 0x86,
.ep_audio_w = 0x02,
},
+ [LINE6_PODHDPROX] = {
+ .id = "PODHDPROX",
+ .name = "POD HD Pro X",
+ .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO
+ | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+ .altsetting = 1,
+ .ctrl_if = 1,
+ .ep_ctrl_r = 0x81,
+ .ep_ctrl_w = 0x01,
+ .ep_audio_r = 0x86,
+ .ep_audio_w = 0x02,
+ },
};
/*
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index e33df58740a9..68cda7bf330c 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
* Emil Myhrman (emil.myhrman@gmail.com)
*/
@@ -199,7 +199,7 @@ static int snd_toneport_source_info(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item >= size)
uinfo->value.enumerated.item = size - 1;
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
toneport_source_info[uinfo->value.enumerated.item].name);
return 0;
@@ -386,7 +386,7 @@ static int toneport_setup(struct usb_line6_toneport *toneport)
toneport_update_led(toneport);
schedule_delayed_work(&toneport->line6.startup_work,
- msecs_to_jiffies(TONEPORT_PCM_DELAY * 1000));
+ secs_to_jiffies(TONEPORT_PCM_DELAY));
return 0;
}
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
index c2245aa93b08..b2f6637c84b2 100644
--- a/sound/usb/line6/variax.c
+++ b/sound/usb/line6/variax.c
@@ -2,7 +2,7 @@
/*
* Line 6 Linux USB driver
*
- * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
+ * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
*/
#include <linux/slab.h>
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 c1f2e5a03de9..dd8249e75970 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -224,10 +224,10 @@ static void snd_usbmidi_input_data(struct snd_usb_midi_in_endpoint *ep,
#ifdef DUMP_PACKETS
static void dump_urb(const char *type, const u8 *data, int length)
{
- snd_printk(KERN_DEBUG "%s packet: [", type);
+ pr_debug("%s packet: [", type);
for (; length > 0; ++data, --length)
- printk(KERN_CONT " %02x", *data);
- printk(KERN_CONT " ]\n");
+ pr_cont(" %02x", *data);
+ pr_cont(" ]\n");
}
#else
#define dump_urb(type, data, length) /* nothing */
@@ -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)
@@ -339,12 +334,11 @@ static void snd_usbmidi_out_work(struct work_struct *work)
/* called after transfers had been interrupted due to some USB error */
static void snd_usbmidi_error_timer(struct timer_list *t)
{
- struct snd_usb_midi *umidi = from_timer(umidi, t, error_timer);
+ 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 */
@@ -489,16 +482,84 @@ static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep,
/*
* CME protocol: like the standard protocol, but SysEx commands are sent as a
- * single USB packet preceded by a 0x0F byte.
+ * single USB packet preceded by a 0x0F byte, as are system realtime
+ * messages and MIDI Active Sensing.
+ * Also, multiple messages can be sent in the same packet.
*/
static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep,
uint8_t *buffer, int buffer_length)
{
- if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f)
- snd_usbmidi_standard_input(ep, buffer, buffer_length);
- else
- snd_usbmidi_input_data(ep, buffer[0] >> 4,
- &buffer[1], buffer_length - 1);
+ int remaining = buffer_length;
+
+ /*
+ * CME send sysex, song position pointer, system realtime
+ * and active sensing using CIN 0x0f, which in the standard
+ * is only intended for single byte unparsed data.
+ * So we need to interpret these here before sending them on.
+ * By default, we assume single byte data, which is true
+ * for system realtime (midi clock, start, stop and continue)
+ * and active sensing, and handle the other (known) cases
+ * separately.
+ * In contrast to the standard, CME does not split sysex
+ * into multiple 4-byte packets, but lumps everything together
+ * into one. In addition, CME can string multiple messages
+ * together in the same packet; pressing the Record button
+ * on an UF6 sends a sysex message directly followed
+ * by a song position pointer in the same packet.
+ * For it to have any reasonable meaning, a sysex message
+ * needs to be at least 3 bytes in length (0xf0, id, 0xf7),
+ * corresponding to a packet size of 4 bytes, and the ones sent
+ * by CME devices are 6 or 7 bytes, making the packet fragments
+ * 7 or 8 bytes long (six or seven bytes plus preceding CN+CIN byte).
+ * For the other types, the packet size is always 4 bytes,
+ * as per the standard, with the data size being 3 for SPP
+ * and 1 for the others.
+ * Thus all packet fragments are at least 4 bytes long, so we can
+ * skip anything that is shorter; this also conveniantly skips
+ * packets with size 0, which CME devices continuously send when
+ * they have nothing better to do.
+ * Another quirk is that sometimes multiple messages are sent
+ * in the same packet. This has been observed for midi clock
+ * and active sensing i.e. 0x0f 0xf8 0x00 0x00 0x0f 0xfe 0x00 0x00,
+ * but also multiple note ons/offs, and control change together
+ * with MIDI clock. Similarly, some sysex messages are followed by
+ * the song position pointer in the same packet, and occasionally
+ * additionally by a midi clock or active sensing.
+ * We handle this by looping over all data and parsing it along the way.
+ */
+ while (remaining >= 4) {
+ int source_length = 4; /* default */
+
+ if ((buffer[0] & 0x0f) == 0x0f) {
+ int data_length = 1; /* default */
+
+ if (buffer[1] == 0xf0) {
+ /* Sysex: Find EOX and send on whole message. */
+ /* To kick off the search, skip the first
+ * two bytes (CN+CIN and SYSEX (0xf0).
+ */
+ uint8_t *tmp_buf = buffer + 2;
+ int tmp_length = remaining - 2;
+
+ while (tmp_length > 1 && *tmp_buf != 0xf7) {
+ tmp_buf++;
+ tmp_length--;
+ }
+ data_length = tmp_buf - buffer;
+ source_length = data_length + 1;
+ } else if (buffer[1] == 0xf2) {
+ /* Three byte song position pointer */
+ data_length = 3;
+ }
+ snd_usbmidi_input_data(ep, buffer[0] >> 4,
+ &buffer[1], data_length);
+ } else {
+ /* normal channel events */
+ snd_usbmidi_standard_input(ep, buffer, source_length);
+ }
+ buffer += source_length;
+ remaining -= source_length;
+ }
}
/*
@@ -1080,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) {
@@ -1115,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;
}
@@ -1145,7 +1202,7 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
{
struct usbmidi_out_port *port = substream->runtime->private_data;
- cancel_work_sync(&port->ep->work);
+ flush_work(&port->ep->work);
return substream_open(substream, 0, 0);
}
@@ -1454,12 +1511,12 @@ 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);
kfree(umidi);
@@ -1479,13 +1536,12 @@ 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;
+ }
- del_timer_sync(&umidi->error_timer);
+ timer_shutdown_sync(&umidi->error_timer);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i];
@@ -1817,10 +1873,18 @@ static void snd_usbmidi_init_substream(struct snd_usb_midi *umidi,
}
port_info = find_port_info(umidi, number);
- name_format = port_info ? port_info->name :
- (jack_name != default_jack_name ? "%s %s" : "%s %s %d");
- snprintf(substream->name, sizeof(substream->name),
- name_format, umidi->card->shortname, jack_name, number + 1);
+ if (port_info || jack_name == default_jack_name ||
+ strncmp(umidi->card->shortname, jack_name, strlen(umidi->card->shortname)) != 0) {
+ name_format = port_info ? port_info->name :
+ (jack_name != default_jack_name ? "%s %s" : "%s %s %d");
+ snprintf(substream->name, sizeof(substream->name),
+ name_format, umidi->card->shortname, jack_name, number + 1);
+ } else {
+ /* The manufacturer included the iProduct name in the jack
+ * name, do not use both
+ */
+ strscpy(substream->name, jack_name);
+ }
*rsubstream = substream;
}
@@ -2012,16 +2076,15 @@ static int roland_load_get(struct snd_kcontrol *kcontrol,
static int roland_load_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
- struct snd_usb_midi *umidi = kcontrol->private_data;
+ struct snd_usb_midi *umidi = snd_kcontrol_chip(kcontrol);
int changed;
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;
}
@@ -2330,7 +2393,7 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi *umidi,
out_ports, in_ports, &rmidi);
if (err < 0)
return err;
- strcpy(rmidi->name, umidi->card->shortname);
+ strscpy(rmidi->name, umidi->card->shortname);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -2371,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);
}
}
@@ -2411,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);
@@ -2425,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 820d3e4b672a..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 */
@@ -607,12 +599,8 @@ static int parse_group_terminal_block(struct snd_usb_midi2_ump *rmidi,
return 0;
}
- if (ump->info.protocol && ump->info.protocol != protocol)
- usb_audio_info(rmidi->umidi->chip,
- "Overriding preferred MIDI protocol in GTB %d: %x -> %x\n",
- rmidi->usb_block_id, ump->info.protocol,
- protocol);
- ump->info.protocol = protocol;
+ if (!ump->info.protocol)
+ ump->info.protocol = protocol;
protocol_caps = protocol;
switch (desc->bMIDIProtocol) {
@@ -624,13 +612,7 @@ static int parse_group_terminal_block(struct snd_usb_midi2_ump *rmidi,
break;
}
- if (ump->info.protocol_caps && ump->info.protocol_caps != protocol_caps)
- usb_audio_info(rmidi->umidi->chip,
- "Overriding MIDI protocol caps in GTB %d: %x -> %x\n",
- rmidi->usb_block_id, ump->info.protocol_caps,
- protocol_caps);
- ump->info.protocol_caps = protocol_caps;
-
+ ump->info.protocol_caps |= protocol_caps;
return 0;
}
@@ -873,9 +855,25 @@ static int create_gtb_block(struct snd_usb_midi2_ump *rmidi, int dir, int blk)
fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1 |
SNDRV_UMP_BLOCK_IS_LOWSPEED;
+ /* if MIDI 2.0 protocol is supported and yet the GTB shows MIDI 1.0,
+ * treat it as a MIDI 1.0-specific block
+ */
+ if (rmidi->ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2) {
+ switch (desc->bMIDIProtocol) {
+ case USB_MS_MIDI_PROTO_1_0_64:
+ case USB_MS_MIDI_PROTO_1_0_64_JRTS:
+ case USB_MS_MIDI_PROTO_1_0_128:
+ case USB_MS_MIDI_PROTO_1_0_128_JRTS:
+ fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1;
+ break;
+ }
+ }
+
+ snd_ump_update_group_attrs(rmidi->ump);
+
usb_audio_dbg(umidi->chip,
- "Created a UMP block %d from GTB, name=%s\n",
- blk, fb->info.name);
+ "Created a UMP block %d from GTB, name=%s, flags=0x%x\n",
+ blk, fb->info.name, fb->info.flags);
return 0;
}
@@ -1052,7 +1050,8 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi)
fill_ump_ep_name(ump, dev, dev->descriptor.iProduct);
/* fill fallback name */
if (!*ump->info.name)
- sprintf(ump->info.name, "USB MIDI %d", rmidi->index);
+ scnprintf(ump->info.name, sizeof(ump->info.name),
+ "USB MIDI %d", rmidi->index);
/* copy as rawmidi name if not set */
if (!*ump->core.name)
strscpy(ump->core.name, ump->info.name,
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index 4f6b20ed29dd..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);
@@ -1246,8 +1222,8 @@ static int ua101_probe(struct usb_interface *interface,
goto probe_error;
name = usb_id->idProduct == 0x0044 ? "UA-1000" : "UA-101";
- strcpy(card->driver, "UA-101");
- strcpy(card->shortname, name);
+ strscpy(card->driver, "UA-101");
+ strscpy(card->shortname, name);
usb_make_path(ua->dev, usb_path, sizeof(usb_path));
snprintf(ua->card->longname, sizeof(ua->card->longname),
"EDIROL %s (serial %s), %u Hz at %s, %s speed", name,
@@ -1272,7 +1248,7 @@ static int ua101_probe(struct usb_interface *interface,
if (err < 0)
goto probe_error;
ua->pcm->private_data = ua;
- strcpy(ua->pcm->name, name);
+ strscpy(ua->pcm->name, name);
snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops);
snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops);
snd_pcm_set_managed_buffer_all(ua->pcm, SNDRV_DMA_TYPE_VMALLOC,
@@ -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 409fc1164694..3af71d42b9b9 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,
@@ -433,7 +430,7 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
{
int err;
- if (cval->cached & (1 << channel)) {
+ if (cval->cached & BIT(channel)) {
*value = cval->cache_val[index];
return 0;
}
@@ -445,7 +442,7 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
cval->control, channel, err);
return err;
}
- cval->cached |= 1 << channel;
+ cval->cached |= BIT(channel);
cval->cache_val[index] = *value;
return 0;
}
@@ -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,
@@ -522,7 +513,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
int err;
unsigned int read_only = (channel == 0) ?
cval->master_readonly :
- cval->ch_readonly & (1 << (channel - 1));
+ cval->ch_readonly & BIT(channel - 1);
if (read_only) {
usb_audio_dbg(cval->head.mixer->chip,
@@ -536,7 +527,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
value);
if (err < 0)
return err;
- cval->cached |= 1 << channel;
+ cval->cached |= BIT(channel);
cval->cache_val[index] = value;
return 0;
}
@@ -547,7 +538,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
DECLARE_TLV_DB_MINMAX(scale, 0, 0);
if (size < sizeof(scale))
@@ -674,40 +665,40 @@ static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iter
return 0;
switch (iterm->type >> 16) {
case UAC3_SELECTOR_UNIT:
- strcpy(name, "Selector");
+ strscpy(name, "Selector", maxlen);
return 8;
case UAC3_PROCESSING_UNIT:
- strcpy(name, "Process Unit");
+ strscpy(name, "Process Unit", maxlen);
return 12;
case UAC3_EXTENSION_UNIT:
- strcpy(name, "Ext Unit");
+ strscpy(name, "Ext Unit", maxlen);
return 8;
case UAC3_MIXER_UNIT:
- strcpy(name, "Mixer");
+ strscpy(name, "Mixer", maxlen);
return 5;
default:
- return sprintf(name, "Unit %d", iterm->id);
+ return scnprintf(name, maxlen, "Unit %d", iterm->id);
}
}
switch (iterm->type & 0xff00) {
case 0x0100:
- strcpy(name, "PCM");
+ strscpy(name, "PCM", maxlen);
return 3;
case 0x0200:
- strcpy(name, "Mic");
+ strscpy(name, "Mic", maxlen);
return 3;
case 0x0400:
- strcpy(name, "Headset");
+ strscpy(name, "Headset", maxlen);
return 7;
case 0x0500:
- strcpy(name, "Phone");
+ strscpy(name, "Phone", maxlen);
return 5;
}
for (names = iterm_names; names->type; names++) {
if (names->type == iterm->type) {
- strcpy(name, names->name);
+ strscpy(name, names->name, maxlen);
return strlen(names->name);
}
}
@@ -728,7 +719,7 @@ static int get_cluster_channels_v3(struct mixer_build *state, unsigned int clust
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
cluster_id,
- snd_usb_ctrl_intf(state->chip),
+ snd_usb_ctrl_intf(state->mixer->hostif),
&c_header, sizeof(c_header));
if (err < 0)
goto error;
@@ -930,7 +921,7 @@ static int parse_term_uac2_clock_source(struct mixer_build *state,
{
struct uac_clock_source_descriptor *d = p1;
- term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */
+ term->type = UAC2_CLOCK_SOURCE << 16; /* virtual type */
term->id = id;
term->name = d->iClockSource;
return 0;
@@ -1084,6 +1075,21 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
{
struct snd_usb_audio *chip = cval->head.mixer->chip;
+
+ if (chip->quirk_flags & QUIRK_FLAG_MIC_RES_384) {
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 384\n");
+ cval->res = 384;
+ }
+ } else if (chip->quirk_flags & QUIRK_FLAG_MIC_RES_16) {
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 16\n");
+ cval->res = 16;
+ }
+ }
+
switch (chip->usb_id) {
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
@@ -1141,6 +1147,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
break;
+ case USB_ID(0x045e, 0x070f): /* MS LifeChat LX-3000 Headset */
+ if (!strcmp(kctl->id.name, "Speaker Playback Volume")) {
+ usb_audio_info(chip,
+ "set volume quirk for MS LifeChat LX-3000\n");
+ cval->res = 192;
+ }
+ break;
+
case USB_ID(0x0471, 0x0101):
case USB_ID(0x0471, 0x0104):
case USB_ID(0x0471, 0x0105):
@@ -1168,27 +1182,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
break;
- case USB_ID(0x046d, 0x0807): /* Logitech Webcam C500 */
- case USB_ID(0x046d, 0x0808):
- case USB_ID(0x046d, 0x0809):
- case USB_ID(0x046d, 0x0819): /* Logitech Webcam C210 */
- case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */
- case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */
- case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */
- case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
- case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
- case USB_ID(0x046d, 0x0991):
- case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */
- /* Most audio usb devices lie about volume resolution.
- * Most Logitech webcams have res = 384.
- * Probably there is some logitech magic behind this number --fishor
- */
- if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
- usb_audio_info(chip,
- "set resolution quirk: cval->res = 384\n");
- cval->res = 384;
- }
- break;
case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */
if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
strstr(kctl->id.name, "Capture Volume") != NULL) {
@@ -1197,18 +1190,18 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 1;
}
break;
- case USB_ID(0x1224, 0x2a25): /* Jieli Technology USB PHY 2.0 */
- if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ case USB_ID(0x3302, 0x12db): /* MOONDROP Quark2 */
+ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
usb_audio_info(chip,
- "set resolution quirk: cval->res = 16\n");
- cval->res = 16;
+ "set volume quirk for MOONDROP Quark2\n");
+ cval->min = -14208; /* Mute under it */
}
break;
- case USB_ID(0x1bcf, 0x2283): /* NexiGo N930AF FHD Webcam */
- if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ case USB_ID(0x12d1, 0x3a07): /* Huawei Technologies Co., Ltd. CM-Q3 */
+ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
usb_audio_info(chip,
- "set resolution quirk: cval->res = 16\n");
- cval->res = 16;
+ "set volume quirk for Huawei Technologies Co., Ltd. CM-Q3\n");
+ cval->min = -11264; /* Mute under it */
}
break;
}
@@ -1253,7 +1246,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
int minchn = 0;
if (cval->cmask) {
for (i = 0; i < MAX_CHANNELS; i++)
- if (cval->cmask & (1 << i)) {
+ if (cval->cmask & BIT(i)) {
minchn = i + 1;
break;
}
@@ -1358,7 +1351,7 @@ no_res_check:
} else {
idx = 0;
for (i = 0; i < MAX_CHANNELS; i++) {
- if (cval->cmask & (1 << i)) {
+ if (cval->cmask & BIT(i)) {
init_cur_mix_raw(cval, i + 1, idx);
idx++;
}
@@ -1370,11 +1363,24 @@ no_res_check:
#define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL)
+/* get the max value advertised via control API */
+static int get_max_exposed(struct usb_mixer_elem_info *cval)
+{
+ if (!cval->max_exposed) {
+ if (cval->res)
+ cval->max_exposed =
+ DIV_ROUND_UP(cval->max - cval->min, cval->res);
+ else
+ cval->max_exposed = cval->max - cval->min;
+ }
+ return cval->max_exposed;
+}
+
/* get a feature/mixer unit info */
static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
if (cval->val_type == USB_MIXER_BOOLEAN ||
cval->val_type == USB_MIXER_INV_BOOLEAN)
@@ -1382,11 +1388,8 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
else
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = cval->channels;
- if (cval->val_type == USB_MIXER_BOOLEAN ||
- cval->val_type == USB_MIXER_INV_BOOLEAN) {
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- } else {
+ if (cval->val_type != USB_MIXER_BOOLEAN &&
+ cval->val_type != USB_MIXER_INV_BOOLEAN) {
if (!cval->initialized) {
get_min_max_with_quirks(cval, 0, kcontrol);
if (cval->initialized && cval->dBmin >= cval->dBmax) {
@@ -1398,10 +1401,10 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
&kcontrol->id);
}
}
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max =
- DIV_ROUND_UP(cval->max - cval->min, cval->res);
}
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = get_max_exposed(cval);
return 0;
}
@@ -1409,14 +1412,14 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int c, cnt, val, err;
ucontrol->value.integer.value[0] = cval->min;
if (cval->cmask) {
cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (!(cval->cmask & (1 << c)))
+ if (!(cval->cmask & BIT(c)))
continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val);
if (err < 0)
@@ -1441,19 +1444,22 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
+ int max_val = get_max_exposed(cval);
int c, cnt, val, oval, err;
int changed = 0;
if (cval->cmask) {
cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (!(cval->cmask & (1 << c)))
+ if (!(cval->cmask & BIT(c)))
continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval);
if (err < 0)
return filter_error(cval, err);
val = ucontrol->value.integer.value[cnt];
+ if (val < 0 || val > max_val)
+ return -EINVAL;
val = get_abs_value(cval, val);
if (oval != val) {
snd_usb_set_cur_mix_value(cval, c + 1, cnt, val);
@@ -1467,6 +1473,8 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
if (err < 0)
return filter_error(cval, err);
val = ucontrol->value.integer.value[0];
+ if (val < 0 || val > max_val)
+ return -EINVAL;
val = get_abs_value(cval, val);
if (val != oval) {
snd_usb_set_cur_mix_value(cval, 0, 0, val);
@@ -1480,7 +1488,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int val, err;
err = snd_usb_get_cur_mix_value(cval, 0, 0, &val);
@@ -1499,9 +1507,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) {
@@ -1522,8 +1532,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)
@@ -1548,7 +1556,7 @@ error:
static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int ret, val;
ret = get_connector_value(cval, kcontrol->id.name, &val);
@@ -1700,7 +1708,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
} else {
int i, c = 0;
for (i = 0; i < 16; i++)
- if (ctl_mask & (1 << i))
+ if (ctl_mask & BIT(i))
c++;
cval->channels = c;
cval->ch_readonly = readonly_mask;
@@ -2014,6 +2022,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
bmaControls = ftr->bmaControls;
}
+ if (channels > 32) {
+ usb_audio_info(state->chip,
+ "usbmixer: too many channels (%d) in unit %d\n",
+ channels, unitid);
+ return -EINVAL;
+ }
+
/* parse the source unit */
err = parse_audio_unit(state, hdr->bSourceID);
if (err < 0)
@@ -2053,8 +2068,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
- if (mask & (1 << i))
- ch_bits |= (1 << j);
+ if (mask & BIT(i))
+ ch_bits |= BIT(j);
}
/* audio class v1 controls are never read-only */
@@ -2065,7 +2080,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
if (ch_bits & 1)
build_feature_ctl(state, _ftr, ch_bits, control,
&iterm, unitid, 0);
- if (master_bits & (1 << i))
+ if (master_bits & BIT(i))
build_feature_ctl(state, _ftr, 0, control,
&iterm, unitid, 0);
}
@@ -2081,9 +2096,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
if (uac_v2v3_control_is_readable(mask, control)) {
- ch_bits |= (1 << j);
+ ch_bits |= BIT(j);
if (!uac_v2v3_control_is_writeable(mask, control))
- ch_read_only |= (1 << j);
+ ch_read_only |= BIT(j);
}
}
@@ -2174,7 +2189,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
__u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
if (check_matrix_bitmap(c, in_ch, i, num_outs)) {
- cval->cmask |= (1 << i);
+ cval->cmask |= BIT(i);
cval->channels++;
}
}
@@ -2195,7 +2210,8 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
len = get_term_name(state->chip, iterm, kctl->id.name,
sizeof(kctl->id.name), 0);
if (!len)
- len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1);
+ snprintf(kctl->id.name, sizeof(kctl->id.name), "Mixer Source %d", in_ch + 1);
+
append_ctl_name(kctl, " Volume");
usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n",
@@ -2299,7 +2315,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int err, val;
err = get_cur_ctl_value(cval, cval->control << 8, &val);
@@ -2316,13 +2332,15 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol,
static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int val, oval, err;
err = get_cur_ctl_value(cval, cval->control << 8, &oval);
if (err < 0)
return filter_error(cval, err);
val = ucontrol->value.integer.value[0];
+ if (val < 0 || val > get_max_exposed(cval))
+ return -EINVAL;
val = get_abs_value(cval, val);
if (val != oval) {
set_cur_ctl_value(cval, cval->control << 8, val);
@@ -2497,7 +2515,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
if (state->mixer->protocol == UAC_VERSION_1) {
if (!(controls[valinfo->control / 8] &
- (1 << ((valinfo->control % 8) - 1))))
+ BIT((valinfo->control % 8) - 1)))
continue;
} else { /* UAC_VERSION_2/3 */
if (!uac_v2v3_control_is_readable(controls[valinfo->control / 8],
@@ -2649,7 +2667,7 @@ static int parse_audio_extension_unit(struct mixer_build *state, int unitid,
static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
const char **itemlist = (const char **)kcontrol->private_value;
if (snd_BUG_ON(!itemlist))
@@ -2661,7 +2679,7 @@ static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol,
static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int val, err;
err = get_cur_ctl_value(cval, cval->control << 8, &val);
@@ -2678,13 +2696,15 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
int val, oval, err;
err = get_cur_ctl_value(cval, cval->control << 8, &oval);
if (err < 0)
return filter_error(cval, err);
val = ucontrol->value.enumerated.item[0];
+ if (val < 0 || val >= cval->max) /* here cval->max = # elements */
+ return -EINVAL;
val = get_abs_value(cval, val);
if (val != oval) {
set_cur_ctl_value(cval, cval->control << 8, val);
@@ -2797,7 +2817,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
len = get_term_name(state->chip, &iterm, namelist[i],
MAX_ITEM_NAME_LEN, 0);
if (! len)
- sprintf(namelist[i], "Input %u", i);
+ scnprintf(namelist[i], MAX_ITEM_NAME_LEN, "Input %u", i);
}
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
@@ -3066,6 +3086,8 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
int i;
assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
+ if (!assoc)
+ return -EINVAL;
/* Detect BADD capture/playback channels from AS EP descriptors */
for (i = 0; i < assoc->bInterfaceCount; i++) {
@@ -3441,7 +3463,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
case UAC2_CS_CUR:
/* invalidate cache, so the value is read from the device */
if (channel)
- info->cached &= ~(1 << channel);
+ info->cached &= ~BIT(channel);
else /* master channel */
info->cached = 0;
@@ -3562,7 +3584,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
struct usb_mixer_interface *mixer;
int err;
- strcpy(chip->card->mixername, "USB Mixer");
+ strscpy(chip->card->mixername, "USB Mixer");
mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
if (!mixer)
@@ -3677,9 +3699,9 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list)
if (cval->cmask) {
idx = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (!(cval->cmask & (1 << c)))
+ if (!(cval->cmask & BIT(c)))
continue;
- if (cval->cached & (1 << (c + 1))) {
+ if (cval->cached & BIT(c + 1)) {
err = snd_usb_set_cur_mix_value(cval, c + 1, idx,
cval->cache_val[idx]);
if (err < 0)
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index d43895c1ae5c..167fbfcf01ac 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -88,6 +88,7 @@ struct usb_mixer_elem_info {
int channels;
int val_type;
int min, max, res;
+ int max_exposed; /* control API exposes the value in 0..max_exposed */
int dBmin, dBmax;
int cached;
int cache_val[MAX_CHANNELS];
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 23260aa1919d..faac7df1fbcf 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -383,6 +383,13 @@ static const struct usbmix_name_map ms_usb_link_map[] = {
{ 0 } /* terminator */
};
+/* KTMicro USB */
+static struct usbmix_name_map s31b2_0022_map[] = {
+ { 23, "Speaker Playback" },
+ { 18, "Headphone Playback" },
+ { 0 }
+};
+
/* ASUS ROG Zenith II with Realtek ALC1220-VB */
static const struct usbmix_name_map asus_zenith_ii_map[] = {
{ 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */
@@ -621,6 +628,16 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x1b1c, 0x0a42),
.map = corsair_virtuoso_map,
},
+ {
+ /* Corsair HS80 RGB Wireless (wired mode) */
+ .id = USB_ID(0x1b1c, 0x0a6a),
+ .map = corsair_virtuoso_map,
+ },
+ {
+ /* Corsair HS80 RGB Wireless (wireless mode) */
+ .id = USB_ID(0x1b1c, 0x0a6b),
+ .map = corsair_virtuoso_map,
+ },
{ /* Gigabyte TRX40 Aorus Master (rear panel + front mic) */
.id = USB_ID(0x0414, 0xa001),
.map = aorus_master_alc1220vb_map,
@@ -682,6 +699,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x045e, 0x083c),
.map = ms_usb_link_map,
},
+ {
+ /* KTMicro USB */
+ .id = USB_ID(0X31b2, 0x0022),
+ .map = s31b2_0022_map,
+ },
{ 0 } /* terminator */
};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 212b5e6443d8..f7189990dd6a 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -14,8 +14,10 @@
* Przemek Rudy (prudy1@o2.pl)
*/
+#include <linux/bitfield.h>
#include <linux/hid.h>
#include <linux/init.h>
+#include <linux/input.h>
#include <linux/math64.h>
#include <linux/slab.h>
#include <linux/usb.h>
@@ -37,6 +39,7 @@
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
#include "helper.h"
+#include "fcp.h"
struct std_mono_table {
unsigned int unitid, control, cmask;
@@ -53,13 +56,13 @@ struct std_mono_table {
* version, we keep it mono for simplicity.
*/
static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
- unsigned int unitid,
- unsigned int control,
- unsigned int cmask,
- int val_type,
- unsigned int idx_off,
- const char *name,
- snd_kcontrol_tlv_rw_t *tlv_callback)
+ unsigned int unitid,
+ unsigned int control,
+ unsigned int cmask,
+ int val_type,
+ unsigned int idx_off,
+ const char *name,
+ snd_kcontrol_tlv_rw_t *tlv_callback)
{
struct usb_mixer_elem_info *cval;
struct snd_kcontrol *kctl;
@@ -76,7 +79,8 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
cval->idx_off = idx_off;
/* get_min_max() is called only for integer volumes later,
- * so provide a short-cut for booleans */
+ * so provide a short-cut for booleans
+ */
cval->min = 0;
cval->max = 1;
cval->res = 0;
@@ -106,15 +110,16 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
}
static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
- unsigned int unitid,
- unsigned int control,
- unsigned int cmask,
- int val_type,
- const char *name,
- snd_kcontrol_tlv_rw_t *tlv_callback)
+ unsigned int unitid,
+ unsigned int control,
+ unsigned int cmask,
+ int val_type,
+ const char *name,
+ snd_kcontrol_tlv_rw_t *tlv_callback)
{
return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask,
- val_type, 0 /* Offset */, name, tlv_callback);
+ val_type, 0 /* Offset */,
+ name, tlv_callback);
}
/*
@@ -125,9 +130,10 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer,
{
int err;
- while (t->name != NULL) {
+ while (t->name) {
err = snd_create_std_mono_ctl(mixer, t->unitid, t->control,
- t->cmask, t->val_type, t->name, t->tlv_callback);
+ t->cmask, t->val_type, t->name,
+ t->tlv_callback);
if (err < 0)
return err;
t++;
@@ -207,12 +213,11 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb)
if (code == rc->mute_code)
snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
mixer->rc_code = code;
- wmb();
wake_up(&mixer->rc_waitq);
}
static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
- long count, loff_t *offset)
+ long count, loff_t *offset)
{
struct usb_mixer_interface *mixer = hw->private_data;
int err;
@@ -232,7 +237,7 @@ static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
}
static __poll_t snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
- poll_table *wait)
+ poll_table *wait)
{
struct usb_mixer_interface *mixer = hw->private_data;
@@ -283,7 +288,7 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
mixer->rc_setup_packet->wLength = cpu_to_le16(len);
usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0),
- (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
+ (u8 *)mixer->rc_setup_packet, mixer->rc_buffer, len,
snd_usb_soundblaster_remote_complete, mixer);
return 0;
}
@@ -302,27 +307,26 @@ 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,
- usb_sndctrlpipe(chip->dev, 0), 0x24,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- !value, 0, NULL, 0);
+ usb_sndctrlpipe(chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ !value, 0, NULL, 0);
/* USB X-Fi S51 Pro */
if (chip->usb_id == USB_ID(0x041e, 0x30df))
err = snd_usb_ctl_msg(chip->dev,
- usb_sndctrlpipe(chip->dev, 0), 0x24,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- !value, 0, NULL, 0);
+ usb_sndctrlpipe(chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ !value, 0, NULL, 0);
else
err = snd_usb_ctl_msg(chip->dev,
- 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);
+ usb_sndctrlpipe(chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ value, index + 2, NULL, 0);
return err;
}
@@ -375,17 +379,17 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
struct snd_kcontrol_new knew;
/* USB X-Fi S51 doesn't have a CMSS LED */
- if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042) && i == 0)
continue;
/* USB X-Fi S51 Pro doesn't have one either */
- if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df) && i == 0)
continue;
if (i > 1 && /* Live24ext has 2 LEDs only */
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
- break;
+ break;
knew = snd_audigy2nx_control;
knew.name = snd_audigy2nx_led_names[i];
@@ -433,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
@@ -469,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,
@@ -527,6 +527,265 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
&snd_emu0204_control, NULL);
}
+#if IS_REACHABLE(CONFIG_INPUT)
+/*
+ * Sony DualSense controller (PS5) jack detection
+ *
+ * Since this is an UAC 1 device, it doesn't support jack detection.
+ * However, the controller hid-playstation driver reports HP & MIC
+ * insert events through a dedicated input device.
+ */
+
+#define SND_DUALSENSE_JACK_OUT_TERM_ID 3
+#define SND_DUALSENSE_JACK_IN_TERM_ID 4
+
+struct dualsense_mixer_elem_info {
+ struct usb_mixer_elem_info info;
+ struct input_handler ih;
+ struct input_device_id id_table[2];
+ bool connected;
+};
+
+static void snd_dualsense_ih_event(struct input_handle *handle,
+ unsigned int type, unsigned int code,
+ int value)
+{
+ struct dualsense_mixer_elem_info *mei;
+ struct usb_mixer_elem_list *me;
+
+ if (type != EV_SW)
+ return;
+
+ mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih);
+ me = &mei->info.head;
+
+ if ((me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && code == SW_HEADPHONE_INSERT) ||
+ (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && code == SW_MICROPHONE_INSERT)) {
+ mei->connected = !!value;
+ snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &me->kctl->id);
+ }
+}
+
+static bool snd_dualsense_ih_match(struct input_handler *handler,
+ struct input_dev *dev)
+{
+ struct dualsense_mixer_elem_info *mei;
+ struct usb_device *snd_dev;
+ char *input_dev_path, *usb_dev_path;
+ size_t usb_dev_path_len;
+ bool match = false;
+
+ mei = container_of(handler, struct dualsense_mixer_elem_info, ih);
+ snd_dev = mei->info.head.mixer->chip->dev;
+
+ input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
+ if (!input_dev_path) {
+ dev_warn(&snd_dev->dev, "Failed to get input dev path\n");
+ return false;
+ }
+
+ usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL);
+ if (!usb_dev_path) {
+ dev_warn(&snd_dev->dev, "Failed to get USB dev path\n");
+ goto free_paths;
+ }
+
+ /*
+ * Ensure the VID:PID matched input device supposedly owned by the
+ * hid-playstation driver belongs to the actual hardware handled by
+ * the current USB audio device, which implies input_dev_path being
+ * a subpath of usb_dev_path.
+ *
+ * This verification is necessary when there is more than one identical
+ * controller attached to the host system.
+ */
+ usb_dev_path_len = strlen(usb_dev_path);
+ if (usb_dev_path_len >= strlen(input_dev_path))
+ goto free_paths;
+
+ usb_dev_path[usb_dev_path_len] = '/';
+ match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1);
+
+free_paths:
+ kfree(input_dev_path);
+ kfree(usb_dev_path);
+
+ return match;
+}
+
+static int snd_dualsense_ih_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int err;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = handler->name;
+
+ err = input_register_handle(handle);
+ if (err)
+ goto err_free;
+
+ err = input_open_device(handle);
+ if (err)
+ goto err_unregister;
+
+ return 0;
+
+err_unregister:
+ input_unregister_handle(handle);
+err_free:
+ kfree(handle);
+ return err;
+}
+
+static void snd_dualsense_ih_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static void snd_dualsense_ih_start(struct input_handle *handle)
+{
+ struct dualsense_mixer_elem_info *mei;
+ struct usb_mixer_elem_list *me;
+ int status = -1;
+
+ mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih);
+ me = &mei->info.head;
+
+ if (me->id == SND_DUALSENSE_JACK_OUT_TERM_ID &&
+ test_bit(SW_HEADPHONE_INSERT, handle->dev->swbit))
+ status = test_bit(SW_HEADPHONE_INSERT, handle->dev->sw);
+ else if (me->id == SND_DUALSENSE_JACK_IN_TERM_ID &&
+ test_bit(SW_MICROPHONE_INSERT, handle->dev->swbit))
+ status = test_bit(SW_MICROPHONE_INSERT, handle->dev->sw);
+
+ if (status >= 0) {
+ mei->connected = !!status;
+ snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &me->kctl->id);
+ }
+}
+
+static int snd_dualsense_jack_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl);
+
+ ucontrol->value.integer.value[0] = mei->connected;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new snd_dualsense_jack_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_dualsense_jack_get,
+};
+
+static int snd_dualsense_resume_jack(struct usb_mixer_elem_list *list)
+{
+ snd_ctl_notify(list->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &list->kctl->id);
+ return 0;
+}
+
+static void snd_dualsense_mixer_elem_free(struct snd_kcontrol *kctl)
+{
+ struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl);
+
+ if (mei->ih.event)
+ input_unregister_handler(&mei->ih);
+
+ snd_usb_mixer_elem_free(kctl);
+}
+
+static int snd_dualsense_jack_create(struct usb_mixer_interface *mixer,
+ const char *name, bool is_output)
+{
+ struct dualsense_mixer_elem_info *mei;
+ struct input_device_id *idev_id;
+ struct snd_kcontrol *kctl;
+ int err;
+
+ mei = kzalloc(sizeof(*mei), GFP_KERNEL);
+ if (!mei)
+ return -ENOMEM;
+
+ snd_usb_mixer_elem_init_std(&mei->info.head, mixer,
+ is_output ? SND_DUALSENSE_JACK_OUT_TERM_ID :
+ SND_DUALSENSE_JACK_IN_TERM_ID);
+
+ mei->info.head.resume = snd_dualsense_resume_jack;
+ mei->info.val_type = USB_MIXER_BOOLEAN;
+ mei->info.channels = 1;
+ mei->info.min = 0;
+ mei->info.max = 1;
+
+ kctl = snd_ctl_new1(&snd_dualsense_jack_control, mei);
+ if (!kctl) {
+ kfree(mei);
+ return -ENOMEM;
+ }
+
+ strscpy(kctl->id.name, name, sizeof(kctl->id.name));
+ kctl->private_free = snd_dualsense_mixer_elem_free;
+
+ err = snd_usb_mixer_add_control(&mei->info.head, kctl);
+ if (err)
+ return err;
+
+ idev_id = &mei->id_table[0];
+ idev_id->flags = INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT |
+ INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT;
+ idev_id->vendor = USB_ID_VENDOR(mixer->chip->usb_id);
+ idev_id->product = USB_ID_PRODUCT(mixer->chip->usb_id);
+ idev_id->evbit[BIT_WORD(EV_SW)] = BIT_MASK(EV_SW);
+ if (is_output)
+ idev_id->swbit[BIT_WORD(SW_HEADPHONE_INSERT)] = BIT_MASK(SW_HEADPHONE_INSERT);
+ else
+ idev_id->swbit[BIT_WORD(SW_MICROPHONE_INSERT)] = BIT_MASK(SW_MICROPHONE_INSERT);
+
+ mei->ih.event = snd_dualsense_ih_event;
+ mei->ih.match = snd_dualsense_ih_match;
+ mei->ih.connect = snd_dualsense_ih_connect;
+ mei->ih.disconnect = snd_dualsense_ih_disconnect;
+ mei->ih.start = snd_dualsense_ih_start;
+ mei->ih.name = name;
+ mei->ih.id_table = mei->id_table;
+
+ err = input_register_handler(&mei->ih);
+ if (err) {
+ dev_warn(&mixer->chip->dev->dev,
+ "Could not register input handler: %d\n", err);
+ mei->ih.event = NULL;
+ }
+
+ return 0;
+}
+
+static int snd_dualsense_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_dualsense_jack_create(mixer, "Headphone Jack", true);
+ if (err < 0)
+ return err;
+
+ return snd_dualsense_jack_create(mixer, "Headset Mic Jack", false);
+}
+#endif /* IS_REACHABLE(CONFIG_INPUT) */
+
/* ASUS Xonar U1 / U3 controls */
static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
@@ -540,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,
@@ -681,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)
@@ -702,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,
@@ -773,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,
@@ -854,6 +1101,7 @@ static const struct snd_kcontrol_new snd_mbox1_src_switch = {
static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer)
{
int err;
+
err = add_single_ctl_with_resume(mixer, 0,
snd_mbox1_clk_switch_resume,
&snd_mbox1_clk_switch, NULL);
@@ -867,7 +1115,7 @@ static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer)
/* Native Instruments device quirks */
-#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
+#define _MAKE_NI_CONTROL(bRequest, wIndex) ((bRequest) << 16 | (wIndex))
static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl)
@@ -902,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,
@@ -1019,7 +1264,7 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
/* M-Audio FastTrack Ultra quirks */
/* FTU Effect switch (also used by C400/C600) */
static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+ struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[8] = {
"Room 1", "Room 2", "Room 3", "Hall 1",
@@ -1043,7 +1288,7 @@ static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
pval & 0xff00,
- snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
+ snd_usb_ctrl_intf(mixer->hostif) | ((pval & 0xff) << 8),
value, 2);
if (err < 0)
return err;
@@ -1053,7 +1298,7 @@ static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
}
static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
return 0;
@@ -1064,27 +1309,24 @@ 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(chip) | ((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,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
unsigned int pval = list->kctl->private_value;
@@ -1102,7 +1344,7 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
}
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
- int validx, int bUnitID)
+ int validx, int bUnitID)
{
static struct snd_kcontrol_new template = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1139,24 +1381,24 @@ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
for (out = 0; out < 8; out++) {
control = out + 1;
for (in = 0; in < 8; in++) {
- cmask = 1 << in;
+ cmask = BIT(in);
snprintf(name, sizeof(name),
- "AIn%d - Out%d Capture Volume",
- in + 1, out + 1);
+ "AIn%d - Out%d Capture Volume",
+ in + 1, out + 1);
err = snd_create_std_mono_ctl(mixer, id, control,
- cmask, val_type, name,
- &snd_usb_mixer_vol_tlv);
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
for (in = 8; in < 16; in++) {
- cmask = 1 << in;
+ cmask = BIT(in);
snprintf(name, sizeof(name),
- "DIn%d - Out%d Playback Volume",
- in - 7, out + 1);
+ "DIn%d - Out%d Playback Volume",
+ in - 7, out + 1);
err = snd_create_std_mono_ctl(mixer, id, control,
- cmask, val_type, name,
- &snd_usb_mixer_vol_tlv);
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -1215,12 +1457,12 @@ static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
const unsigned int control = 7;
for (ch = 0; ch < 4; ++ch) {
- cmask = 1 << ch;
+ cmask = BIT(ch);
snprintf(name, sizeof(name),
- "Effect Return %d Volume", ch + 1);
+ "Effect Return %d Volume", ch + 1);
err = snd_create_std_mono_ctl(mixer, id, control,
- cmask, val_type, name,
- snd_usb_mixer_vol_tlv);
+ cmask, val_type, name,
+ snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -1239,22 +1481,22 @@ static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
const unsigned int control = 9;
for (ch = 0; ch < 8; ++ch) {
- cmask = 1 << ch;
+ cmask = BIT(ch);
snprintf(name, sizeof(name),
- "Effect Send AIn%d Volume", ch + 1);
+ "Effect Send AIn%d Volume", ch + 1);
err = snd_create_std_mono_ctl(mixer, id, control, cmask,
- val_type, name,
- snd_usb_mixer_vol_tlv);
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
for (ch = 8; ch < 16; ++ch) {
- cmask = 1 << ch;
+ cmask = BIT(ch);
snprintf(name, sizeof(name),
- "Effect Send DIn%d Volume", ch - 7);
+ "Effect Send DIn%d Volume", ch - 7);
err = snd_create_std_mono_ctl(mixer, id, control, cmask,
- val_type, name,
- snd_usb_mixer_vol_tlv);
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -1344,19 +1586,19 @@ static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
for (out = 0; out < num_outs; out++) {
if (chan < num_outs) {
snprintf(name, sizeof(name),
- "PCM%d-Out%d Playback Volume",
- chan + 1, out + 1);
+ "PCM%d-Out%d Playback Volume",
+ chan + 1, out + 1);
} else {
snprintf(name, sizeof(name),
- "In%d-Out%d Playback Volume",
- chan - num_outs + 1, out + 1);
+ "In%d-Out%d Playback Volume",
+ chan - num_outs + 1, out + 1);
}
- cmask = (out == 0) ? 0 : 1 << (out - 1);
+ cmask = (out == 0) ? 0 : BIT(out - 1);
offset = chan * num_outs;
err = snd_create_std_mono_ctl_offset(mixer, id, control,
- cmask, val_type, offset, name,
- &snd_usb_mixer_vol_tlv);
+ cmask, val_type, offset, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -1375,7 +1617,7 @@ static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
const unsigned int cmask = 0;
return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
- name, snd_usb_mixer_vol_tlv);
+ name, snd_usb_mixer_vol_tlv);
}
/* This control needs a volume quirk, see mixer.c */
@@ -1388,7 +1630,7 @@ static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer
const unsigned int cmask = 0;
return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
- name, snd_usb_mixer_vol_tlv);
+ name, snd_usb_mixer_vol_tlv);
}
/* This control needs a volume quirk, see mixer.c */
@@ -1401,7 +1643,7 @@ static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer
const unsigned int cmask = 0;
return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
- name, NULL);
+ name, NULL);
}
static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
@@ -1430,18 +1672,18 @@ static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
for (chan = 0; chan < num_outs + num_ins; chan++) {
if (chan < num_outs) {
snprintf(name, sizeof(name),
- "Effect Send DOut%d",
- chan + 1);
+ "Effect Send DOut%d",
+ chan + 1);
} else {
snprintf(name, sizeof(name),
- "Effect Send AIn%d",
- chan - num_outs + 1);
+ "Effect Send AIn%d",
+ chan - num_outs + 1);
}
- cmask = (chan == 0) ? 0 : 1 << (chan - 1);
+ cmask = (chan == 0) ? 0 : BIT(chan - 1);
err = snd_create_std_mono_ctl(mixer, id, control,
- cmask, val_type, name,
- &snd_usb_mixer_vol_tlv);
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -1476,14 +1718,14 @@ static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer
for (chan = 0; chan < num_outs; chan++) {
snprintf(name, sizeof(name),
- "Effect Return %d",
- chan + 1);
+ "Effect Return %d",
+ chan + 1);
cmask = (chan == 0) ? 0 :
- 1 << (chan + (chan % 2) * num_outs - 1);
+ BIT(chan + (chan % 2) * num_outs - 1);
err = snd_create_std_mono_ctl_offset(mixer, id, control,
- cmask, val_type, offset, name,
- &snd_usb_mixer_vol_tlv);
+ cmask, val_type, offset, name,
+ &snd_usb_mixer_vol_tlv);
if (err < 0)
return err;
}
@@ -1624,7 +1866,7 @@ static const struct std_mono_table ebox44_table[] = {
*
*/
static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
@@ -1632,7 +1874,7 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
}
static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = list->mixer->chip;
@@ -1643,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;
@@ -1653,36 +1895,29 @@ 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,
- usb_rcvctrlpipe(chip->dev, 0),
- UAC_GET_CUR,
- USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
- UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
- ep,
- data,
- sizeof(data));
+ usb_rcvctrlpipe(chip->dev, 0),
+ UAC_GET_CUR,
+ USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
+ UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
+ ep,
+ 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)
@@ -1692,48 +1927,44 @@ 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,
- usb_sndctrlpipe(chip->dev, 0),
- UAC_SET_CUR,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- reg,
- 2,
- NULL,
- 0);
+ usb_sndctrlpipe(chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 2,
+ NULL,
+ 0);
if (err < 0)
- goto end;
+ return err;
reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
reg |= (pval >> 12) & 0x0f;
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,
- 3,
- NULL,
- 0);
- if (err < 0)
- goto end;
-
- end:
- snd_usb_unlock_shutdown(chip);
+ usb_sndctrlpipe(chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 3,
+ NULL,
+ 0);
return err;
}
static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
unsigned int pval, pval_old;
int err;
- pval = pval_old = kcontrol->private_value;
+ pval = kcontrol->private_value;
+ pval_old = pval;
pval &= 0xfffff0f0;
pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
pval |= (ucontrol->value.iec958.status[0] & 0x0f);
@@ -1754,7 +1985,7 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
}
static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.iec958.status[0] = 0x0f;
ucontrol->value.iec958.status[1] = 0xff;
@@ -1765,7 +1996,7 @@ static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
}
static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
@@ -1776,27 +2007,23 @@ 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;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.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);
-
- 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,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
u8 reg;
@@ -1871,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,
@@ -1998,7 +2222,7 @@ static int realtek_hda_get(struct snd_usb_audio *chip, u32 cmd, u32 *value)
static int realtek_ctl_connector_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = cval->head.mixer->chip;
u32 pv = kcontrol->private_value;
u32 node_id = pv & 0xff;
@@ -2007,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)
@@ -2057,7 +2277,8 @@ static int realtek_resume_jack(struct usb_mixer_elem_list *list)
}
static int realtek_add_jack(struct usb_mixer_interface *mixer,
- char *name, u32 val)
+ char *name, u32 val, int unitid,
+ const struct snd_kcontrol_new *kctl_new)
{
struct usb_mixer_elem_info *cval;
struct snd_kcontrol *kctl;
@@ -2065,14 +2286,13 @@ static int realtek_add_jack(struct usb_mixer_interface *mixer,
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return -ENOMEM;
- snd_usb_mixer_elem_init_std(&cval->head, mixer,
- REALTEK_JACK_INTERRUPT_NODE);
+ snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
cval->head.resume = realtek_resume_jack;
cval->val_type = USB_MIXER_BOOLEAN;
cval->channels = 1;
cval->min = 0;
cval->max = 1;
- kctl = snd_ctl_new1(&realtek_connector_ctl_ro, cval);
+ kctl = snd_ctl_new1(kctl_new, cval);
if (!kctl) {
kfree(cval);
return -ENOMEM;
@@ -2102,40 +2322,146 @@ static int dell_dock_mixer_create(struct usb_mixer_interface *mixer)
USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0, NULL, 0);
- err = realtek_add_jack(mixer, "Line Out Jack", REALTEK_LINE1);
+ err = realtek_add_jack(mixer, "Line Out Jack", REALTEK_LINE1,
+ REALTEK_JACK_INTERRUPT_NODE,
+ &realtek_connector_ctl_ro);
if (err < 0)
return err;
- err = realtek_add_jack(mixer, "Headphone Jack", REALTEK_HP_OUT);
+ err = realtek_add_jack(mixer, "Headphone Jack", REALTEK_HP_OUT,
+ REALTEK_JACK_INTERRUPT_NODE,
+ &realtek_connector_ctl_ro);
if (err < 0)
return err;
err = realtek_add_jack(mixer, "Headset Mic Jack",
- REALTEK_HP_OUT | REALTEK_MIC_FLAG);
+ REALTEK_HP_OUT | REALTEK_MIC_FLAG,
+ REALTEK_JACK_INTERRUPT_NODE,
+ &realtek_connector_ctl_ro);
if (err < 0)
return err;
return 0;
}
-static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
+static void dell_dock_init_vol(struct usb_mixer_interface *mixer, int ch, int id)
{
+ struct snd_usb_audio *chip = mixer->chip;
u16 buf = 0;
snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
(UAC_FU_VOLUME << 8) | ch,
- snd_usb_ctrl_intf(chip) | (id << 8),
+ snd_usb_ctrl_intf(mixer->hostif) | (id << 8),
&buf, 2);
}
static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
{
/* fix to 0dB playback volumes */
- dell_dock_init_vol(mixer->chip, 1, 16);
- dell_dock_init_vol(mixer->chip, 2, 16);
- dell_dock_init_vol(mixer->chip, 1, 19);
- dell_dock_init_vol(mixer->chip, 2, 19);
+ dell_dock_init_vol(mixer, 1, 16);
+ dell_dock_init_vol(mixer, 2, 16);
+ dell_dock_init_vol(mixer, 1, 19);
+ dell_dock_init_vol(mixer, 2, 19);
+ return 0;
+}
+
+/*
+ * HP Thunderbolt Dock G2 jack detection
+ *
+ * Similar to the Dell WD15/WD19, but with different commands.
+ */
+
+#define HP_DOCK_JACK_INTERRUPT_NODE 7
+
+#define HP_DOCK_GET 37
+
+#define HP_DOCK_JACK_PRESENCE 0xffb8
+#define HP_DOCK_JACK_PRESENCE_BIT BIT(2)
+
+#define HP_DOCK_MIC_SENSE 0xf753
+#define HP_DOCK_MIC_SENSE_COMPLETE_BIT BIT(4)
+
+#define HP_DOCK_MIC_SENSE_MASK (BIT(2) | BIT(1) | BIT(0))
+/* #define HP_DOCK_MIC_SENSE_PRESENT 0x2 */
+#define HP_DOCK_MIC_SENSE_NOT_PRESENT 0x4
+
+static int hp_dock_ctl_connector_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
+ u32 pv = kcontrol->private_value;
+ bool presence;
+ int err;
+ u8 buf;
+
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return pm.err;
+
+ err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
+ HP_DOCK_GET,
+ USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN,
+ 0, HP_DOCK_JACK_PRESENCE, &buf, sizeof(buf));
+ if (err < 0)
+ return err;
+
+ presence = !(buf & HP_DOCK_JACK_PRESENCE_BIT);
+
+ if (pv && presence) {
+ for (int i = 0; i < 20; i++) {
+ err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
+ HP_DOCK_GET,
+ USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN,
+ 0, HP_DOCK_MIC_SENSE, &buf, sizeof(buf));
+ if (err < 0)
+ return err;
+
+ /* Mic sense is complete, we have a result. */
+ if (buf & HP_DOCK_MIC_SENSE_COMPLETE_BIT)
+ break;
+
+ msleep(100);
+ }
+
+ /*
+ * If we reach the retry limit without mic sense having
+ * completed, buf will contain HP_DOCK_MIC_SENSE_PRESENT,
+ * thus presence remains true even when detection fails.
+ */
+ if ((buf & HP_DOCK_MIC_SENSE_MASK) == HP_DOCK_MIC_SENSE_NOT_PRESENT)
+ presence = false;
+ }
+ ucontrol->value.integer.value[0] = presence;
+ return 0;
+}
+
+static const struct snd_kcontrol_new hp_dock_connector_ctl_ro = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "", /* will be filled later manually */
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ctl_boolean_mono_info,
+ .get = hp_dock_ctl_connector_get,
+};
+
+static int hp_dock_mixer_create(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = realtek_add_jack(mixer, "Headsets Playback Jack", 0,
+ HP_DOCK_JACK_INTERRUPT_NODE,
+ &hp_dock_connector_ctl_ro);
+ if (err < 0)
+ return err;
+
+ err = realtek_add_jack(mixer, "Headset Capture Jack", 1,
+ HP_DOCK_JACK_INTERRUPT_NODE,
+ &hp_dock_connector_ctl_ro);
+ if (err < 0)
+ return err;
+
return 0;
}
+
/* RME Class Compliant device quirks */
#define SND_RME_GET_STATUS1 23
@@ -2150,15 +2476,15 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
#define SND_RME_CLK_FREQMUL_SHIFT 18
#define SND_RME_CLK_FREQMUL_MASK 0x7
#define SND_RME_CLK_SYSTEM(x) \
- ((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK)
+ (((x) >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK)
#define SND_RME_CLK_AES(x) \
- ((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
+ (((x) >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
#define SND_RME_CLK_SPDIF(x) \
- ((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
+ (((x) >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
#define SND_RME_CLK_SYNC(x) \
- ((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK)
+ (((x) >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK)
#define SND_RME_CLK_FREQMUL(x) \
- ((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK)
+ (((x) >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK)
#define SND_RME_CLK_AES_LOCK 0x1
#define SND_RME_CLK_AES_SYNC 0x4
#define SND_RME_CLK_SPDIF_LOCK 0x2
@@ -2167,9 +2493,9 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
#define SND_RME_SPDIF_FORMAT_SHIFT 5
#define SND_RME_BINARY_MASK 0x1
#define SND_RME_SPDIF_IF(x) \
- ((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK)
+ (((x) >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK)
#define SND_RME_SPDIF_FORMAT(x) \
- ((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK)
+ (((x) >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK)
static const u32 snd_rme_rate_table[] = {
32000, 44100, 48000, 50000,
@@ -2178,6 +2504,7 @@ static const u32 snd_rme_rate_table[] = {
256000, 352800, 384000, 400000,
512000, 705600, 768000, 800000
};
+
/* maximum number of items for AES and S/PDIF rates for above table */
#define SND_RME_RATE_IDX_AES_SPDIF_NUM 12
@@ -2217,14 +2544,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,
@@ -2341,22 +2665,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,
@@ -2541,26 +2862,34 @@ enum {
#define SND_BBFPRO_CTL_REG2_PAD_AN1 4
#define SND_BBFPRO_CTL_REG2_PAD_AN2 5
-#define SND_BBFPRO_MIXER_IDX_MASK 0x1ff
+#define SND_BBFPRO_MIXER_MAIN_OUT_CH_OFFSET 992
+#define SND_BBFPRO_MIXER_IDX_MASK 0x3ff
#define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
#define SND_BBFPRO_MIXER_VAL_SHIFT 9
#define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
#define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
+#define SND_BBFPRO_GAIN_CHANNEL_MASK 0x03
+#define SND_BBFPRO_GAIN_CHANNEL_SHIFT 7
+#define SND_BBFPRO_GAIN_VAL_MASK 0x7f
+#define SND_BBFPRO_GAIN_VAL_MIN 0
+#define SND_BBFPRO_GAIN_VAL_MIC_MAX 65
+#define SND_BBFPRO_GAIN_VAL_LINE_MAX 18 // 9db in 0.5db incraments
+
#define SND_BBFPRO_USBREQ_CTL_REG1 0x10
#define SND_BBFPRO_USBREQ_CTL_REG2 0x17
+#define SND_BBFPRO_USBREQ_GAIN 0x1a
#define SND_BBFPRO_USBREQ_MIXER 0x12
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;
@@ -2568,22 +2897,19 @@ static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
usb_idx = 3;
usb_val = value ? 3 : 0;
} else {
- usb_idx = 1 << index;
+ usb_idx = BIT(index);
usb_val = value ? usb_idx : 0;
}
} else {
usb_req = SND_BBFPRO_USBREQ_CTL_REG2;
- usb_idx = 1 << index;
+ usb_idx = BIT(index);
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,
@@ -2695,18 +3021,121 @@ static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
}
+static int snd_bbfpro_gain_update(struct usb_mixer_interface *mixer,
+ u8 channel, u8 gain)
+{
+ struct snd_usb_audio *chip = mixer->chip;
+
+ if (channel < 2) {
+ // XLR preamp: 3-bit fine, 5-bit coarse; special case >60
+ if (gain < 60)
+ gain = ((gain % 3) << 5) | (gain / 3);
+ else
+ gain = ((gain % 6) << 5) | (60 / 3);
+ }
+
+ 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),
+ 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,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int value = kcontrol->private_value & SND_BBFPRO_GAIN_VAL_MASK;
+
+ ucontrol->value.integer.value[0] = value;
+ return 0;
+}
+
+static int snd_bbfpro_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int pv, channel;
+
+ pv = kcontrol->private_value;
+ channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) &
+ SND_BBFPRO_GAIN_CHANNEL_MASK;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = SND_BBFPRO_GAIN_VAL_MIN;
+
+ if (channel < 2)
+ uinfo->value.integer.max = SND_BBFPRO_GAIN_VAL_MIC_MAX;
+ else
+ uinfo->value.integer.max = SND_BBFPRO_GAIN_VAL_LINE_MAX;
+
+ return 0;
+}
+
+static int snd_bbfpro_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int pv, channel, old_value, value, err;
+
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+ struct usb_mixer_interface *mixer = list->mixer;
+
+ pv = kcontrol->private_value;
+ channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) &
+ SND_BBFPRO_GAIN_CHANNEL_MASK;
+ old_value = pv & SND_BBFPRO_GAIN_VAL_MASK;
+ value = ucontrol->value.integer.value[0];
+
+ if (value < SND_BBFPRO_GAIN_VAL_MIN)
+ return -EINVAL;
+
+ if (channel < 2) {
+ if (value > SND_BBFPRO_GAIN_VAL_MIC_MAX)
+ return -EINVAL;
+ } else {
+ if (value > SND_BBFPRO_GAIN_VAL_LINE_MAX)
+ return -EINVAL;
+ }
+
+ if (value == old_value)
+ return 0;
+
+ err = snd_bbfpro_gain_update(mixer, channel, value);
+ if (err < 0)
+ return err;
+
+ kcontrol->private_value =
+ (channel << SND_BBFPRO_GAIN_CHANNEL_SHIFT) | value;
+ return 1;
+}
+
+static int snd_bbfpro_gain_resume(struct usb_mixer_elem_list *list)
+{
+ int pv, channel, value;
+ struct snd_kcontrol *kctl = list->kctl;
+
+ pv = kctl->private_value;
+ channel = (pv >> SND_BBFPRO_GAIN_CHANNEL_SHIFT) &
+ SND_BBFPRO_GAIN_CHANNEL_MASK;
+ value = pv & SND_BBFPRO_GAIN_VAL_MASK;
+
+ return snd_bbfpro_gain_update(list->mixer, channel, value);
+}
+
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.
@@ -2714,15 +3143,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,
@@ -2790,6 +3216,15 @@ static const struct snd_kcontrol_new snd_bbfpro_ctl_control = {
.put = snd_bbfpro_ctl_put
};
+static const struct snd_kcontrol_new snd_bbfpro_gain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .index = 0,
+ .info = snd_bbfpro_gain_info,
+ .get = snd_bbfpro_gain_get,
+ .put = snd_bbfpro_gain_put
+};
+
static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -2813,6 +3248,18 @@ static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg,
&knew, NULL);
}
+static int snd_bbfpro_gain_add(struct usb_mixer_interface *mixer, u8 channel,
+ char *name)
+{
+ struct snd_kcontrol_new knew = snd_bbfpro_gain_control;
+
+ knew.name = name;
+ knew.private_value = channel << SND_BBFPRO_GAIN_CHANNEL_SHIFT;
+
+ return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_gain_resume,
+ &knew, NULL);
+}
+
static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
char *name)
{
@@ -2860,6 +3307,29 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
}
}
+ // Main out volume
+ for (i = 0 ; i < 12 ; ++i) {
+ snprintf(name, sizeof(name), "Main-Out %s", output[i]);
+ // Main outs are offset to 992
+ err = snd_bbfpro_vol_add(mixer,
+ i + SND_BBFPRO_MIXER_MAIN_OUT_CH_OFFSET,
+ name);
+ if (err < 0)
+ return err;
+ }
+
+ // Input gain
+ for (i = 0 ; i < 4 ; ++i) {
+ if (i < 2)
+ snprintf(name, sizeof(name), "Mic-%s Gain", input[i]);
+ else
+ snprintf(name, sizeof(name), "Line-%s Gain", input[i]);
+
+ err = snd_bbfpro_gain_add(mixer, i, name);
+ if (err < 0)
+ return err;
+ }
+
// Control Reg 1
err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
@@ -2926,7 +3396,410 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
}
/*
- * Pioneer DJ DJM Mixers
+ * RME Digiface USB
+ */
+
+#define RME_DIGIFACE_READ_STATUS 17
+#define RME_DIGIFACE_STATUS_REG0L 0
+#define RME_DIGIFACE_STATUS_REG0H 1
+#define RME_DIGIFACE_STATUS_REG1L 2
+#define RME_DIGIFACE_STATUS_REG1H 3
+#define RME_DIGIFACE_STATUS_REG2L 4
+#define RME_DIGIFACE_STATUS_REG2H 5
+#define RME_DIGIFACE_STATUS_REG3L 6
+#define RME_DIGIFACE_STATUS_REG3H 7
+
+#define RME_DIGIFACE_CTL_REG1 16
+#define RME_DIGIFACE_CTL_REG2 18
+
+/* Reg is overloaded, 0-7 for status halfwords or 16 or 18 for control registers */
+#define RME_DIGIFACE_REGISTER(reg, mask) (((reg) << 16) | (mask))
+#define RME_DIGIFACE_INVERT BIT(31)
+
+static int snd_rme_digiface_write_reg(struct snd_kcontrol *kcontrol, int item, u16 mask, u16 val)
+{
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+ struct snd_usb_audio *chip = list->mixer->chip;
+ struct usb_device *dev = chip->dev;
+ int err;
+
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ item,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, mask, NULL, 0);
+ if (err < 0)
+ dev_err(&dev->dev,
+ "unable to issue control set request %d (ret = %d)",
+ item, err);
+ return err;
+}
+
+static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 status[4])
+{
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+ struct snd_usb_audio *chip = list->mixer->chip;
+ struct usb_device *dev = chip->dev;
+ __le32 buf[4];
+ int err;
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ RME_DIGIFACE_READ_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0,
+ buf, sizeof(buf));
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "unable to issue status read request (ret = %d)",
+ err);
+ } else {
+ for (int i = 0; i < ARRAY_SIZE(buf); i++)
+ status[i] = le32_to_cpu(buf[i]);
+ }
+ return err;
+}
+
+static int snd_rme_digiface_get_status_val(struct snd_kcontrol *kcontrol)
+{
+ int err;
+ u32 status[4];
+ bool invert = kcontrol->private_value & RME_DIGIFACE_INVERT;
+ u8 reg = (kcontrol->private_value >> 16) & 0xff;
+ u16 mask = kcontrol->private_value & 0xffff;
+ u16 val;
+
+ err = snd_rme_digiface_read_status(kcontrol, status);
+ if (err < 0)
+ return err;
+
+ switch (reg) {
+ /* Status register halfwords */
+ case RME_DIGIFACE_STATUS_REG0L ... RME_DIGIFACE_STATUS_REG3H:
+ break;
+ case RME_DIGIFACE_CTL_REG1: /* Control register 1, present in halfword 3L */
+ reg = RME_DIGIFACE_STATUS_REG3L;
+ break;
+ case RME_DIGIFACE_CTL_REG2: /* Control register 2, present in halfword 3H */
+ reg = RME_DIGIFACE_STATUS_REG3H;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (reg & 1)
+ val = status[reg >> 1] >> 16;
+ else
+ val = status[reg >> 1] & 0xffff;
+
+ if (invert)
+ val ^= mask;
+
+ return field_get(mask, val);
+}
+
+static int snd_rme_digiface_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int freq = snd_rme_digiface_get_status_val(kcontrol);
+
+ if (freq < 0)
+ return freq;
+ if (freq >= ARRAY_SIZE(snd_rme_rate_table))
+ return -EIO;
+
+ ucontrol->value.integer.value[0] = snd_rme_rate_table[freq];
+ return 0;
+}
+
+static int snd_rme_digiface_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int val = snd_rme_digiface_get_status_val(kcontrol);
+
+ if (val < 0)
+ return val;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int snd_rme_digiface_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ bool invert = kcontrol->private_value & RME_DIGIFACE_INVERT;
+ u8 reg = (kcontrol->private_value >> 16) & 0xff;
+ u16 mask = kcontrol->private_value & 0xffff;
+ u16 val = field_prep(mask, ucontrol->value.enumerated.item[0]);
+
+ if (invert)
+ val ^= mask;
+
+ return snd_rme_digiface_write_reg(kcontrol, reg, mask, val);
+}
+
+static int snd_rme_digiface_current_sync_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = snd_rme_digiface_enum_get(kcontrol, ucontrol);
+
+ /* 7 means internal for current sync */
+ if (ucontrol->value.enumerated.item[0] == 7)
+ ucontrol->value.enumerated.item[0] = 0;
+
+ return ret;
+}
+
+static int snd_rme_digiface_sync_state_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 status[4];
+ int err;
+ bool valid, sync;
+
+ err = snd_rme_digiface_read_status(kcontrol, status);
+ if (err < 0)
+ return err;
+
+ valid = status[0] & BIT(kcontrol->private_value);
+ sync = status[0] & BIT(5 + kcontrol->private_value);
+
+ if (!valid)
+ ucontrol->value.enumerated.item[0] = SND_RME_CLOCK_NOLOCK;
+ else if (!sync)
+ ucontrol->value.enumerated.item[0] = SND_RME_CLOCK_LOCK;
+ else
+ ucontrol->value.enumerated.item[0] = SND_RME_CLOCK_SYNC;
+ return 0;
+}
+
+static int snd_rme_digiface_format_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const format[] = {
+ "ADAT", "S/PDIF"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1,
+ ARRAY_SIZE(format), format);
+}
+
+static int snd_rme_digiface_sync_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const sync_sources[] = {
+ "Internal", "Input 1", "Input 2", "Input 3", "Input 4"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1,
+ ARRAY_SIZE(sync_sources), sync_sources);
+}
+
+static int snd_rme_digiface_rate_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 = 200000;
+ uinfo->value.integer.step = 0;
+ return 0;
+}
+
+static const struct snd_kcontrol_new snd_rme_digiface_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 1 Sync",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_sync_state_info,
+ .get = snd_rme_digiface_sync_state_get,
+ .private_value = 0,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 1 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG0H, BIT(0)) |
+ RME_DIGIFACE_INVERT,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 1 Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_rate_info,
+ .get = snd_rme_digiface_rate_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG1L, GENMASK(3, 0)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 2 Sync",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_sync_state_info,
+ .get = snd_rme_digiface_sync_state_get,
+ .private_value = 1,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 2 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG0L, BIT(13)) |
+ RME_DIGIFACE_INVERT,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 2 Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_rate_info,
+ .get = snd_rme_digiface_rate_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG1L, GENMASK(7, 4)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 3 Sync",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_sync_state_info,
+ .get = snd_rme_digiface_sync_state_get,
+ .private_value = 2,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 3 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG0L, BIT(14)) |
+ RME_DIGIFACE_INVERT,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 3 Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_rate_info,
+ .get = snd_rme_digiface_rate_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG1L, GENMASK(11, 8)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 4 Sync",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_sync_state_info,
+ .get = snd_rme_digiface_sync_state_get,
+ .private_value = 3,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 4 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG0L, GENMASK(15, 12)) |
+ RME_DIGIFACE_INVERT,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input 4 Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_rate_info,
+ .get = snd_rme_digiface_rate_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG1L, GENMASK(3, 0)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Output 1 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .put = snd_rme_digiface_enum_put,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_CTL_REG2, BIT(0)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Output 2 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .put = snd_rme_digiface_enum_put,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_CTL_REG2, BIT(1)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Output 3 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .put = snd_rme_digiface_enum_put,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_CTL_REG2, BIT(3)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Output 4 Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_rme_digiface_format_info,
+ .get = snd_rme_digiface_enum_get,
+ .put = snd_rme_digiface_enum_put,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_CTL_REG2, BIT(4)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Sync Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_rme_digiface_sync_source_info,
+ .get = snd_rme_digiface_enum_get,
+ .put = snd_rme_digiface_enum_put,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_CTL_REG1, GENMASK(2, 0)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Current Sync Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_digiface_sync_source_info,
+ .get = snd_rme_digiface_current_sync_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG0L, GENMASK(12, 10)),
+ },
+ {
+ /*
+ * This is writeable, but it is only set by the PCM rate.
+ * Mixer apps currently need to drive the mixer using raw USB requests,
+ * so they can also change this that way to configure the rate for
+ * stand-alone operation when the PCM is closed.
+ */
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "System Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_rate_info,
+ .get = snd_rme_digiface_rate_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_CTL_REG1, GENMASK(6, 3)),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Current Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_rme_rate_info,
+ .get = snd_rme_digiface_rate_get,
+ .private_value = RME_DIGIFACE_REGISTER(RME_DIGIFACE_STATUS_REG1H, GENMASK(7, 4)),
+ }
+};
+
+static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_rme_digiface_controls); ++i) {
+ err = add_single_ctl_with_resume(mixer, 0,
+ NULL,
+ &snd_rme_digiface_controls[i],
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Pioneer DJ / AlphaTheta DJM Mixers
*
* These devices generally have options for soft-switching the playback and
* capture sources in addition to the recording level. Although different
@@ -2943,17 +3816,26 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
#define SND_DJM_CAP_CDLINE 0x01
#define SND_DJM_CAP_DIGITAL 0x02
#define SND_DJM_CAP_PHONO 0x03
+#define SND_DJM_CAP_PREFADER 0x05
#define SND_DJM_CAP_PFADER 0x06
#define SND_DJM_CAP_XFADERA 0x07
#define SND_DJM_CAP_XFADERB 0x08
#define SND_DJM_CAP_MIC 0x09
#define SND_DJM_CAP_AUX 0x0d
#define SND_DJM_CAP_RECOUT 0x0a
+#define SND_DJM_CAP_RECOUT_NOMIC 0x0e
#define SND_DJM_CAP_NONE 0x0f
+#define SND_DJM_CAP_FXSEND 0x10
#define SND_DJM_CAP_CH1PFADER 0x11
#define SND_DJM_CAP_CH2PFADER 0x12
#define SND_DJM_CAP_CH3PFADER 0x13
#define SND_DJM_CAP_CH4PFADER 0x14
+#define SND_DJM_CAP_EXT1SEND 0x21
+#define SND_DJM_CAP_EXT2SEND 0x22
+#define SND_DJM_CAP_CH1PREFADER 0x31
+#define SND_DJM_CAP_CH2PREFADER 0x32
+#define SND_DJM_CAP_CH3PREFADER 0x33
+#define SND_DJM_CAP_CH4PREFADER 0x34
// Playback types
#define SND_DJM_PB_CH1 0x00
@@ -2979,7 +3861,8 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
#define SND_DJM_900NXS2_IDX 0x3
#define SND_DJM_750MK2_IDX 0x4
#define SND_DJM_450_IDX 0x5
-
+#define SND_DJM_A9_IDX 0x6
+#define SND_DJM_V10_IDX 0x7
#define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \
.name = _name, \
@@ -2992,7 +3875,6 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
.controls = snd_djm_ctls_##suffix, \
.ncontrols = ARRAY_SIZE(snd_djm_ctls_##suffix) }
-
struct snd_djm_device {
const char *name;
const struct snd_djm_ctl *controls;
@@ -3007,7 +3889,7 @@ struct snd_djm_ctl {
u16 wIndex;
};
-static const char *snd_djm_get_label_caplevel(u16 wvalue)
+static const char *snd_djm_get_label_caplevel_common(u16 wvalue)
{
switch (wvalue) {
case 0x0000: return "-19dB";
@@ -3018,6 +3900,20 @@ static const char *snd_djm_get_label_caplevel(u16 wvalue)
}
};
+// Models like DJM-A9 or DJM-V10 have different capture levels than others
+static const char *snd_djm_get_label_caplevel_high(u16 wvalue)
+{
+ switch (wvalue) {
+ case 0x0000: return "+15dB";
+ case 0x0100: return "+12dB";
+ case 0x0200: return "+9dB";
+ case 0x0300: return "+6dB";
+ case 0x0400: return "+3dB";
+ case 0x0500: return "0dB";
+ default: return NULL;
+ }
+};
+
static const char *snd_djm_get_label_cap_common(u16 wvalue)
{
switch (wvalue & 0x00ff) {
@@ -3030,12 +3926,20 @@ static const char *snd_djm_get_label_cap_common(u16 wvalue)
case SND_DJM_CAP_XFADERB: return "Cross Fader B";
case SND_DJM_CAP_MIC: return "Mic";
case SND_DJM_CAP_RECOUT: return "Rec Out";
+ case SND_DJM_CAP_RECOUT_NOMIC: return "Rec Out without Mic";
case SND_DJM_CAP_AUX: return "Aux";
case SND_DJM_CAP_NONE: return "None";
+ case SND_DJM_CAP_FXSEND: return "FX SEND";
+ case SND_DJM_CAP_CH1PREFADER: return "Pre Fader Ch1";
+ case SND_DJM_CAP_CH2PREFADER: return "Pre Fader Ch2";
+ case SND_DJM_CAP_CH3PREFADER: return "Pre Fader Ch3";
+ case SND_DJM_CAP_CH4PREFADER: return "Pre Fader Ch4";
case SND_DJM_CAP_CH1PFADER: return "Post Fader Ch1";
case SND_DJM_CAP_CH2PFADER: return "Post Fader Ch2";
case SND_DJM_CAP_CH3PFADER: return "Post Fader Ch3";
case SND_DJM_CAP_CH4PFADER: return "Post Fader Ch4";
+ case SND_DJM_CAP_EXT1SEND: return "EXT1 SEND";
+ case SND_DJM_CAP_EXT2SEND: return "EXT2 SEND";
default: return NULL;
}
};
@@ -3051,6 +3955,15 @@ static const char *snd_djm_get_label_cap_850(u16 wvalue)
}
};
+static const char *snd_djm_get_label_caplevel(u8 device_idx, u16 wvalue)
+{
+ switch (device_idx) {
+ case SND_DJM_A9_IDX: return snd_djm_get_label_caplevel_high(wvalue);
+ case SND_DJM_V10_IDX: return snd_djm_get_label_caplevel_high(wvalue);
+ default: return snd_djm_get_label_caplevel_common(wvalue);
+ }
+};
+
static const char *snd_djm_get_label_cap(u8 device_idx, u16 wvalue)
{
switch (device_idx) {
@@ -3072,7 +3985,7 @@ static const char *snd_djm_get_label_pb(u16 wvalue)
static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex)
{
switch (windex) {
- case SND_DJM_WINDEX_CAPLVL: return snd_djm_get_label_caplevel(wvalue);
+ case SND_DJM_WINDEX_CAPLVL: return snd_djm_get_label_caplevel(device_idx, wvalue);
case SND_DJM_WINDEX_CAP: return snd_djm_get_label_cap(device_idx, wvalue);
case SND_DJM_WINDEX_PB: return snd_djm_get_label_pb(wvalue);
default: return NULL;
@@ -3083,7 +3996,6 @@ static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex)
static const u16 snd_djm_opts_cap_level[] = {
0x0000, 0x0100, 0x0200, 0x0300 };
-
// DJM-250MK2
static const u16 snd_djm_opts_250mk2_cap1[] = {
0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a };
@@ -3099,16 +4011,15 @@ static const u16 snd_djm_opts_250mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
static const u16 snd_djm_opts_250mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
static const struct snd_djm_ctl snd_djm_ctls_250mk2[] = {
- SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
- SND_DJM_CTL("Ch1 Input", 250mk2_cap1, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch2 Input", 250mk2_cap2, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch3 Input", 250mk2_cap3, 0, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch1 Output", 250mk2_pb1, 0, SND_DJM_WINDEX_PB),
- SND_DJM_CTL("Ch2 Output", 250mk2_pb2, 1, SND_DJM_WINDEX_PB),
- SND_DJM_CTL("Ch3 Output", 250mk2_pb3, 2, SND_DJM_WINDEX_PB)
+ SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", 250mk2_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", 250mk2_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", 250mk2_cap3, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Output 1 Playback Switch", 250mk2_pb1, 0, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Output 2 Playback Switch", 250mk2_pb2, 1, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Output 3 Playback Switch", 250mk2_pb3, 2, SND_DJM_WINDEX_PB)
};
-
// DJM-450
static const u16 snd_djm_opts_450_cap1[] = {
0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a };
@@ -3124,16 +4035,15 @@ static const u16 snd_djm_opts_450_pb2[] = { 0x0200, 0x0201, 0x0204 };
static const u16 snd_djm_opts_450_pb3[] = { 0x0300, 0x0301, 0x0304 };
static const struct snd_djm_ctl snd_djm_ctls_450[] = {
- SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
- SND_DJM_CTL("Ch1 Input", 450_cap1, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch2 Input", 450_cap2, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch3 Input", 450_cap3, 0, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch1 Output", 450_pb1, 0, SND_DJM_WINDEX_PB),
- SND_DJM_CTL("Ch2 Output", 450_pb2, 1, SND_DJM_WINDEX_PB),
- SND_DJM_CTL("Ch3 Output", 450_pb3, 2, SND_DJM_WINDEX_PB)
+ SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", 450_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", 450_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", 450_cap3, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Output 1 Playback Switch", 450_pb1, 0, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Output 2 Playback Switch", 450_pb2, 1, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Output 3 Playback Switch", 450_pb3, 2, SND_DJM_WINDEX_PB)
};
-
// DJM-750
static const u16 snd_djm_opts_750_cap1[] = {
0x0101, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
@@ -3145,14 +4055,13 @@ static const u16 snd_djm_opts_750_cap4[] = {
0x0401, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
static const struct snd_djm_ctl snd_djm_ctls_750[] = {
- SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
- SND_DJM_CTL("Ch1 Input", 750_cap1, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch2 Input", 750_cap2, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch3 Input", 750_cap3, 0, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch4 Input", 750_cap4, 0, SND_DJM_WINDEX_CAP)
+ SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", 750_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", 750_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", 750_cap3, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 4 Capture Switch", 750_cap4, 0, SND_DJM_WINDEX_CAP)
};
-
// DJM-850
static const u16 snd_djm_opts_850_cap1[] = {
0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
@@ -3164,14 +4073,13 @@ static const u16 snd_djm_opts_850_cap4[] = {
0x0400, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
static const struct snd_djm_ctl snd_djm_ctls_850[] = {
- SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
- SND_DJM_CTL("Ch1 Input", 850_cap1, 1, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch2 Input", 850_cap2, 0, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch3 Input", 850_cap3, 0, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch4 Input", 850_cap4, 1, SND_DJM_WINDEX_CAP)
+ SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", 850_cap1, 1, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", 850_cap2, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", 850_cap3, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 4 Capture Switch", 850_cap4, 1, SND_DJM_WINDEX_CAP)
};
-
// DJM-900NXS2
static const u16 snd_djm_opts_900nxs2_cap1[] = {
0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
@@ -3185,12 +4093,12 @@ static const u16 snd_djm_opts_900nxs2_cap5[] = {
0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = {
- SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
- SND_DJM_CTL("Ch1 Input", 900nxs2_cap1, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch2 Input", 900nxs2_cap2, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch3 Input", 900nxs2_cap3, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch4 Input", 900nxs2_cap4, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch5 Input", 900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
+ SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", 900nxs2_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", 900nxs2_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", 900nxs2_cap3, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 4 Capture Switch", 900nxs2_cap4, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 5 Capture Switch", 900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
};
// DJM-750MK2
@@ -3209,19 +4117,91 @@ static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
-
static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = {
- SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
- SND_DJM_CTL("Ch1 Input", 750mk2_cap1, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch2 Input", 750mk2_cap2, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch3 Input", 750mk2_cap3, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch4 Input", 750mk2_cap4, 2, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch5 Input", 750mk2_cap5, 3, SND_DJM_WINDEX_CAP),
- SND_DJM_CTL("Ch1 Output", 750mk2_pb1, 0, SND_DJM_WINDEX_PB),
- SND_DJM_CTL("Ch2 Output", 750mk2_pb2, 1, SND_DJM_WINDEX_PB),
- SND_DJM_CTL("Ch3 Output", 750mk2_pb3, 2, SND_DJM_WINDEX_PB)
+ SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", 750mk2_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", 750mk2_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", 750mk2_cap3, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 4 Capture Switch", 750mk2_cap4, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 5 Capture Switch", 750mk2_cap5, 3, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Output 1 Playback Switch", 750mk2_pb1, 0, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Output 2 Playback Switch", 750mk2_pb2, 1, SND_DJM_WINDEX_PB),
+ SND_DJM_CTL("Output 3 Playback Switch", 750mk2_pb3, 2, SND_DJM_WINDEX_PB)
+};
+
+// DJM-A9
+static const u16 snd_djm_opts_a9_cap_level[] = {
+ 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500 };
+static const u16 snd_djm_opts_a9_cap1[] = {
+ 0x0107, 0x0108, 0x0109, 0x010a, 0x010e,
+ 0x111, 0x112, 0x113, 0x114, 0x0131, 0x132, 0x133, 0x134 };
+static const u16 snd_djm_opts_a9_cap2[] = {
+ 0x0201, 0x0202, 0x0203, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020e };
+static const u16 snd_djm_opts_a9_cap3[] = {
+ 0x0301, 0x0302, 0x0303, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030e };
+static const u16 snd_djm_opts_a9_cap4[] = {
+ 0x0401, 0x0402, 0x0403, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040e };
+static const u16 snd_djm_opts_a9_cap5[] = {
+ 0x0501, 0x0502, 0x0503, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x050a, 0x050e };
+
+static const struct snd_djm_ctl snd_djm_ctls_a9[] = {
+ SND_DJM_CTL("Master Input Level Capture Switch", a9_cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Master Input Capture Switch", a9_cap1, 3, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 1 Capture Switch", a9_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", a9_cap3, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", a9_cap4, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 4 Capture Switch", a9_cap5, 2, SND_DJM_WINDEX_CAP)
+};
+
+// DJM-V10
+static const u16 snd_djm_opts_v10_cap_level[] = {
+ 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500
+};
+
+static const u16 snd_djm_opts_v10_cap1[] = {
+ 0x0103,
+ 0x0100, 0x0102, 0x0106, 0x0110, 0x0107,
+ 0x0108, 0x0109, 0x010a, 0x0121, 0x0122
+};
+
+static const u16 snd_djm_opts_v10_cap2[] = {
+ 0x0200, 0x0202, 0x0206, 0x0210, 0x0207,
+ 0x0208, 0x0209, 0x020a, 0x0221, 0x0222
+};
+
+static const u16 snd_djm_opts_v10_cap3[] = {
+ 0x0303,
+ 0x0300, 0x0302, 0x0306, 0x0310, 0x0307,
+ 0x0308, 0x0309, 0x030a, 0x0321, 0x0322
+};
+
+static const u16 snd_djm_opts_v10_cap4[] = {
+ 0x0403,
+ 0x0400, 0x0402, 0x0406, 0x0410, 0x0407,
+ 0x0408, 0x0409, 0x040a, 0x0421, 0x0422
+};
+
+static const u16 snd_djm_opts_v10_cap5[] = {
+ 0x0500, 0x0502, 0x0506, 0x0510, 0x0507,
+ 0x0508, 0x0509, 0x050a, 0x0521, 0x0522
};
+static const u16 snd_djm_opts_v10_cap6[] = {
+ 0x0603,
+ 0x0600, 0x0602, 0x0606, 0x0610, 0x0607,
+ 0x0608, 0x0609, 0x060a, 0x0621, 0x0622
+};
+
+static const struct snd_djm_ctl snd_djm_ctls_v10[] = {
+ SND_DJM_CTL("Master Input Level Capture Switch", v10_cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+ SND_DJM_CTL("Input 1 Capture Switch", v10_cap1, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 2 Capture Switch", v10_cap2, 2, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 3 Capture Switch", v10_cap3, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 4 Capture Switch", v10_cap4, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 5 Capture Switch", v10_cap5, 0, SND_DJM_WINDEX_CAP),
+ SND_DJM_CTL("Input 6 Capture Switch", v10_cap6, 0, SND_DJM_WINDEX_CAP)
+ // playback channels are fixed and controlled by hardware knobs on the mixer
+};
static const struct snd_djm_device snd_djm_devices[] = {
[SND_DJM_250MK2_IDX] = SND_DJM_DEVICE(250mk2),
@@ -3230,11 +4210,12 @@ static const struct snd_djm_device snd_djm_devices[] = {
[SND_DJM_900NXS2_IDX] = SND_DJM_DEVICE(900nxs2),
[SND_DJM_750MK2_IDX] = SND_DJM_DEVICE(750mk2),
[SND_DJM_450_IDX] = SND_DJM_DEVICE(450),
+ [SND_DJM_A9_IDX] = SND_DJM_DEVICE(a9),
+ [SND_DJM_V10_IDX] = SND_DJM_DEVICE(v10),
};
-
static int snd_djm_controls_info(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_info *info)
+ struct snd_ctl_elem_info *info)
{
unsigned long private_value = kctl->private_value;
u8 device_idx = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
@@ -3253,8 +4234,8 @@ static int snd_djm_controls_info(struct snd_kcontrol *kctl,
info->value.enumerated.item = noptions - 1;
name = snd_djm_get_label(device_idx,
- ctl->options[info->value.enumerated.item],
- ctl->wIndex);
+ ctl->options[info->value.enumerated.item],
+ ctl->wIndex);
if (!name)
return -EINVAL;
@@ -3266,28 +4247,24 @@ 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)
+ 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)
+ 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);
-
- snd_usb_unlock_shutdown(mixer->chip);
- return err;
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err)
+ return pm.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,
@@ -3325,7 +4302,7 @@ static int snd_djm_controls_resume(struct usb_mixer_elem_list *list)
}
static int snd_djm_controls_create(struct usb_mixer_interface *mixer,
- const u8 device_idx)
+ const u8 device_idx)
{
int err, i;
u16 value;
@@ -3344,10 +4321,10 @@ static int snd_djm_controls_create(struct usb_mixer_interface *mixer,
for (i = 0; i < device->ncontrols; i++) {
value = device->controls[i].default_value;
knew.name = device->controls[i].name;
- knew.private_value = (
+ knew.private_value =
((unsigned long)device_idx << SND_DJM_DEVICE_SHIFT) |
(i << SND_DJM_GROUP_SHIFT) |
- value);
+ value;
err = snd_djm_controls_update(mixer, device_idx, i, value);
if (err)
return err;
@@ -3389,6 +4366,13 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_emu0204_controls_create(mixer);
break;
+#if IS_REACHABLE(CONFIG_INPUT)
+ case USB_ID(0x054c, 0x0ce6): /* Sony DualSense controller (PS5) */
+ case USB_ID(0x054c, 0x0df2): /* Sony DualSense Edge controller (PS5) */
+ err = snd_dualsense_controls_create(mixer);
+ break;
+#endif /* IS_REACHABLE(CONFIG_INPUT) */
+
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
err = snd_c400_create_mixer(mixer);
@@ -3414,13 +4398,15 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
break;
case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
- err = snd_nativeinstruments_create_mixer(mixer,
+ err = snd_nativeinstruments_create_mixer(/* checkpatch hack */
+ mixer,
snd_nativeinstruments_ta6_mixers,
ARRAY_SIZE(snd_nativeinstruments_ta6_mixers));
break;
case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */
- err = snd_nativeinstruments_create_mixer(mixer,
+ err = snd_nativeinstruments_create_mixer(/* checkpatch hack */
+ mixer,
snd_nativeinstruments_ta10_mixers,
ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
break;
@@ -3461,6 +4447,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_scarlett2_init(mixer);
break;
+ case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */
+ case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */
+ case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */
+ err = snd_fcp_init(mixer);
+ break;
+
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
break;
@@ -3470,6 +4462,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
break;
err = dell_dock_mixer_init(mixer);
break;
+ case USB_ID(0x0bda, 0x402e): /* Dell WD19 dock */
+ err = dell_dock_mixer_create(mixer);
+ break;
case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */
case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */
@@ -3480,9 +4475,16 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */
err = snd_sc1810_init_mixer(mixer);
break;
+ case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */
+ err = snd_sc1810_init_mixer(mixer);
+ break;
case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
err = snd_bbfpro_controls_create(mixer);
break;
+ case USB_ID(0x2a39, 0x3f8c): /* RME Digiface USB */
+ case USB_ID(0x2a39, 0x3fa0): /* RME Digiface USB (alternate) */
+ err = snd_rme_digiface_controls_create(mixer);
+ break;
case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */
err = snd_djm_controls_create(mixer, SND_DJM_250MK2_IDX);
break;
@@ -3501,6 +4503,15 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
err = snd_djm_controls_create(mixer, SND_DJM_900NXS2_IDX);
break;
+ case USB_ID(0x2b73, 0x003c): /* Pioneer DJ / AlphaTheta DJM-A9 */
+ err = snd_djm_controls_create(mixer, SND_DJM_A9_IDX);
+ break;
+ case USB_ID(0x2b73, 0x0034): /* Pioneer DJ DJM-V10 */
+ err = snd_djm_controls_create(mixer, SND_DJM_V10_IDX);
+ break;
+ case USB_ID(0x03f0, 0x0269): /* HP TB Dock G2 */
+ err = hp_dock_mixer_create(mixer);
+ break;
}
return err;
@@ -3548,7 +4559,8 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl)
{
/* Approximation using 10 ranges based on output measurement on hw v1.2.
- * This seems close to the cubic mapping e.g. alsamixer uses. */
+ * This seems close to the cubic mapping e.g. alsamixer uses.
+ */
static const DECLARE_TLV_DB_RANGE(scale,
0, 1, TLV_DB_MINMAX_ITEM(-5300, -4970),
2, 5, TLV_DB_MINMAX_ITEM(-4710, -4160),
@@ -3577,6 +4589,52 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
}
}
+/*
+ * Some Plantronics headsets have control names that don't meet ALSA naming
+ * standards. This function fixes nonstandard source names. By the time
+ * this function is called the control name should look like one of these:
+ * "source names Playback Volume"
+ * "source names Playback Switch"
+ * "source names Capture Volume"
+ * "source names Capture Switch"
+ * If any of the trigger words are found in the name then the name will
+ * be changed to:
+ * "Headset Playback Volume"
+ * "Headset Playback Switch"
+ * "Headset Capture Volume"
+ * "Headset Capture Switch"
+ * depending on the current suffix.
+ */
+static void snd_fix_plt_name(struct snd_usb_audio *chip,
+ struct snd_ctl_elem_id *id)
+{
+ /* no variant of "Sidetone" should be added to this list */
+ static const char * const trigger[] = {
+ "Earphone", "Microphone", "Receive", "Transmit"
+ };
+ static const char * const suffix[] = {
+ " Playback Volume", " Playback Switch",
+ " Capture Volume", " Capture Switch"
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(trigger); i++)
+ if (strstr(id->name, trigger[i]))
+ goto triggered;
+ usb_audio_dbg(chip, "no change in %s\n", id->name);
+ return;
+
+triggered:
+ for (i = 0; i < ARRAY_SIZE(suffix); i++)
+ if (strstr(id->name, suffix[i])) {
+ usb_audio_dbg(chip, "fixing kctl name %s\n", id->name);
+ snprintf(id->name, sizeof(id->name), "Headset%s",
+ suffix[i]);
+ return;
+ }
+ usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name);
+}
+
void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval, int unitid,
struct snd_kcontrol *kctl)
@@ -3586,13 +4644,25 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
if (unitid == 7 && cval->control == UAC_FU_VOLUME)
snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
break;
+ }
+
/* lowest playback value is muted on some devices */
- case USB_ID(0x0d8c, 0x000c): /* C-Media */
- case USB_ID(0x0d8c, 0x0014): /* C-Media */
- case USB_ID(0x19f7, 0x0003): /* RODE NT-USB */
- 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;
- break;
- }
-}
+ }
+ /* 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))
+ snd_fix_plt_name(mixer->chip, &kctl->id);
+}
diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c
index fac4bbc6b275..6e09e074c0e7 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,
@@ -181,7 +178,7 @@ snd_sc1810c_get_status_field(struct usb_device *dev,
pkt_out.fields[SC1810C_STATE_F1_IDX] = SC1810C_SET_STATE_F1;
pkt_out.fields[SC1810C_STATE_F2_IDX] = SC1810C_SET_STATE_F2;
- ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ ret = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
SC1810C_SET_STATE_REQ,
SC1810C_SET_STATE_REQTYPE,
(*seqnum), 0, &pkt_out, sizeof(pkt_out));
@@ -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",
@@ -551,15 +597,6 @@ int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer)
if (!list_empty(&chip->mixer_list))
return 0;
- dev_info(&dev->dev,
- "Presonus Studio 1810c, device_setup: %u\n", chip->setup);
- if (chip->setup == 1)
- dev_info(&dev->dev, "(8out/18in @ 48kHz)\n");
- else if (chip->setup == 2)
- dev_info(&dev->dev, "(6out/8in @ 192kHz)\n");
- else
- dev_info(&dev->dev, "(8out/14in @ 96kHz)\n");
-
ret = snd_s1810c_init_mixer_maps(chip);
if (ret < 0)
return ret;
@@ -588,8 +625,29 @@ 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;
+ switch (chip->usb_id) {
+ case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */
+ dev_info(&dev->dev,
+ "Presonus Studio 1810c, device_setup: %u\n", chip->setup);
+ if (chip->setup == 1)
+ dev_info(&dev->dev, "(8out/18in @ 48kHz)\n");
+ else if (chip->setup == 2)
+ dev_info(&dev->dev, "(6out/8in @ 192kHz)\n");
+ else
+ dev_info(&dev->dev, "(8out/14in @ 96kHz)\n");
+
+ ret = snd_s1810c_switch_init(mixer, &snd_s1810c_ab_sw);
+ if (ret < 0)
+ return ret;
+
+ break;
+ case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */
+ ret = snd_s1810c_switch_init(mixer, &snd_s1824c_mono_sw);
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+
return ret;
}
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index 0d6e4f15bf77..8babfa3f7c45 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -357,21 +357,21 @@ static int scarlett_ctl_put(struct snd_kcontrol *kctl,
return changed;
}
-static void scarlett_generate_name(int i, char *dst, int offsets[])
+static void scarlett_generate_name(int i, char *dst, size_t size, int offsets[])
{
if (i > offsets[SCARLETT_OFFSET_MIX])
- sprintf(dst, "Mix %c",
- 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1));
+ scnprintf(dst, size, "Mix %c",
+ 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1));
else if (i > offsets[SCARLETT_OFFSET_ADAT])
- sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]);
+ scnprintf(dst, size, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]);
else if (i > offsets[SCARLETT_OFFSET_SPDIF])
- sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]);
+ scnprintf(dst, size, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]);
else if (i > offsets[SCARLETT_OFFSET_ANALOG])
- sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]);
+ scnprintf(dst, size, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]);
else if (i > offsets[SCARLETT_OFFSET_PCM])
- sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]);
+ scnprintf(dst, size, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]);
else
- sprintf(dst, "Off");
+ scnprintf(dst, size, "Off");
}
static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl,
@@ -391,6 +391,7 @@ static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl,
/* generate name dynamically based on item number and offset info */
scarlett_generate_name(uinfo->value.enumerated.item,
uinfo->value.enumerated.name,
+ sizeof(uinfo->value.enumerated.name),
opt->offsets);
return 0;
@@ -460,7 +461,7 @@ static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl,
struct snd_usb_audio *chip = elem->head.mixer->chip;
unsigned char buf[2 * MAX_CHANNELS] = {0, };
int wValue = (elem->control << 8) | elem->idx_off;
- int idx = snd_usb_ctrl_intf(chip) | (elem->head.id << 8);
+ int idx = snd_usb_ctrl_intf(elem->head.mixer->hostif) | (elem->head.id << 8);
int err;
err = snd_usb_ctl_msg(chip->dev,
@@ -876,7 +877,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer,
return err;
break;
case SCARLETT_SWITCH_IMPEDANCE:
- sprintf(mx, "Input %d Impedance Switch", ctl->num);
+ scnprintf(mx, sizeof(mx),
+ "Input %d Impedance Switch", ctl->num);
err = add_new_ctl(mixer, &usb_scarlett_ctl_enum,
scarlett_ctl_enum_resume, 0x01,
0x09, ctl->num, USB_MIXER_S16, 1, mx,
@@ -885,7 +887,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer,
return err;
break;
case SCARLETT_SWITCH_PAD:
- sprintf(mx, "Input %d Pad Switch", ctl->num);
+ scnprintf(mx, sizeof(mx),
+ "Input %d Pad Switch", ctl->num);
err = add_new_ctl(mixer, &usb_scarlett_ctl_enum,
scarlett_ctl_enum_resume, 0x01,
0x0b, ctl->num, USB_MIXER_S16, 1, mx,
@@ -894,7 +897,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer,
return err;
break;
case SCARLETT_SWITCH_GAIN:
- sprintf(mx, "Input %d Gain Switch", ctl->num);
+ scnprintf(mx, sizeof(mx),
+ "Input %d Gain Switch", ctl->num);
err = add_new_ctl(mixer, &usb_scarlett_ctl_enum,
scarlett_ctl_enum_resume, 0x01,
0x08, ctl->num, USB_MIXER_S16, 1, mx,
@@ -960,8 +964,9 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer)
return err;
for (o = 0; o < info->matrix_out; o++) {
- sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1,
- o+'A');
+ scnprintf(mx, sizeof(mx),
+ "Matrix %02d Mix %c Playback Volume", i+1,
+ o+'A');
err = add_new_ctl(mixer, &usb_scarlett_ctl,
scarlett_ctl_resume, 0x3c, 0x00,
(i << 3) + (o & 0x07), USB_MIXER_S16,
@@ -1002,7 +1007,7 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer)
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0), UAC2_CS_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS |
- USB_DIR_OUT, 0x0100, snd_usb_ctrl_intf(mixer->chip) |
+ USB_DIR_OUT, 0x0100, snd_usb_ctrl_intf(mixer->hostif) |
(0x29 << 8), sample_rate_buffer, 4);
if (err < 0)
return err;
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 1150cf104985..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-2023 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
@@ -166,6 +170,7 @@
#include "helper.h"
#include "mixer_scarlett2.h"
+#include "fcp.h"
/* device_setup value to allow turning MSD mode back on */
#define SCARLETT2_MSD_ENABLE 0x02
@@ -173,6 +178,9 @@
/* device_setup value to disable this mixer driver */
#define SCARLETT2_DISABLE 0x04
+/* device_setup value to use the FCP driver instead */
+#define SCARLETT2_USE_FCP_DRIVER 0x08
+
/* some gui mixers can't handle negative ctl values */
#define SCARLETT2_VOLUME_BIAS 127
@@ -360,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.
*
@@ -543,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
};
@@ -810,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 },
}
};
@@ -1079,6 +1106,9 @@ struct scarlett2_device_info {
/* minimum firmware version required */
u16 min_firmware_version;
+ /* has a downloadable device map */
+ u8 has_devmap;
+
/* support for main/alt speaker switching */
u8 has_speaker_switching;
@@ -1170,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];
@@ -1242,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];
@@ -1253,7 +1287,7 @@ struct scarlett2_data {
u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX];
u8 phantom_persistence;
u8 input_select_switch;
- u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX / 2];
+ u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX];
u8 gain[SCARLETT2_INPUT_GAIN_MAX];
u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX];
u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX];
@@ -1284,7 +1318,7 @@ struct scarlett2_data {
struct snd_kcontrol *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX];
struct snd_kcontrol *input_select_ctl;
- struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2];
+ struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
@@ -1773,6 +1807,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
static const struct scarlett2_device_info vocaster_one_info = {
.config_set = &scarlett2_config_set_vocaster,
.min_firmware_version = 1769,
+ .has_devmap = 1,
.phantom_count = 1,
.inputs_per_phantom = 1,
@@ -1783,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 },
@@ -1815,6 +1851,7 @@ static const struct scarlett2_device_info vocaster_one_info = {
static const struct scarlett2_device_info vocaster_two_info = {
.config_set = &scarlett2_config_set_vocaster,
.min_firmware_version = 1769,
+ .has_devmap = 1,
.phantom_count = 2,
.inputs_per_phantom = 1,
@@ -1826,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 },
@@ -1858,6 +1896,7 @@ static const struct scarlett2_device_info vocaster_two_info = {
static const struct scarlett2_device_info solo_gen4_info = {
.config_set = &scarlett2_config_set_gen4_solo,
.min_firmware_version = 2115,
+ .has_devmap = 1,
.level_input_count = 1,
.air_input_count = 1,
@@ -1912,6 +1951,7 @@ static const struct scarlett2_device_info solo_gen4_info = {
static const struct scarlett2_device_info s2i2_gen4_info = {
.config_set = &scarlett2_config_set_gen4_2i2,
.min_firmware_version = 2115,
+ .has_devmap = 1,
.level_input_count = 2,
.air_input_count = 2,
@@ -1966,6 +2006,7 @@ static const struct scarlett2_device_info s2i2_gen4_info = {
static const struct scarlett2_device_info s4i4_gen4_info = {
.config_set = &scarlett2_config_set_gen4_4i4,
.min_firmware_version = 2089,
+ .has_devmap = 1,
.level_input_count = 2,
.air_input_count = 2,
@@ -2264,6 +2305,8 @@ static int scarlett2_get_port_start_num(
#define SCARLETT2_USB_GET_DATA 0x00800000
#define SCARLETT2_USB_SET_DATA 0x00800001
#define SCARLETT2_USB_DATA_CMD 0x00800002
+#define SCARLETT2_USB_INFO_DEVMAP 0x0080000c
+#define SCARLETT2_USB_GET_DEVMAP 0x0080000d
#define SCARLETT2_USB_CONFIG_SAVE 6
@@ -2277,6 +2320,14 @@ static int scarlett2_get_port_start_num(
#define SCARLETT2_SEGMENT_SETTINGS_NAME "App_Settings"
#define SCARLETT2_SEGMENT_FIRMWARE_NAME "App_Upgrade"
+/* Gen 4 device firmware provides access to a base64-encoded
+ * zlib-compressed JSON description of the device's capabilities and
+ * configuration. This device map is made available in
+ * /proc/asound/cardX/device-map.json.zz.b64
+ */
+#define SCARLETT2_DEVMAP_BLOCK_SIZE 1024
+#define SCARLETT2_DEVMAP_FILENAME "device-map.json.zz.b64"
+
/* proprietary request/response format */
struct scarlett2_usb_packet {
__le32 cmd;
@@ -2326,24 +2377,23 @@ 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;
+ const int max_retries = 5;
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 */
@@ -2352,16 +2402,20 @@ static int scarlett2_usb(
if (req_size)
memcpy(req->data, req_data, req_size);
+retry:
err = scarlett2_usb_tx(dev, private->bInterfaceNumber,
req, req_buf_size);
if (err != req_buf_size) {
+ if (err == -EPROTO && ++retries <= max_retries) {
+ msleep(5 * (1 << (retries - 1)));
+ goto retry;
+ }
usb_audio_err(
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,
@@ -2371,8 +2425,7 @@ static int scarlett2_usb(
"%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 */
@@ -2389,17 +2442,14 @@ static int scarlett2_usb(
* 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
@@ -2422,18 +2472,12 @@ static int scarlett2_usb(
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;
}
@@ -3292,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 = {
@@ -3409,7 +3448,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
private->num_autogain_status_texts - 1;
- for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
if (scarlett2_has_config_item(private,
scarlett2_ag_target_configs[i])) {
err = scarlett2_usb_get_config(
@@ -3420,7 +3459,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
}
/* convert from negative dBFS as used by the device */
- for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+ for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
private->ag_targets[i] = -ag_target_values[i];
return 0;
@@ -3439,7 +3478,7 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
scarlett2_set_ctl_access(private->input_select_ctl, val);
if (scarlett2_has_config_item(private,
SCARLETT2_CONFIG_INPUT_LINK_SWITCH))
- for (i = 0; i < info->gain_input_count / 2; i++)
+ for (i = 0; i < info->gain_input_count; i++)
scarlett2_set_ctl_access(private->input_link_ctls[i],
val);
for (i = 0; i < info->gain_input_count; i++)
@@ -3480,7 +3519,7 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
&private->input_select_ctl->id);
if (scarlett2_has_config_item(private,
SCARLETT2_CONFIG_INPUT_LINK_SWITCH))
- for (i = 0; i < info->gain_input_count / 2; i++)
+ for (i = 0; i < info->gain_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
&private->input_link_ctls[i]->id);
for (i = 0; i < info->gain_input_count; i++)
@@ -3560,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(
@@ -3581,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(
@@ -3608,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(
@@ -3633,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(
@@ -3713,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;
@@ -3730,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(
@@ -3741,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(
@@ -3769,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(
@@ -3825,7 +3825,7 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
- int link_count = info->gain_input_count / 2;
+ int link_count = info->gain_input_count;
int err;
private->input_select_updated = 0;
@@ -3847,10 +3847,6 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
if (err < 0)
return err;
- /* simplified because no model yet has link_count > 1 */
- if (private->input_link_switch[0])
- private->input_select_switch = 0;
-
return 0;
}
@@ -3860,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(
@@ -3887,45 +3878,37 @@ static int scarlett2_input_select_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;
-
+ const struct scarlett2_device_info *info = private->info;
int oval, val, err;
- int max_val = private->input_link_switch[0] ? 0 : 1;
- 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];
if (val < 0)
val = 0;
- else if (val > max_val)
- val = max_val;
+ else if (val >= info->gain_input_count)
+ val = info->gain_input_count - 1;
if (oval == val)
- goto unlock;
+ return 0;
private->input_select_switch = val;
- /* Send switch change to the device if inputs not linked */
- if (!private->input_link_switch[0])
- err = scarlett2_usb_set_config(
- mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
- 1, val);
- if (err == 0)
- err = 1;
+ /* Send new value to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ 0, val);
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static int scarlett2_input_select_ctl_info(
@@ -3934,49 +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, j;
- int err;
- char **values = kcalloc(inputs, sizeof(char *), GFP_KERNEL);
+ int i, err;
+ 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
- * Linked inputs have one value for the pair
- */
- for (i = 0, j = 0; i < inputs; i++) {
- if (private->input_link_switch[i / 2]) {
- values[j++] = kasprintf(
- GFP_KERNEL, "Input %d-%d", i + 1, i + 2);
- i++;
- } else {
- values[j++] = kasprintf(
- GFP_KERNEL, "Input %d", i + 1);
+ /* 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 clear;
}
}
- err = snd_ctl_enum_info(uinfo, 1, j,
+ 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;
}
@@ -4002,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(
@@ -4026,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(
@@ -4054,47 +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;
- /* Notify of change in input select options available */
- snd_ctl_notify(mixer->chip->card,
- SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
- &private->input_select_ctl->id);
- private->input_select_updated = 1;
-
- /* Send switch change to the device
- * Link for channels 1-2 is at index 1
- * No device yet has more than 2 channels linked
- */
err = scarlett2_usb_set_config(
- mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index + 1, val);
- if (err == 0)
- err = 1;
+ mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index, val);
-unlock:
- mutex_unlock(&private->data_mutex);
- return err;
+ return err < 0 ? err : 1;
}
static const struct snd_kcontrol_new scarlett2_input_link_ctl = {
@@ -4130,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;
@@ -4147,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,
@@ -4158,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,
@@ -4186,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 = {
@@ -4254,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,
@@ -4282,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 = {
@@ -4348,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(
@@ -4370,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;
@@ -4392,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(
@@ -4484,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(
@@ -4512,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)
@@ -4553,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,
@@ -4581,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(
@@ -4683,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,
@@ -4711,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 = {
@@ -4855,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 = {
@@ -4916,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,
@@ -4941,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,
@@ -4972,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;
@@ -5002,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 = {
@@ -5041,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,
@@ -5069,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 = {
@@ -5130,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,
@@ -5157,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(
@@ -5202,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] = {
@@ -5260,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,
@@ -5287,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 = {
@@ -5381,33 +5188,29 @@ 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;
- const struct compressor_param *param = &compressor_params[param_index];
-
scaled_val = val << param->scale_bits;
/* Send change to the device */
@@ -5419,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(
@@ -5487,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;
@@ -5508,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(
@@ -5522,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;
@@ -5543,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 = {
@@ -5613,6 +5397,8 @@ static int scarlett2_update_filter_values(struct usb_mixer_interface *mixer)
info->peq_flt_total_count *
SCARLETT2_BIQUAD_COEFFS,
peq_flt_values);
+ if (err < 0)
+ return err;
for (i = 0, dst_idx = 0; i < info->dsp_input_count; i++) {
src_idx = i *
@@ -5667,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++) {
@@ -5691,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++)
@@ -5702,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(
@@ -5724,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 /
@@ -5734,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++) {
@@ -5754,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++)
@@ -5765,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(
@@ -5830,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,
@@ -5858,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;
@@ -5885,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 = {
@@ -6001,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,
@@ -6027,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;
@@ -6057,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 = {
@@ -6094,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 = {
@@ -6212,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
@@ -6296,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;
+ guard(mutex)(&private->data_mutex);
- mutex_lock(&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;
@@ -6319,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)
@@ -6334,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 = {
@@ -6382,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(
@@ -6409,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;
@@ -6432,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 = {
@@ -6474,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;
@@ -6498,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 = {
@@ -6551,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,
@@ -6579,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);
@@ -6614,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 = {
@@ -6627,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)
@@ -6659,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.
*/
@@ -6914,10 +6675,9 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
if (scarlett2_has_config_item(private,
SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) {
- for (i = 0; i < info->gain_input_count / 2; i++) {
+ for (i = 0; i < info->gain_input_count; i++) {
scnprintf(s, sizeof(s),
- "Line In %d-%d Link Capture Switch",
- (i * 2) + 1, (i * 2) + 2);
+ "Line In %d Link Capture Switch", i + 1);
err = scarlett2_add_new_ctl(
mixer, &scarlett2_input_link_ctl,
i, 1, s, &private->input_link_ctls[i]);
@@ -7029,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,
@@ -7056,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],
@@ -7072,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(
@@ -7168,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(
@@ -7195,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(
@@ -7272,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 = {
@@ -7399,13 +7130,15 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl,
if (port_type == SCARLETT2_PORT_TYPE_MIX &&
item >= private->num_mix_out)
- sprintf(uinfo->value.enumerated.name,
- port->dsp_src_descr,
- item - private->num_mix_out + 1);
+ scnprintf(uinfo->value.enumerated.name,
+ sizeof(uinfo->value.enumerated.name),
+ port->dsp_src_descr,
+ item - private->num_mix_out + 1);
else
- sprintf(uinfo->value.enumerated.name,
- port->src_descr,
- item + port->src_num_offset);
+ scnprintf(uinfo->value.enumerated.name,
+ sizeof(uinfo->value.enumerated.name),
+ port->src_descr,
+ item + port->src_num_offset);
return 0;
}
@@ -7422,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,
@@ -7450,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 = {
@@ -7550,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++) {
@@ -7575,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 = {
@@ -7620,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 = {
@@ -7691,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;
@@ -7713,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 = {
@@ -7778,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(
@@ -7853,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,
@@ -7880,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(
@@ -8000,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 = {
@@ -8244,7 +7925,7 @@ static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
&private->input_select_ctl->id);
- for (i = 0; i < info->gain_input_count / 2; i++)
+ for (i = 0; i < info->gain_input_count; i++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->input_link_ctls[i]->id);
}
@@ -8577,8 +8258,7 @@ static int scarlett2_find_fc_interface(struct usb_device *dev,
epd = get_endpoint(intf->altsetting, 0);
private->bInterfaceNumber = desc->bInterfaceNumber;
- private->bEndpointAddress = epd->bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
+ private->bEndpointAddress = usb_endpoint_num(epd);
private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
private->bInterval = epd->bInterval;
return 0;
@@ -9130,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 */
@@ -9404,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)
@@ -9450,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,
@@ -9481,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 -
@@ -9516,7 +9189,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
SCARLETT2_FLASH_BLOCK_SIZE;
if (count < 0 || *offset < 0 || *offset + count >= flash_size)
- return -EINVAL;
+ return -ENOSPC;
if (!count)
return 0;
@@ -9535,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)
@@ -9589,6 +9256,111 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer)
return 0;
}
+/*** device-map file ***/
+
+static ssize_t scarlett2_devmap_read(
+ struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char __user *buf,
+ size_t count,
+ loff_t pos)
+{
+ struct usb_mixer_interface *mixer = entry->private_data;
+ u8 *resp_buf __free(kfree) = NULL;
+ const size_t block_size = SCARLETT2_DEVMAP_BLOCK_SIZE;
+ size_t copied = 0;
+
+ if (pos >= entry->size)
+ return 0;
+
+ if (pos + count > entry->size)
+ count = entry->size - pos;
+
+ resp_buf = kmalloc(block_size, GFP_KERNEL);
+ if (!resp_buf)
+ return -ENOMEM;
+
+ while (count > 0) {
+ /* SCARLETT2_USB_GET_DEVMAP reads only on block boundaries,
+ * so we need to read a whole block and copy the requested
+ * chunk to userspace.
+ */
+
+ __le32 req;
+ int err;
+
+ /* offset within the block that we're reading */
+ size_t offset = pos % block_size;
+
+ /* read_size is block_size except for the last block */
+ size_t block_start = pos - offset;
+ size_t read_size = min_t(size_t,
+ block_size,
+ entry->size - block_start);
+
+ /* size of the chunk to copy to userspace */
+ size_t copy_size = min_t(size_t, count, read_size - offset);
+
+ /* request the block */
+ 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)
+ return copied ? copied : err;
+
+ if (copy_to_user(buf, resp_buf + offset, copy_size))
+ return -EFAULT;
+
+ buf += copy_size;
+ pos += copy_size;
+ copied += copy_size;
+ count -= copy_size;
+ }
+
+ return copied;
+}
+
+static const struct snd_info_entry_ops scarlett2_devmap_ops = {
+ .read = scarlett2_devmap_read,
+};
+
+static int scarlett2_devmap_init(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ __le16 config_len_buf[2];
+ int config_len;
+ struct snd_info_entry *entry;
+ int err;
+
+ /* If the device doesn't support the DEVMAP commands, don't
+ * create the /proc/asound/cardX/scarlett.json.zlib entry
+ */
+ if (!info->has_devmap)
+ return 0;
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_DEVMAP,
+ NULL, 0, &config_len_buf, sizeof(config_len_buf));
+ if (err < 0)
+ return err;
+
+ config_len = le16_to_cpu(config_len_buf[1]);
+
+ err = snd_card_proc_new(card, SCARLETT2_DEVMAP_FILENAME, &entry);
+ if (err < 0)
+ return err;
+
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->private_data = mixer;
+ entry->c.ops = &scarlett2_devmap_ops;
+ entry->size = config_len;
+ entry->mode = S_IFREG | 0444;
+
+ return 0;
+}
+
int snd_scarlett2_init(struct usb_mixer_interface *mixer)
{
struct snd_usb_audio *chip = mixer->chip;
@@ -9599,6 +9371,10 @@ int snd_scarlett2_init(struct usb_mixer_interface *mixer)
if (!mixer->protocol)
return 0;
+ /* check if the user wants to use the FCP driver instead */
+ if (chip->setup & SCARLETT2_USE_FCP_DRIVER)
+ return snd_fcp_init(mixer);
+
/* find entry in scarlett2_devices */
entry = get_scarlett2_device_entry(mixer);
if (!entry) {
@@ -9639,11 +9415,20 @@ int snd_scarlett2_init(struct usb_mixer_interface *mixer)
}
err = scarlett2_hwdep_init(mixer);
- if (err < 0)
+ if (err < 0) {
usb_audio_err(mixer->chip,
"Error creating %s hwdep device: %d",
entry->series_name,
err);
+ return err;
+ }
+
+ err = scarlett2_devmap_init(mixer);
+ if (err < 0)
+ usb_audio_err(mixer->chip,
+ "Error creating %s devmap entry: %d",
+ entry->series_name,
+ err);
return err;
}
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
index 6eb7d93b358d..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;
}
@@ -180,7 +179,7 @@ static int snd_us16x08_route_info(struct snd_kcontrol *kcontrol,
static int snd_us16x08_route_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
int index = ucontrol->id.index;
/* route has no bias */
@@ -192,7 +191,7 @@ static int snd_us16x08_route_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
int index = ucontrol->id.index;
char buf[sizeof(route_msg)];
@@ -249,7 +248,7 @@ static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol,
static int snd_us16x08_master_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
int index = ucontrol->id.index;
ucontrol->value.integer.value[0] = elem->cache_val[index];
@@ -260,7 +259,7 @@ static int snd_us16x08_master_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
char buf[sizeof(mix_msg_out)];
int val, err;
@@ -297,7 +296,7 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol,
static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
char buf[sizeof(mix_msg_out)];
int val, err = 0;
@@ -338,7 +337,7 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
switch (elem->head.id) {
case SND_US16X08_ID_BUSS_OUT:
@@ -359,7 +358,7 @@ static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_channel_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
int index = ucontrol->id.index;
ucontrol->value.integer.value[0] = elem->cache_val[index];
@@ -370,7 +369,7 @@ static int snd_us16x08_channel_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
char buf[sizeof(mix_msg_in)];
int val, err;
@@ -417,7 +416,7 @@ static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol,
static int snd_us16x08_comp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_us16x08_comp_store *store = elem->private_data;
int index = ucontrol->id.index;
int val_idx = COMP_STORE_IDX(elem->head.id);
@@ -430,7 +429,7 @@ static int snd_us16x08_comp_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
struct snd_us16x08_comp_store *store = elem->private_data;
int index = ucontrol->id.index;
@@ -485,7 +484,7 @@ static int snd_us16x08_eqswitch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int val;
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_us16x08_eq_store *store = elem->private_data;
int index = ucontrol->id.index;
@@ -500,7 +499,7 @@ static int snd_us16x08_eqswitch_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
struct snd_us16x08_eq_store *store = elem->private_data;
int index = ucontrol->id.index;
@@ -544,7 +543,7 @@ static int snd_us16x08_eq_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int val;
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_us16x08_eq_store *store = elem->private_data;
int index = ucontrol->id.index;
int b_idx = EQ_STORE_BAND_IDX(elem->head.id) - 1;
@@ -560,7 +559,7 @@ static int snd_us16x08_eq_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
struct snd_us16x08_eq_store *store = elem->private_data;
int index = ucontrol->id.index;
@@ -684,10 +683,10 @@ static int snd_us16x08_meter_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int i, set;
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = elem->head.mixer->chip;
struct snd_us16x08_meter_store *store = elem->private_data;
- u8 meter_urb[64];
+ u8 meter_urb[64] = {0};
switch (kcontrol->private_value) {
case 0: {
@@ -744,7 +743,7 @@ static int snd_us16x08_meter_get(struct snd_kcontrol *kcontrol,
static int snd_us16x08_meter_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct usb_mixer_elem_info *elem = snd_kcontrol_chip(kcontrol);
struct snd_us16x08_meter_store *store = elem->private_data;
int val;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 08bf535ed163..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);
}
@@ -148,6 +148,16 @@ find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
return found;
}
+const struct audioformat *
+snd_usb_find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
+ unsigned int rate, unsigned int channels, bool strict_match,
+ struct snd_usb_substream *subs)
+{
+ return find_format(fmt_list_head, format, rate, channels, strict_match,
+ subs);
+}
+EXPORT_SYMBOL_GPL(snd_usb_find_format);
+
static const struct audioformat *
find_substream_format(struct snd_usb_substream *subs,
const struct snd_pcm_hw_params *params)
@@ -157,6 +167,14 @@ find_substream_format(struct snd_usb_substream *subs,
true, subs);
}
+const struct audioformat *
+snd_usb_find_substream_format(struct snd_usb_substream *subs,
+ const struct snd_pcm_hw_params *params)
+{
+ return find_substream_format(subs, params);
+}
+EXPORT_SYMBOL_GPL(snd_usb_find_substream_format);
+
bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs)
{
const struct audioformat *fp;
@@ -461,20 +479,9 @@ static void close_endpoints(struct snd_usb_audio *chip,
}
}
-/*
- * hw_params callback
- *
- * allocate a buffer and set the given audio format.
- *
- * so far we use a physically linear buffer although packetize transfer
- * doesn't need a continuous area.
- * if sg buffer is supported on the later version of alsa, we'll follow
- * that.
- */
-static int snd_usb_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+int snd_usb_hw_params(struct snd_usb_substream *subs,
+ struct snd_pcm_hw_params *hw_params)
{
- struct snd_usb_substream *subs = substream->runtime->private_data;
struct snd_usb_audio *chip = subs->stream->chip;
const struct audioformat *fmt;
const struct audioformat *sync_fmt;
@@ -499,7 +506,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if (fmt->implicit_fb) {
sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
hw_params,
- !substream->stream,
+ !subs->direction,
&sync_fixed_rate);
if (!sync_fmt) {
usb_audio_dbg(chip,
@@ -553,9 +560,9 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
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;
@@ -579,30 +586,56 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+EXPORT_SYMBOL_GPL(snd_usb_hw_params);
/*
- * hw_free callback
+ * hw_params callback
*
- * reset the audio format and release the buffer
+ * allocate a buffer and set the given audio format.
+ *
+ * so far we use a physically linear buffer although packetize transfer
+ * doesn't need a continuous area.
+ * if sg buffer is supported on the later version of alsa, we'll follow
+ * that.
*/
-static int snd_usb_hw_free(struct snd_pcm_substream *substream)
+static int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ return snd_usb_hw_params(subs, hw_params);
+}
+
+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;
}
+EXPORT_SYMBOL_GPL(snd_usb_hw_free);
+
+/*
+ * hw_free callback
+ *
+ * reset the audio format and release the buffer
+ */
+static int snd_usb_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ return snd_usb_hw_free(subs);
+}
/* free-wheeling mode? (e.g. dmix) */
static int in_free_wheeling_mode(struct snd_pcm_runtime *runtime)
@@ -642,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;
@@ -689,8 +720,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
goto again;
}
}
- unlock:
- snd_usb_unlock_shutdown(chip);
+
return ret;
}
@@ -1208,8 +1238,15 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = &as->substream[direction];
+ struct snd_usb_audio *chip = subs->stream->chip;
int ret;
+ scoped_guard(mutex, &chip->mutex) {
+ if (subs->opened)
+ return -EBUSY;
+ subs->opened = 1;
+ }
+
runtime->hw = snd_usb_hardware;
/* need an explicit sync to catch applptr update in low-latency mode */
if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
@@ -1226,13 +1263,23 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
ret = setup_hw_info(runtime, subs);
if (ret < 0)
- return ret;
+ goto err_open;
ret = snd_usb_autoresume(subs->stream->chip);
if (ret < 0)
- return ret;
+ goto err_open;
ret = snd_media_stream_init(subs, as->pcm, direction);
if (ret < 0)
- snd_usb_autosuspend(subs->stream->chip);
+ goto err_resume;
+
+ return 0;
+
+err_resume:
+ snd_usb_autosuspend(subs->stream->chip);
+err_open:
+ scoped_guard(mutex, &chip->mutex) {
+ subs->opened = 0;
+ }
+
return ret;
}
@@ -1241,19 +1288,25 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
int direction = substream->stream;
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_usb_substream *subs = &as->substream[direction];
+ struct snd_usb_audio *chip = subs->stream->chip;
int ret;
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);
+ scoped_guard(mutex, &chip->mutex) {
+ subs->opened = 0;
+ }
return 0;
}
@@ -1269,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;
@@ -1280,11 +1332,10 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
for (i = 0; i < urb->number_of_packets; i++) {
cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset + subs->pkt_offset_adj;
- if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
- dev_dbg(&subs->dev->dev, "frame %d active: %d\n",
- i, urb->iso_frame_desc[i].status);
- // continue;
- }
+ if (urb->iso_frame_desc[i].status)
+ dev_dbg_ratelimited(&subs->dev->dev,
+ "frame %d active: %d\n", i,
+ urb->iso_frame_desc[i].status);
bytes = urb->iso_frame_desc[i].actual_length;
if (subs->stream_offset_adj > 0) {
unsigned int adj = min(subs->stream_offset_adj, bytes);
@@ -1303,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;
@@ -1478,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;
@@ -1487,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)
@@ -1604,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);
}
@@ -1746,8 +1788,8 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
static const struct snd_pcm_ops snd_usb_playback_ops = {
.open = snd_usb_pcm_open,
.close = snd_usb_pcm_close,
- .hw_params = snd_usb_hw_params,
- .hw_free = snd_usb_hw_free,
+ .hw_params = snd_usb_pcm_hw_params,
+ .hw_free = snd_usb_pcm_hw_free,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_substream_playback_trigger,
.sync_stop = snd_usb_pcm_sync_stop,
@@ -1758,8 +1800,8 @@ static const struct snd_pcm_ops snd_usb_playback_ops = {
static const struct snd_pcm_ops snd_usb_capture_ops = {
.open = snd_usb_pcm_open,
.close = snd_usb_pcm_close,
- .hw_params = snd_usb_hw_params,
- .hw_free = snd_usb_hw_free,
+ .hw_params = snd_usb_pcm_hw_params,
+ .hw_free = snd_usb_pcm_hw_free,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_substream_capture_trigger,
.sync_stop = snd_usb_pcm_sync_stop,
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index 388fe2ba346d..c096021adb2b 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -15,4 +15,15 @@ void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);
int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip,
struct audioformat *fmt);
+const struct audioformat *
+snd_usb_find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
+ unsigned int rate, unsigned int channels, bool strict_match,
+ struct snd_usb_substream *subs);
+const struct audioformat *
+snd_usb_find_substream_format(struct snd_usb_substream *subs,
+ const struct snd_pcm_hw_params *params);
+
+int snd_usb_hw_params(struct snd_usb_substream *subs,
+ struct snd_pcm_hw_params *hw_params);
+int snd_usb_hw_free(struct snd_usb_substream *subs);
#endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/power.c b/sound/usb/power.c
index 606a2cb23eab..66bd4daa68fd 100644
--- a/sound/usb/power.c
+++ b/sound/usb/power.c
@@ -40,6 +40,7 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
le16_to_cpu(pd_desc->waRecoveryTime1);
pd->pd_d2d0_rec =
le16_to_cpu(pd_desc->waRecoveryTime2);
+ pd->ctrl_iface = ctrl_iface;
return pd;
}
}
@@ -57,7 +58,7 @@ int snd_usb_power_domain_set(struct snd_usb_audio *chip,
unsigned char current_state;
int err, idx;
- idx = snd_usb_ctrl_intf(chip) | (pd->pd_id << 8);
+ idx = snd_usb_ctrl_intf(pd->ctrl_iface) | (pd->pd_id << 8);
err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
UAC2_CS_CUR,
diff --git a/sound/usb/power.h b/sound/usb/power.h
index 396e3e51440a..1fa92ad0ca92 100644
--- a/sound/usb/power.h
+++ b/sound/usb/power.h
@@ -6,6 +6,7 @@ struct snd_usb_power_domain {
int pd_id; /* UAC3 Power Domain ID */
int pd_d1d0_rec; /* D1 to D0 recovery time */
int pd_d2d0_rec; /* D2 to D0 recovery time */
+ struct usb_host_interface *ctrl_iface; /* Control interface */
};
enum {
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index e9bbaea7b2fa..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)
@@ -231,7 +230,7 @@ void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream)
char name[32];
struct snd_card *card = stream->chip->card;
- sprintf(name, "stream%d", stream->pcm_index);
+ scnprintf(name, sizeof(name), "stream%d", stream->pcm_index);
snd_card_ro_proc_new(card, name, stream, proc_pcm_format_read);
}
diff --git a/sound/usb/qcom/Makefile b/sound/usb/qcom/Makefile
new file mode 100644
index 000000000000..6567727b66f0
--- /dev/null
+++ b/sound/usb/qcom/Makefile
@@ -0,0 +1,4 @@
+snd-usb-audio-qmi-y := usb_audio_qmi_v01.o qc_audio_offload.o
+snd-usb-audio-qmi-y += mixer_usb_offload.o
+obj-$(CONFIG_SND_USB_AUDIO_QMI) += snd-usb-audio-qmi.o
+
diff --git a/sound/usb/qcom/mixer_usb_offload.c b/sound/usb/qcom/mixer_usb_offload.c
new file mode 100644
index 000000000000..2adeb64f4d33
--- /dev/null
+++ b/sound/usb/qcom/mixer_usb_offload.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/usb.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/soc-usb.h>
+
+#include "../usbaudio.h"
+#include "../card.h"
+#include "../helper.h"
+#include "../mixer.h"
+
+#include "mixer_usb_offload.h"
+
+#define PCM_IDX(n) ((n) & 0xffff)
+#define CARD_IDX(n) ((n) >> 16)
+
+static int
+snd_usb_offload_card_route_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct device *sysdev = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = snd_soc_usb_update_offload_route(sysdev,
+ CARD_IDX(kcontrol->private_value),
+ PCM_IDX(kcontrol->private_value),
+ SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_USB_KCTL_CARD_ROUTE,
+ ucontrol->value.integer.value);
+ if (ret < 0) {
+ ucontrol->value.integer.value[0] = -1;
+ ucontrol->value.integer.value[1] = -1;
+ }
+
+ return 0;
+}
+
+static int snd_usb_offload_card_route_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 = -1;
+ uinfo->value.integer.max = SNDRV_CARDS;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_usb_offload_mapped_card_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_usb_offload_card_route_info,
+ .get = snd_usb_offload_card_route_get,
+};
+
+static int
+snd_usb_offload_pcm_route_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct device *sysdev = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = snd_soc_usb_update_offload_route(sysdev,
+ CARD_IDX(kcontrol->private_value),
+ PCM_IDX(kcontrol->private_value),
+ SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_USB_KCTL_PCM_ROUTE,
+ ucontrol->value.integer.value);
+ if (ret < 0) {
+ ucontrol->value.integer.value[0] = -1;
+ ucontrol->value.integer.value[1] = -1;
+ }
+
+ return 0;
+}
+
+static int snd_usb_offload_pcm_route_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 = -1;
+ /* Arbitrary max value, as there is no 'limit' on number of PCM devices */
+ uinfo->value.integer.max = 0xff;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_usb_offload_mapped_pcm_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_usb_offload_pcm_route_info,
+ .get = snd_usb_offload_pcm_route_get,
+};
+
+/**
+ * snd_usb_offload_create_ctl() - Add USB offload bounded mixer
+ * @chip: USB SND chip device
+ * @bedev: Reference to USB backend DAI device
+ *
+ * Creates a sound control for a USB audio device, so that applications can
+ * query for if there is an available USB audio offload path, and which
+ * card is managing it.
+ */
+int snd_usb_offload_create_ctl(struct snd_usb_audio *chip, struct device *bedev)
+{
+ struct snd_kcontrol_new *chip_kctl;
+ struct snd_usb_substream *subs;
+ struct snd_usb_stream *as;
+ char ctl_name[48];
+ int ret;
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK];
+ if (!subs->ep_num || as->pcm_index > 0xff)
+ continue;
+
+ chip_kctl = &snd_usb_offload_mapped_card_ctl;
+ chip_kctl->count = 1;
+ /*
+ * Store the associated USB SND card number and PCM index for
+ * the kctl.
+ */
+ chip_kctl->private_value = as->pcm_index |
+ chip->card->number << 16;
+ sprintf(ctl_name, "USB Offload Playback Card Route PCM#%d",
+ as->pcm_index);
+ chip_kctl->name = ctl_name;
+ ret = snd_ctl_add(chip->card, snd_ctl_new1(chip_kctl, bedev));
+ if (ret < 0)
+ break;
+
+ chip_kctl = &snd_usb_offload_mapped_pcm_ctl;
+ chip_kctl->count = 1;
+ /*
+ * Store the associated USB SND card number and PCM index for
+ * the kctl.
+ */
+ chip_kctl->private_value = as->pcm_index |
+ chip->card->number << 16;
+ sprintf(ctl_name, "USB Offload Playback PCM Route PCM#%d",
+ as->pcm_index);
+ chip_kctl->name = ctl_name;
+ ret = snd_ctl_add(chip->card, snd_ctl_new1(chip_kctl, bedev));
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
diff --git a/sound/usb/qcom/mixer_usb_offload.h b/sound/usb/qcom/mixer_usb_offload.h
new file mode 100644
index 000000000000..cf20673f4dc9
--- /dev/null
+++ b/sound/usb/qcom/mixer_usb_offload.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __USB_OFFLOAD_MIXER_H
+#define __USB_OFFLOAD_MIXER_H
+
+int snd_usb_offload_create_ctl(struct snd_usb_audio *chip, struct device *bedev);
+
+#endif /* __USB_OFFLOAD_MIXER_H */
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
new file mode 100644
index 000000000000..cfb30a195364
--- /dev/null
+++ b/sound/usb/qcom/qc_audio_offload.c
@@ -0,0 +1,1993 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/ctype.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-map-ops.h>
+#include <linux/init.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
+#include <linux/usb/xhci-sideband.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/q6usboffload.h>
+#include <sound/soc.h>
+#include <sound/soc-usb.h>
+
+#include "../usbaudio.h"
+#include "../card.h"
+#include "../endpoint.h"
+#include "../format.h"
+#include "../helper.h"
+#include "../pcm.h"
+#include "../power.h"
+
+#include "mixer_usb_offload.h"
+#include "usb_audio_qmi_v01.h"
+
+/* Stream disable request timeout during USB device disconnect */
+#define DEV_RELEASE_WAIT_TIMEOUT 10000 /* in ms */
+
+/* Data interval calculation parameters */
+#define BUS_INTERVAL_FULL_SPEED 1000 /* in us */
+#define BUS_INTERVAL_HIGHSPEED_AND_ABOVE 125 /* in us */
+#define MAX_BINTERVAL_ISOC_EP 16
+
+#define QMI_STREAM_REQ_CARD_NUM_MASK 0xffff0000
+#define QMI_STREAM_REQ_DEV_NUM_MASK 0xff00
+#define QMI_STREAM_REQ_DIRECTION 0xff
+
+/* iommu resource parameters and management */
+#define PREPEND_SID_TO_IOVA(iova, sid) ((u64)(((u64)(iova)) | \
+ (((u64)sid) << 32)))
+#define IOVA_MASK(iova) (((u64)(iova)) & 0xFFFFFFFF)
+#define IOVA_BASE 0x1000
+#define IOVA_XFER_RING_BASE (IOVA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
+#define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32)
+#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
+#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
+
+#define MAX_XFER_BUFF_LEN (24 * PAGE_SIZE)
+
+struct iova_info {
+ struct list_head list;
+ unsigned long start_iova;
+ size_t size;
+ bool in_use;
+};
+
+struct intf_info {
+ /* IOMMU ring/buffer mapping information */
+ unsigned long data_xfer_ring_va;
+ size_t data_xfer_ring_size;
+ unsigned long sync_xfer_ring_va;
+ size_t sync_xfer_ring_size;
+ dma_addr_t xfer_buf_iova;
+ size_t xfer_buf_size;
+ dma_addr_t xfer_buf_dma;
+ u8 *xfer_buf_cpu;
+
+ /* USB endpoint information */
+ unsigned int data_ep_pipe;
+ unsigned int sync_ep_pipe;
+ unsigned int data_ep_idx;
+ unsigned int sync_ep_idx;
+
+ u8 intf_num;
+ u8 pcm_card_num;
+ u8 pcm_dev_num;
+ u8 direction;
+ bool in_use;
+};
+
+struct uaudio_qmi_dev {
+ struct device *dev;
+ struct q6usb_offload *data;
+ struct auxiliary_device *auxdev;
+
+ /* list to keep track of available iova */
+ struct list_head xfer_ring_list;
+ size_t xfer_ring_iova_size;
+ unsigned long curr_xfer_ring_iova;
+ struct list_head xfer_buf_list;
+ size_t xfer_buf_iova_size;
+ unsigned long curr_xfer_buf_iova;
+
+ /* bit fields representing pcm card enabled */
+ unsigned long card_slot;
+ /* indicate event ring mapped or not */
+ bool er_mapped;
+};
+
+struct uaudio_dev {
+ struct usb_device *udev;
+ /* audio control interface */
+ struct usb_host_interface *ctrl_intf;
+ unsigned int usb_core_id;
+ atomic_t in_use;
+ struct kref kref;
+ wait_queue_head_t disconnect_wq;
+
+ /* interface specific */
+ int num_intf;
+ struct intf_info *info;
+ struct snd_usb_audio *chip;
+
+ /* xhci sideband */
+ struct xhci_sideband *sb;
+
+ /* SoC USB device */
+ struct snd_soc_usb_device *sdev;
+};
+
+static struct uaudio_dev uadev[SNDRV_CARDS];
+static struct uaudio_qmi_dev *uaudio_qdev;
+static struct uaudio_qmi_svc *uaudio_svc;
+static DEFINE_MUTEX(qdev_mutex);
+
+struct uaudio_qmi_svc {
+ struct qmi_handle *uaudio_svc_hdl;
+ struct sockaddr_qrtr client_sq;
+ bool client_connected;
+};
+
+enum mem_type {
+ MEM_EVENT_RING,
+ MEM_XFER_RING,
+ MEM_XFER_BUF,
+};
+
+/* Supported audio formats */
+enum usb_qmi_audio_format {
+ USB_QMI_PCM_FORMAT_S8 = 0,
+ USB_QMI_PCM_FORMAT_U8,
+ USB_QMI_PCM_FORMAT_S16_LE,
+ USB_QMI_PCM_FORMAT_S16_BE,
+ USB_QMI_PCM_FORMAT_U16_LE,
+ USB_QMI_PCM_FORMAT_U16_BE,
+ USB_QMI_PCM_FORMAT_S24_LE,
+ USB_QMI_PCM_FORMAT_S24_BE,
+ USB_QMI_PCM_FORMAT_U24_LE,
+ USB_QMI_PCM_FORMAT_U24_BE,
+ USB_QMI_PCM_FORMAT_S24_3LE,
+ USB_QMI_PCM_FORMAT_S24_3BE,
+ USB_QMI_PCM_FORMAT_U24_3LE,
+ USB_QMI_PCM_FORMAT_U24_3BE,
+ USB_QMI_PCM_FORMAT_S32_LE,
+ USB_QMI_PCM_FORMAT_S32_BE,
+ USB_QMI_PCM_FORMAT_U32_LE,
+ USB_QMI_PCM_FORMAT_U32_BE,
+};
+
+static int usb_qmi_get_pcm_num(struct snd_usb_audio *chip, int direction)
+{
+ struct snd_usb_substream *subs = NULL;
+ struct snd_usb_stream *as;
+ int count = 0;
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ subs = &as->substream[direction];
+ if (subs->ep_num)
+ count++;
+ }
+
+ return count;
+}
+
+static enum usb_qmi_audio_device_speed_enum_v01
+get_speed_info(enum usb_device_speed udev_speed)
+{
+ switch (udev_speed) {
+ case USB_SPEED_LOW:
+ return USB_QMI_DEVICE_SPEED_LOW_V01;
+ case USB_SPEED_FULL:
+ return USB_QMI_DEVICE_SPEED_FULL_V01;
+ case USB_SPEED_HIGH:
+ return USB_QMI_DEVICE_SPEED_HIGH_V01;
+ case USB_SPEED_SUPER:
+ return USB_QMI_DEVICE_SPEED_SUPER_V01;
+ case USB_SPEED_SUPER_PLUS:
+ return USB_QMI_DEVICE_SPEED_SUPER_PLUS_V01;
+ default:
+ return USB_QMI_DEVICE_SPEED_INVALID_V01;
+ }
+}
+
+static struct snd_usb_substream *find_substream(unsigned int card_num,
+ unsigned int pcm_idx,
+ unsigned int direction)
+{
+ struct snd_usb_substream *subs = NULL;
+ struct snd_usb_audio *chip;
+ struct snd_usb_stream *as;
+
+ chip = uadev[card_num].chip;
+ if (!chip || atomic_read(&chip->shutdown))
+ goto done;
+
+ if (pcm_idx >= chip->pcm_devs)
+ goto done;
+
+ if (direction > SNDRV_PCM_STREAM_CAPTURE)
+ goto done;
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ if (as->pcm_index == pcm_idx) {
+ subs = &as->substream[direction];
+ goto done;
+ }
+ }
+
+done:
+ return subs;
+}
+
+static int info_idx_from_ifnum(int card_num, int intf_num, bool enable)
+{
+ int i;
+
+ /*
+ * default index 0 is used when info is allocated upon
+ * first enable audio stream req for a pcm device
+ */
+ if (enable && !uadev[card_num].info)
+ return 0;
+
+ for (i = 0; i < uadev[card_num].num_intf; i++) {
+ if (enable && !uadev[card_num].info[i].in_use)
+ return i;
+ else if (!enable &&
+ uadev[card_num].info[i].intf_num == intf_num)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int get_data_interval_from_si(struct snd_usb_substream *subs,
+ u32 service_interval)
+{
+ unsigned int bus_intval_mult;
+ unsigned int bus_intval;
+ unsigned int binterval;
+
+ if (subs->dev->speed >= USB_SPEED_HIGH)
+ bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE;
+ else
+ bus_intval = BUS_INTERVAL_FULL_SPEED;
+
+ if (service_interval % bus_intval)
+ return -EINVAL;
+
+ bus_intval_mult = service_interval / bus_intval;
+ binterval = ffs(bus_intval_mult);
+ if (!binterval || binterval > MAX_BINTERVAL_ISOC_EP)
+ return -EINVAL;
+
+ /* check if another bit is set then bail out */
+ bus_intval_mult = bus_intval_mult >> binterval;
+ if (bus_intval_mult)
+ return -EINVAL;
+
+ return (binterval - 1);
+}
+
+/* maps audio format received over QMI to asound.h based pcm format */
+static snd_pcm_format_t map_pcm_format(enum usb_qmi_audio_format fmt_received)
+{
+ switch (fmt_received) {
+ case USB_QMI_PCM_FORMAT_S8:
+ return SNDRV_PCM_FORMAT_S8;
+ case USB_QMI_PCM_FORMAT_U8:
+ return SNDRV_PCM_FORMAT_U8;
+ case USB_QMI_PCM_FORMAT_S16_LE:
+ return SNDRV_PCM_FORMAT_S16_LE;
+ case USB_QMI_PCM_FORMAT_S16_BE:
+ return SNDRV_PCM_FORMAT_S16_BE;
+ case USB_QMI_PCM_FORMAT_U16_LE:
+ return SNDRV_PCM_FORMAT_U16_LE;
+ case USB_QMI_PCM_FORMAT_U16_BE:
+ return SNDRV_PCM_FORMAT_U16_BE;
+ case USB_QMI_PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S24_LE;
+ case USB_QMI_PCM_FORMAT_S24_BE:
+ return SNDRV_PCM_FORMAT_S24_BE;
+ case USB_QMI_PCM_FORMAT_U24_LE:
+ return SNDRV_PCM_FORMAT_U24_LE;
+ case USB_QMI_PCM_FORMAT_U24_BE:
+ return SNDRV_PCM_FORMAT_U24_BE;
+ case USB_QMI_PCM_FORMAT_S24_3LE:
+ return SNDRV_PCM_FORMAT_S24_3LE;
+ case USB_QMI_PCM_FORMAT_S24_3BE:
+ return SNDRV_PCM_FORMAT_S24_3BE;
+ case USB_QMI_PCM_FORMAT_U24_3LE:
+ return SNDRV_PCM_FORMAT_U24_3LE;
+ case USB_QMI_PCM_FORMAT_U24_3BE:
+ return SNDRV_PCM_FORMAT_U24_3BE;
+ case USB_QMI_PCM_FORMAT_S32_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+ case USB_QMI_PCM_FORMAT_S32_BE:
+ return SNDRV_PCM_FORMAT_S32_BE;
+ case USB_QMI_PCM_FORMAT_U32_LE:
+ return SNDRV_PCM_FORMAT_U32_LE;
+ case USB_QMI_PCM_FORMAT_U32_BE:
+ return SNDRV_PCM_FORMAT_U32_BE;
+ default:
+ /*
+ * We expect the caller to do input validation so we should
+ * never hit this. But we do have to return a proper
+ * snd_pcm_format_t value due to the __bitwise attribute; so
+ * just return the equivalent of 0 in case of bad input.
+ */
+ return SNDRV_PCM_FORMAT_S8;
+ }
+}
+
+/*
+ * Sends QMI disconnect indication message, assumes chip->mutex and qdev_mutex
+ * lock held by caller.
+ */
+static int uaudio_send_disconnect_ind(struct snd_usb_audio *chip)
+{
+ struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0};
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+ struct uaudio_dev *dev;
+ int ret = 0;
+
+ dev = &uadev[chip->card->number];
+
+ if (atomic_read(&dev->in_use)) {
+ mutex_unlock(&chip->mutex);
+ mutex_unlock(&qdev_mutex);
+ dev_dbg(uaudio_qdev->data->dev, "sending qmi indication suspend\n");
+ disconnect_ind.dev_event = USB_QMI_DEV_DISCONNECT_V01;
+ disconnect_ind.slot_id = dev->udev->slot_id;
+ disconnect_ind.controller_num = dev->usb_core_id;
+ disconnect_ind.controller_num_valid = 1;
+ ret = qmi_send_indication(svc->uaudio_svc_hdl, &svc->client_sq,
+ QMI_UAUDIO_STREAM_IND_V01,
+ QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN,
+ qmi_uaudio_stream_ind_msg_v01_ei,
+ &disconnect_ind);
+ if (ret < 0)
+ dev_err(uaudio_qdev->data->dev,
+ "qmi send failed with err: %d\n", ret);
+
+ ret = wait_event_interruptible_timeout(dev->disconnect_wq,
+ !atomic_read(&dev->in_use),
+ msecs_to_jiffies(DEV_RELEASE_WAIT_TIMEOUT));
+ if (!ret) {
+ dev_err(uaudio_qdev->data->dev,
+ "timeout while waiting for dev_release\n");
+ atomic_set(&dev->in_use, 0);
+ } else if (ret < 0) {
+ dev_err(uaudio_qdev->data->dev,
+ "failed with ret %d\n", ret);
+ atomic_set(&dev->in_use, 0);
+ }
+ mutex_lock(&qdev_mutex);
+ mutex_lock(&chip->mutex);
+ }
+
+ return ret;
+}
+
+/* Offloading IOMMU management */
+static unsigned long uaudio_get_iova(unsigned long *curr_iova,
+ size_t *curr_iova_size,
+ struct list_head *head, size_t size)
+{
+ struct iova_info *info, *new_info = NULL;
+ struct list_head *curr_head;
+ size_t tmp_size = size;
+ unsigned long iova = 0;
+
+ if (size % PAGE_SIZE)
+ goto done;
+
+ if (size > *curr_iova_size)
+ goto done;
+
+ if (*curr_iova_size == 0)
+ goto done;
+
+ list_for_each_entry(info, head, list) {
+ /* exact size iova_info */
+ if (!info->in_use && info->size == size) {
+ info->in_use = true;
+ iova = info->start_iova;
+ *curr_iova_size -= size;
+ goto done;
+ } else if (!info->in_use && tmp_size >= info->size) {
+ if (!new_info)
+ new_info = info;
+ tmp_size -= info->size;
+ if (tmp_size)
+ continue;
+
+ iova = new_info->start_iova;
+ for (curr_head = &new_info->list; curr_head !=
+ &info->list; curr_head = curr_head->next) {
+ new_info = list_entry(curr_head, struct
+ iova_info, list);
+ new_info->in_use = true;
+ }
+ info->in_use = true;
+ *curr_iova_size -= size;
+ goto done;
+ } else {
+ /* iova region in use */
+ new_info = NULL;
+ tmp_size = size;
+ }
+ }
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ iova = 0;
+ goto done;
+ }
+
+ iova = *curr_iova;
+ info->start_iova = *curr_iova;
+ info->size = size;
+ info->in_use = true;
+ *curr_iova += size;
+ *curr_iova_size -= size;
+ list_add_tail(&info->list, head);
+
+done:
+ return iova;
+}
+
+static void uaudio_put_iova(unsigned long iova, size_t size, struct list_head
+ *head, size_t *curr_iova_size)
+{
+ struct iova_info *info;
+ size_t tmp_size = size;
+ bool found = false;
+
+ list_for_each_entry(info, head, list) {
+ if (info->start_iova == iova) {
+ if (!info->in_use)
+ return;
+
+ found = true;
+ info->in_use = false;
+ if (info->size == size)
+ goto done;
+ }
+
+ if (found && tmp_size >= info->size) {
+ info->in_use = false;
+ tmp_size -= info->size;
+ if (!tmp_size)
+ goto done;
+ }
+ }
+
+ if (!found)
+ return;
+
+done:
+ *curr_iova_size += size;
+}
+
+/**
+ * uaudio_iommu_unmap() - unmaps iommu memory for adsp
+ * @mtype: ring type
+ * @iova: virtual address to unmap
+ * @iova_size: region size
+ * @mapped_iova_size: mapped region size
+ *
+ * Unmaps the memory region that was previously assigned to the adsp.
+ *
+ */
+static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long iova,
+ size_t iova_size, size_t mapped_iova_size)
+{
+ size_t umap_size;
+ bool unmap = true;
+
+ if (!iova || !iova_size)
+ return;
+
+ switch (mtype) {
+ case MEM_EVENT_RING:
+ if (uaudio_qdev->er_mapped)
+ uaudio_qdev->er_mapped = false;
+ else
+ unmap = false;
+ break;
+
+ case MEM_XFER_RING:
+ uaudio_put_iova(iova, iova_size, &uaudio_qdev->xfer_ring_list,
+ &uaudio_qdev->xfer_ring_iova_size);
+ break;
+ case MEM_XFER_BUF:
+ uaudio_put_iova(iova, iova_size, &uaudio_qdev->xfer_buf_list,
+ &uaudio_qdev->xfer_buf_iova_size);
+ break;
+ default:
+ unmap = false;
+ }
+
+ if (!unmap || !mapped_iova_size)
+ return;
+
+ umap_size = iommu_unmap(uaudio_qdev->data->domain, iova, mapped_iova_size);
+ if (umap_size != mapped_iova_size)
+ dev_err(uaudio_qdev->data->dev,
+ "unmapped size %zu for iova 0x%08lx of mapped size %zu\n",
+ umap_size, iova, mapped_iova_size);
+}
+
+static int uaudio_iommu_map_prot(bool dma_coherent)
+{
+ int prot = IOMMU_READ | IOMMU_WRITE;
+
+ if (dma_coherent)
+ prot |= IOMMU_CACHE;
+ return prot;
+}
+
+/**
+ * uaudio_iommu_map_pa() - maps iommu memory for adsp
+ * @mtype: ring type
+ * @dma_coherent: dma coherent
+ * @pa: physical address for ring/buffer
+ * @size: size of memory region
+ *
+ * Maps the XHCI related resources to a memory region that is assigned to be
+ * used by the adsp. This will be mapped to the domain, which is created by
+ * the ASoC USB backend driver.
+ *
+ */
+static unsigned long uaudio_iommu_map_pa(enum mem_type mtype, bool dma_coherent,
+ phys_addr_t pa, size_t size)
+{
+ unsigned long iova = 0;
+ bool map = true;
+ int prot = uaudio_iommu_map_prot(dma_coherent);
+
+ switch (mtype) {
+ case MEM_EVENT_RING:
+ iova = IOVA_BASE;
+ /* er already mapped */
+ if (uaudio_qdev->er_mapped)
+ map = false;
+ break;
+ case MEM_XFER_RING:
+ iova = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova,
+ &uaudio_qdev->xfer_ring_iova_size,
+ &uaudio_qdev->xfer_ring_list, size);
+ break;
+ default:
+ dev_err(uaudio_qdev->data->dev, "unknown mem type %d\n", mtype);
+ }
+
+ if (!iova || !map)
+ return 0;
+
+ iommu_map(uaudio_qdev->data->domain, iova, pa, size, prot, GFP_KERNEL);
+
+ return iova;
+}
+
+static unsigned long uaudio_iommu_map_xfer_buf(bool dma_coherent, size_t size,
+ struct sg_table *sgt)
+{
+ struct scatterlist *sg;
+ unsigned long iova = 0;
+ size_t total_len = 0;
+ unsigned long iova_sg;
+ phys_addr_t pa_sg;
+ size_t sg_len;
+ int prot = uaudio_iommu_map_prot(dma_coherent);
+ int ret;
+ int i;
+
+ prot = IOMMU_READ | IOMMU_WRITE;
+
+ if (dma_coherent)
+ prot |= IOMMU_CACHE;
+
+ iova = uaudio_get_iova(&uaudio_qdev->curr_xfer_buf_iova,
+ &uaudio_qdev->xfer_buf_iova_size,
+ &uaudio_qdev->xfer_buf_list, size);
+ if (!iova)
+ goto done;
+
+ iova_sg = iova;
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ sg_len = PAGE_ALIGN(sg->offset + sg->length);
+ pa_sg = page_to_phys(sg_page(sg));
+ ret = iommu_map(uaudio_qdev->data->domain, iova_sg, pa_sg, sg_len,
+ prot, GFP_KERNEL);
+ if (ret) {
+ uaudio_iommu_unmap(MEM_XFER_BUF, iova, size, total_len);
+ iova = 0;
+ goto done;
+ }
+
+ iova_sg += sg_len;
+ total_len += sg_len;
+ }
+
+ if (size != total_len) {
+ uaudio_iommu_unmap(MEM_XFER_BUF, iova, size, total_len);
+ iova = 0;
+ }
+done:
+ return iova;
+}
+
+/* looks up alias, if any, for controller DT node and returns the index */
+static int usb_get_controller_id(struct usb_device *udev)
+{
+ if (udev->bus->sysdev && udev->bus->sysdev->of_node)
+ return of_alias_get_id(udev->bus->sysdev->of_node, "usb");
+
+ return -ENODEV;
+}
+
+/**
+ * uaudio_dev_intf_cleanup() - cleanup transfer resources
+ * @udev: usb device
+ * @info: usb offloading interface
+ *
+ * Cleans up the transfer ring related resources which are assigned per
+ * endpoint from XHCI. This is invoked when the USB endpoints are no
+ * longer in use by the adsp.
+ *
+ */
+static void uaudio_dev_intf_cleanup(struct usb_device *udev, struct intf_info *info)
+{
+ uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
+ info->data_xfer_ring_size, info->data_xfer_ring_size);
+ info->data_xfer_ring_va = 0;
+ info->data_xfer_ring_size = 0;
+
+ uaudio_iommu_unmap(MEM_XFER_RING, info->sync_xfer_ring_va,
+ info->sync_xfer_ring_size, info->sync_xfer_ring_size);
+ info->sync_xfer_ring_va = 0;
+ info->sync_xfer_ring_size = 0;
+
+ uaudio_iommu_unmap(MEM_XFER_BUF, info->xfer_buf_iova, info->xfer_buf_size,
+ info->xfer_buf_size);
+ info->xfer_buf_iova = 0;
+
+ usb_free_coherent(udev, info->xfer_buf_size, info->xfer_buf_cpu,
+ info->xfer_buf_dma);
+ info->xfer_buf_size = 0;
+ info->xfer_buf_cpu = NULL;
+ info->xfer_buf_dma = 0;
+
+ info->in_use = false;
+}
+
+/**
+ * uaudio_event_ring_cleanup_free() - cleanup secondary event ring
+ * @dev: usb offload device
+ *
+ * Cleans up the secondary event ring that was requested. This will
+ * occur when the adsp is no longer transferring data on the USB bus
+ * across all endpoints.
+ *
+ */
+static void uaudio_event_ring_cleanup_free(struct uaudio_dev *dev)
+{
+ clear_bit(dev->chip->card->number, &uaudio_qdev->card_slot);
+ /* all audio devices are disconnected */
+ if (!uaudio_qdev->card_slot) {
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
+ PAGE_SIZE);
+ xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb);
+ }
+}
+
+static void uaudio_dev_cleanup(struct uaudio_dev *dev)
+{
+ int if_idx;
+
+ if (!dev->udev)
+ return;
+
+ /* free xfer buffer and unmap xfer ring and buf per interface */
+ for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
+ if (!dev->info[if_idx].in_use)
+ continue;
+ uaudio_dev_intf_cleanup(dev->udev, &dev->info[if_idx]);
+ dev_dbg(uaudio_qdev->data->dev,
+ "release resources: intf# %d card# %d\n",
+ dev->info[if_idx].intf_num, dev->chip->card->number);
+ }
+
+ dev->num_intf = 0;
+
+ /* free interface info */
+ kfree(dev->info);
+ dev->info = NULL;
+ uaudio_event_ring_cleanup_free(dev);
+ dev->udev = NULL;
+}
+
+/**
+ * disable_audio_stream() - disable usb snd endpoints
+ * @subs: usb substream
+ *
+ * Closes the USB SND endpoints associated with the current audio stream
+ * used. This will decrement the USB SND endpoint opened reference count.
+ *
+ */
+static void disable_audio_stream(struct snd_usb_substream *subs)
+{
+ struct snd_usb_audio *chip = subs->stream->chip;
+
+ snd_usb_hw_free(subs);
+ snd_usb_autosuspend(chip);
+}
+
+/* QMI service disconnect handlers */
+static void qmi_stop_session(void)
+{
+ struct snd_usb_substream *subs;
+ struct usb_host_endpoint *ep;
+ struct snd_usb_audio *chip;
+ struct intf_info *info;
+ int pcm_card_num;
+ int if_idx;
+ int idx;
+
+ 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))
+ continue;
+
+ chip = uadev[idx].chip;
+ for (if_idx = 0; if_idx < uadev[idx].num_intf; if_idx++) {
+ if (!uadev[idx].info || !uadev[idx].info[if_idx].in_use)
+ continue;
+ info = &uadev[idx].info[if_idx];
+ pcm_card_num = info->pcm_card_num;
+ subs = find_substream(pcm_card_num, info->pcm_dev_num,
+ info->direction);
+ if (!subs || !chip || atomic_read(&chip->shutdown)) {
+ dev_err(&uadev[idx].udev->dev,
+ "no sub for c#%u dev#%u dir%u\n",
+ info->pcm_card_num,
+ info->pcm_dev_num,
+ info->direction);
+ continue;
+ }
+ /* Release XHCI endpoints */
+ if (info->data_ep_pipe)
+ ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
+ info->data_ep_pipe);
+ xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
+
+ if (info->sync_ep_pipe)
+ ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
+ info->sync_ep_pipe);
+ xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
+
+ disable_audio_stream(subs);
+ }
+ atomic_set(&uadev[idx].in_use, 0);
+ guard(mutex)(&chip->mutex);
+ uaudio_dev_cleanup(&uadev[idx]);
+ }
+}
+
+/**
+ * uaudio_sideband_notifier() - xHCI sideband event handler
+ * @intf: USB interface handle
+ * @evt: xHCI sideband event type
+ *
+ * This callback is executed when the xHCI sideband encounters a sequence
+ * that requires the sideband clients to take action. An example, is when
+ * xHCI frees the transfer ring, so the client has to ensure that the
+ * offload path is halted.
+ *
+ */
+static int uaudio_sideband_notifier(struct usb_interface *intf,
+ struct xhci_sideband_event *evt)
+{
+ struct snd_usb_audio *chip;
+ struct uaudio_dev *dev;
+ int if_idx;
+
+ if (!intf || !evt)
+ return 0;
+
+ chip = usb_get_intfdata(intf);
+
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
+
+ dev = &uadev[chip->card->number];
+
+ if (evt->type == XHCI_SIDEBAND_XFER_RING_FREE) {
+ unsigned int *ep = (unsigned int *) evt->evt_data;
+
+ for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
+ if (dev->info[if_idx].data_ep_idx == *ep ||
+ dev->info[if_idx].sync_ep_idx == *ep)
+ uaudio_send_disconnect_ind(chip);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * qmi_bye_cb() - qmi bye message callback
+ * @handle: QMI handle
+ * @node: id of the dying node
+ *
+ * This callback is invoked when the QMI bye control message is received
+ * from the QMI client. Handle the message accordingly by ensuring that
+ * the USB offload path is disabled and cleaned up. At this point, ADSP
+ * is not utilizing the USB bus.
+ *
+ */
+static void qmi_bye_cb(struct qmi_handle *handle, unsigned int node)
+{
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+
+ if (svc->uaudio_svc_hdl != handle)
+ return;
+
+ if (svc->client_connected && svc->client_sq.sq_node == node) {
+ qmi_stop_session();
+
+ /* clear QMI client parameters to block further QMI messages */
+ svc->client_sq.sq_node = 0;
+ svc->client_sq.sq_port = 0;
+ svc->client_sq.sq_family = 0;
+ svc->client_connected = false;
+ }
+}
+
+/**
+ * qmi_svc_disconnect_cb() - qmi client disconnected
+ * @handle: QMI handle
+ * @node: id of the dying node
+ * @port: port of the dying client
+ *
+ * Invoked when the remote QMI client is disconnected. Handle this event
+ * the same way as when the QMI bye message is received. This will ensure
+ * the USB offloading path is disabled and cleaned up.
+ *
+ */
+static void qmi_svc_disconnect_cb(struct qmi_handle *handle,
+ unsigned int node, unsigned int port)
+{
+ struct uaudio_qmi_svc *svc;
+
+ if (!uaudio_svc)
+ return;
+
+ svc = uaudio_svc;
+ if (svc->uaudio_svc_hdl != handle)
+ return;
+
+ if (svc->client_connected && svc->client_sq.sq_node == node &&
+ svc->client_sq.sq_port == port) {
+ qmi_stop_session();
+
+ /* clear QMI client parameters to block further QMI messages */
+ svc->client_sq.sq_node = 0;
+ svc->client_sq.sq_port = 0;
+ svc->client_sq.sq_family = 0;
+ svc->client_connected = false;
+ }
+}
+
+/* QMI client callback handlers from QMI interface */
+static struct qmi_ops uaudio_svc_ops_options = {
+ .bye = qmi_bye_cb,
+ .del_client = qmi_svc_disconnect_cb,
+};
+
+/* kref release callback when all streams are disabled */
+static void uaudio_dev_release(struct kref *kref)
+{
+ struct uaudio_dev *dev = container_of(kref, struct uaudio_dev, kref);
+
+ uaudio_event_ring_cleanup_free(dev);
+ atomic_set(&dev->in_use, 0);
+ wake_up(&dev->disconnect_wq);
+}
+
+/**
+ * enable_audio_stream() - enable usb snd endpoints
+ * @subs: usb substream
+ * @pcm_format: pcm format requested
+ * @channels: number of channels
+ * @cur_rate: sample rate
+ * @datainterval: interval
+ *
+ * Opens all USB SND endpoints used for the data interface. This will increment
+ * the USB SND endpoint's opened count. Requests to keep the interface resumed
+ * until the audio stream is stopped. Will issue the USB set interface control
+ * message to enable the data interface.
+ *
+ */
+static int enable_audio_stream(struct snd_usb_substream *subs,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels, unsigned int cur_rate,
+ int datainterval)
+{
+ struct snd_pcm_hw_params params;
+ struct snd_usb_audio *chip;
+ struct snd_interval *i;
+ struct snd_mask *m;
+ int ret;
+
+ chip = subs->stream->chip;
+
+ _snd_pcm_hw_params_any(&params);
+
+ m = hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_leave(m, pcm_format);
+
+ i = hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_interval_setinteger(i);
+ i->min = channels;
+ i->max = channels;
+
+ i = hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE);
+ snd_interval_setinteger(i);
+ i->min = cur_rate;
+ i->max = cur_rate;
+
+ pm_runtime_barrier(&chip->intf[0]->dev);
+ snd_usb_autoresume(chip);
+
+ ret = snd_usb_hw_params(subs, &params);
+ if (ret < 0)
+ goto put_suspend;
+
+ if (!atomic_read(&chip->shutdown)) {
+ 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 detach_ep;
+ }
+
+ ret = snd_usb_endpoint_prepare(chip, subs->data_endpoint);
+ if (ret < 0)
+ goto detach_ep;
+
+ dev_dbg(uaudio_qdev->data->dev,
+ "selected %s iface:%d altsetting:%d datainterval:%dus\n",
+ subs->direction ? "capture" : "playback",
+ subs->cur_audiofmt->iface, subs->cur_audiofmt->altsetting,
+ (1 << subs->cur_audiofmt->datainterval) *
+ (subs->dev->speed >= USB_SPEED_HIGH ?
+ BUS_INTERVAL_HIGHSPEED_AND_ABOVE :
+ BUS_INTERVAL_FULL_SPEED));
+ }
+
+ return 0;
+
+detach_ep:
+ snd_usb_hw_free(subs);
+
+put_suspend:
+ snd_usb_autosuspend(chip);
+
+ return ret;
+}
+
+/**
+ * uaudio_transfer_buffer_setup() - fetch and populate xfer buffer params
+ * @subs: usb substream
+ * @xfer_buf: xfer buf to be allocated
+ * @xfer_buf_len: size of allocation
+ * @mem_info: QMI response info
+ *
+ * Allocates and maps the transfer buffers that will be utilized by the
+ * audio DSP. Will populate the information in the QMI response that is
+ * sent back to the stream enable request.
+ *
+ */
+static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs,
+ void **xfer_buf_cpu, u32 xfer_buf_len,
+ struct mem_info_v01 *mem_info)
+{
+ struct sg_table xfer_buf_sgt;
+ dma_addr_t xfer_buf_dma;
+ void *xfer_buf;
+ u32 len = xfer_buf_len;
+ bool dma_coherent;
+ dma_addr_t xfer_buf_dma_sysdev;
+ u32 remainder;
+ u32 mult;
+ int ret;
+
+ dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
+
+ /* xfer buffer, multiple of 4K only */
+ if (!len)
+ len = PAGE_SIZE;
+
+ mult = len / PAGE_SIZE;
+ remainder = len % PAGE_SIZE;
+ len = mult * PAGE_SIZE;
+ len += remainder ? PAGE_SIZE : 0;
+
+ if (len > MAX_XFER_BUFF_LEN) {
+ dev_err(uaudio_qdev->data->dev,
+ "req buf len %d > max buf len %lu, setting %lu\n",
+ len, MAX_XFER_BUFF_LEN, MAX_XFER_BUFF_LEN);
+ len = MAX_XFER_BUFF_LEN;
+ }
+
+ /* get buffer mapped into subs->dev */
+ xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_dma);
+ if (!xfer_buf)
+ return -ENOMEM;
+
+ dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf,
+ xfer_buf_dma, len);
+
+ /* map the physical buffer into sysdev as well */
+ xfer_buf_dma_sysdev = uaudio_iommu_map_xfer_buf(dma_coherent,
+ len, &xfer_buf_sgt);
+ if (!xfer_buf_dma_sysdev) {
+ ret = -ENOMEM;
+ goto unmap_sync;
+ }
+
+ mem_info->dma = xfer_buf_dma;
+ mem_info->size = len;
+ mem_info->iova = PREPEND_SID_TO_IOVA(xfer_buf_dma_sysdev, uaudio_qdev->data->sid);
+ *xfer_buf_cpu = xfer_buf;
+ sg_free_table(&xfer_buf_sgt);
+
+ return 0;
+
+unmap_sync:
+ usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_dma);
+
+ return ret;
+}
+
+/**
+ * uaudio_endpoint_setup() - fetch and populate endpoint params
+ * @subs: usb substream
+ * @endpoint: usb endpoint to add
+ * @card_num: uadev index
+ * @mem_info: QMI response info
+ * @ep_desc: QMI ep desc response field
+ *
+ * Initialize the USB endpoint being used for a particular USB
+ * stream. Will request XHCI sec intr to reserve the EP for
+ * offloading as well as populating the QMI response with the
+ * transfer ring parameters.
+ *
+ */
+static phys_addr_t
+uaudio_endpoint_setup(struct snd_usb_substream *subs,
+ struct snd_usb_endpoint *endpoint, int card_num,
+ struct mem_info_v01 *mem_info,
+ struct usb_endpoint_descriptor_v01 *ep_desc)
+{
+ struct usb_host_endpoint *ep;
+ phys_addr_t tr_pa = 0;
+ struct sg_table *sgt;
+ bool dma_coherent;
+ unsigned long iova;
+ struct page *pg;
+ int ret = -ENODEV;
+
+ dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
+
+ ep = usb_pipe_endpoint(subs->dev, endpoint->pipe);
+ if (!ep) {
+ dev_err(uaudio_qdev->data->dev, "data ep # %d context is null\n",
+ subs->data_endpoint->ep_num);
+ goto exit;
+ }
+
+ memcpy(ep_desc, &ep->desc, sizeof(ep->desc));
+
+ ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep);
+ if (ret < 0) {
+ dev_err(&subs->dev->dev,
+ "failed to add data ep to sec intr\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ sgt = xhci_sideband_get_endpoint_buffer(uadev[card_num].sb, ep);
+ if (!sgt) {
+ dev_err(&subs->dev->dev,
+ "failed to get data ep ring address\n");
+ ret = -ENODEV;
+ goto remove_ep;
+ }
+
+ pg = sg_page(sgt->sgl);
+ tr_pa = page_to_phys(pg);
+ mem_info->dma = sg_dma_address(sgt->sgl);
+ sg_free_table(sgt);
+
+ /* data transfer ring */
+ iova = uaudio_iommu_map_pa(MEM_XFER_RING, dma_coherent, tr_pa,
+ PAGE_SIZE);
+ if (!iova) {
+ ret = -ENOMEM;
+ goto clear_pa;
+ }
+
+ mem_info->iova = PREPEND_SID_TO_IOVA(iova, uaudio_qdev->data->sid);
+ mem_info->size = PAGE_SIZE;
+
+ return 0;
+
+clear_pa:
+ mem_info->dma = 0;
+remove_ep:
+ xhci_sideband_remove_endpoint(uadev[card_num].sb, ep);
+exit:
+ return ret;
+}
+
+/**
+ * uaudio_event_ring_setup() - fetch and populate event ring params
+ * @subs: usb substream
+ * @card_num: uadev index
+ * @mem_info: QMI response info
+ *
+ * Register secondary interrupter to XHCI and fetch the event buffer info
+ * and populate the information into the QMI response.
+ *
+ */
+static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
+ int card_num, struct mem_info_v01 *mem_info)
+{
+ struct sg_table *sgt;
+ phys_addr_t er_pa;
+ bool dma_coherent;
+ unsigned long iova;
+ struct page *pg;
+ int ret;
+
+ dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
+ er_pa = 0;
+
+ /* event ring */
+ ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false,
+ 0, uaudio_qdev->data->intr_num);
+ if (ret < 0) {
+ dev_err(&subs->dev->dev, "failed to fetch interrupter\n");
+ goto exit;
+ }
+
+ sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb);
+ if (!sgt) {
+ dev_err(&subs->dev->dev,
+ "failed to get event ring address\n");
+ ret = -ENODEV;
+ goto remove_interrupter;
+ }
+
+ pg = sg_page(sgt->sgl);
+ er_pa = page_to_phys(pg);
+ mem_info->dma = sg_dma_address(sgt->sgl);
+ sg_free_table(sgt);
+
+ iova = uaudio_iommu_map_pa(MEM_EVENT_RING, dma_coherent, er_pa,
+ PAGE_SIZE);
+ if (!iova) {
+ ret = -ENOMEM;
+ goto clear_pa;
+ }
+
+ mem_info->iova = PREPEND_SID_TO_IOVA(iova, uaudio_qdev->data->sid);
+ mem_info->size = PAGE_SIZE;
+
+ return 0;
+
+clear_pa:
+ mem_info->dma = 0;
+remove_interrupter:
+ xhci_sideband_remove_interrupter(uadev[card_num].sb);
+exit:
+ return ret;
+}
+
+/**
+ * uaudio_populate_uac_desc() - parse UAC parameters and populate QMI resp
+ * @subs: usb substream
+ * @resp: QMI response buffer
+ *
+ * Parses information specified within UAC descriptors which explain the
+ * sample parameters that the device expects. This information is populated
+ * to the QMI response sent back to the audio DSP.
+ *
+ */
+static int uaudio_populate_uac_desc(struct snd_usb_substream *subs,
+ struct qmi_uaudio_stream_resp_msg_v01 *resp)
+{
+ struct usb_interface_descriptor *altsd;
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ int protocol;
+
+ iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
+ if (!iface) {
+ dev_err(&subs->dev->dev, "interface # %d does not exist\n",
+ subs->cur_audiofmt->iface);
+ return -ENODEV;
+ }
+
+ alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
+ altsd = get_iface_desc(alts);
+ protocol = altsd->bInterfaceProtocol;
+
+ if (protocol == UAC_VERSION_1) {
+ struct uac1_as_header_descriptor *as;
+
+ as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_AS_GENERAL);
+ if (!as) {
+ dev_err(&subs->dev->dev,
+ "%u:%d : no UAC_AS_GENERAL desc\n",
+ subs->cur_audiofmt->iface,
+ subs->cur_audiofmt->altset_idx);
+ return -ENODEV;
+ }
+
+ resp->data_path_delay = as->bDelay;
+ resp->data_path_delay_valid = 1;
+
+ resp->usb_audio_subslot_size = subs->cur_audiofmt->fmt_sz;
+ resp->usb_audio_subslot_size_valid = 1;
+
+ resp->usb_audio_spec_revision = le16_to_cpu((__force __le16)0x0100);
+ resp->usb_audio_spec_revision_valid = 1;
+ } else if (protocol == UAC_VERSION_2) {
+ resp->usb_audio_subslot_size = subs->cur_audiofmt->fmt_sz;
+ resp->usb_audio_subslot_size_valid = 1;
+
+ resp->usb_audio_spec_revision = le16_to_cpu((__force __le16)0x0200);
+ resp->usb_audio_spec_revision_valid = 1;
+ } else if (protocol == UAC_VERSION_3) {
+ if (iface->intf_assoc->bFunctionSubClass ==
+ UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0) {
+ dev_err(&subs->dev->dev,
+ "full adc is not supported\n");
+ return -EINVAL;
+ }
+
+ switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: {
+ resp->usb_audio_subslot_size = 0x2;
+ break;
+ }
+
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24:
+ case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
+ case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: {
+ resp->usb_audio_subslot_size = 0x3;
+ break;
+ }
+
+ default:
+ dev_err(&subs->dev->dev,
+ "%d: %u: Invalid wMaxPacketSize\n",
+ subs->cur_audiofmt->iface,
+ subs->cur_audiofmt->altset_idx);
+ return -EINVAL;
+ }
+ resp->usb_audio_subslot_size_valid = 1;
+ } else {
+ dev_err(&subs->dev->dev, "unknown protocol version %x\n",
+ protocol);
+ return -ENODEV;
+ }
+
+ memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
+
+ return 0;
+}
+
+/**
+ * prepare_qmi_response() - prepare stream enable response
+ * @subs: usb substream
+ * @req_msg: QMI request message
+ * @resp: QMI response buffer
+ * @info_idx: usb interface array index
+ *
+ * Prepares the QMI response for a USB QMI stream enable request. Will parse
+ * out the parameters within the stream enable request, in order to match
+ * requested audio profile to the ones exposed by the USB device connected.
+ *
+ * In addition, will fetch the XHCI transfer resources needed for the handoff to
+ * happen. This includes, transfer ring and buffer addresses and secondary event
+ * ring address. These parameters will be communicated as part of the USB QMI
+ * stream enable response.
+ *
+ */
+static int prepare_qmi_response(struct snd_usb_substream *subs,
+ struct qmi_uaudio_stream_req_msg_v01 *req_msg,
+ struct qmi_uaudio_stream_resp_msg_v01 *resp,
+ int info_idx)
+{
+ struct q6usb_offload *data;
+ int pcm_dev_num;
+ int card_num;
+ void *xfer_buf_cpu;
+ int ret;
+
+ pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
+ card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
+
+ if (!uadev[card_num].ctrl_intf) {
+ dev_err(&subs->dev->dev, "audio ctrl intf info not cached\n");
+ return -ENODEV;
+ }
+
+ ret = uaudio_populate_uac_desc(subs, resp);
+ if (ret < 0)
+ return ret;
+
+ resp->slot_id = subs->dev->slot_id;
+ resp->slot_id_valid = 1;
+
+ data = snd_soc_usb_find_priv_data(uaudio_qdev->auxdev->dev.parent);
+ if (!data) {
+ dev_err(&subs->dev->dev, "No private data found\n");
+ return -ENODEV;
+ }
+
+ uaudio_qdev->data = data;
+
+ resp->std_as_opr_intf_desc_valid = 1;
+ ret = uaudio_endpoint_setup(subs, subs->data_endpoint, card_num,
+ &resp->xhci_mem_info.tr_data,
+ &resp->std_as_data_ep_desc);
+ if (ret < 0)
+ return ret;
+
+ resp->std_as_data_ep_desc_valid = 1;
+
+ if (subs->sync_endpoint) {
+ ret = uaudio_endpoint_setup(subs, subs->sync_endpoint, card_num,
+ &resp->xhci_mem_info.tr_sync,
+ &resp->std_as_sync_ep_desc);
+ if (ret < 0)
+ goto drop_data_ep;
+
+ resp->std_as_sync_ep_desc_valid = 1;
+ }
+
+ resp->interrupter_num_valid = 1;
+ resp->controller_num_valid = 0;
+ ret = usb_get_controller_id(subs->dev);
+ if (ret >= 0) {
+ resp->controller_num = ret;
+ resp->controller_num_valid = 1;
+ }
+
+ /* event ring */
+ ret = uaudio_event_ring_setup(subs, card_num,
+ &resp->xhci_mem_info.evt_ring);
+ if (ret < 0)
+ goto drop_sync_ep;
+
+ uaudio_qdev->er_mapped = true;
+ resp->interrupter_num = xhci_sideband_interrupter_id(uadev[card_num].sb);
+
+ resp->speed_info = get_speed_info(subs->dev->speed);
+ if (resp->speed_info == USB_QMI_DEVICE_SPEED_INVALID_V01) {
+ ret = -ENODEV;
+ goto free_sec_ring;
+ }
+
+ resp->speed_info_valid = 1;
+
+ ret = uaudio_transfer_buffer_setup(subs, &xfer_buf_cpu, req_msg->xfer_buff_size,
+ &resp->xhci_mem_info.xfer_buff);
+ if (ret < 0) {
+ ret = -ENOMEM;
+ goto free_sec_ring;
+ }
+
+ resp->xhci_mem_info_valid = 1;
+
+ if (!atomic_read(&uadev[card_num].in_use)) {
+ kref_init(&uadev[card_num].kref);
+ init_waitqueue_head(&uadev[card_num].disconnect_wq);
+ uadev[card_num].num_intf =
+ subs->dev->config->desc.bNumInterfaces;
+ uadev[card_num].info = kcalloc(uadev[card_num].num_intf,
+ sizeof(struct intf_info),
+ GFP_KERNEL);
+ if (!uadev[card_num].info) {
+ ret = -ENOMEM;
+ goto unmap_er;
+ }
+ uadev[card_num].udev = subs->dev;
+ atomic_set(&uadev[card_num].in_use, 1);
+ } else {
+ kref_get(&uadev[card_num].kref);
+ }
+
+ uadev[card_num].usb_core_id = resp->controller_num;
+
+ /* cache intf specific info to use it for unmap and free xfer buf */
+ uadev[card_num].info[info_idx].data_xfer_ring_va =
+ IOVA_MASK(resp->xhci_mem_info.tr_data.iova);
+ uadev[card_num].info[info_idx].data_xfer_ring_size = PAGE_SIZE;
+ uadev[card_num].info[info_idx].sync_xfer_ring_va =
+ IOVA_MASK(resp->xhci_mem_info.tr_sync.iova);
+ uadev[card_num].info[info_idx].sync_xfer_ring_size = PAGE_SIZE;
+ uadev[card_num].info[info_idx].xfer_buf_iova =
+ IOVA_MASK(resp->xhci_mem_info.xfer_buff.iova);
+ uadev[card_num].info[info_idx].xfer_buf_dma =
+ resp->xhci_mem_info.xfer_buff.dma;
+ uadev[card_num].info[info_idx].xfer_buf_size =
+ resp->xhci_mem_info.xfer_buff.size;
+ uadev[card_num].info[info_idx].data_ep_pipe = subs->data_endpoint ?
+ subs->data_endpoint->pipe : 0;
+ uadev[card_num].info[info_idx].sync_ep_pipe = subs->sync_endpoint ?
+ subs->sync_endpoint->pipe : 0;
+ uadev[card_num].info[info_idx].data_ep_idx = subs->data_endpoint ?
+ subs->data_endpoint->ep_num : 0;
+ uadev[card_num].info[info_idx].sync_ep_idx = subs->sync_endpoint ?
+ subs->sync_endpoint->ep_num : 0;
+ uadev[card_num].info[info_idx].xfer_buf_cpu = xfer_buf_cpu;
+ uadev[card_num].info[info_idx].pcm_card_num = card_num;
+ uadev[card_num].info[info_idx].pcm_dev_num = pcm_dev_num;
+ uadev[card_num].info[info_idx].direction = subs->direction;
+ uadev[card_num].info[info_idx].intf_num = subs->cur_audiofmt->iface;
+ uadev[card_num].info[info_idx].in_use = true;
+
+ set_bit(card_num, &uaudio_qdev->card_slot);
+
+ return 0;
+
+unmap_er:
+ uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE);
+free_sec_ring:
+ xhci_sideband_remove_interrupter(uadev[card_num].sb);
+drop_sync_ep:
+ if (subs->sync_endpoint) {
+ uaudio_iommu_unmap(MEM_XFER_RING,
+ IOVA_MASK(resp->xhci_mem_info.tr_sync.iova),
+ PAGE_SIZE, PAGE_SIZE);
+ xhci_sideband_remove_endpoint(uadev[card_num].sb,
+ usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe));
+ }
+drop_data_ep:
+ uaudio_iommu_unmap(MEM_XFER_RING, IOVA_MASK(resp->xhci_mem_info.tr_data.iova),
+ PAGE_SIZE, PAGE_SIZE);
+ xhci_sideband_remove_endpoint(uadev[card_num].sb,
+ usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe));
+
+ return ret;
+}
+
+/**
+ * handle_uaudio_stream_req() - handle stream enable/disable request
+ * @handle: QMI client handle
+ * @sq: qrtr socket
+ * @txn: QMI transaction context
+ * @decoded_msg: decoded QMI message
+ *
+ * Main handler for the QMI stream enable/disable requests. This executes the
+ * corresponding enable/disable stream apis, respectively.
+ *
+ */
+static void handle_uaudio_stream_req(struct qmi_handle *handle,
+ struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn,
+ const void *decoded_msg)
+{
+ struct qmi_uaudio_stream_req_msg_v01 *req_msg;
+ struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0};
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+ struct snd_usb_audio *chip = NULL;
+ struct snd_usb_substream *subs;
+ struct usb_host_endpoint *ep;
+ int datainterval = -EINVAL;
+ int info_idx = -EINVAL;
+ struct intf_info *info;
+ u8 pcm_card_num;
+ u8 pcm_dev_num;
+ u8 direction;
+ int ret = 0;
+
+ if (!svc->client_connected) {
+ svc->client_sq = *sq;
+ svc->client_connected = true;
+ }
+
+ mutex_lock(&qdev_mutex);
+ req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg;
+ if (!req_msg->audio_format_valid || !req_msg->bit_rate_valid ||
+ !req_msg->number_of_ch_valid || !req_msg->xfer_buff_size_valid) {
+ ret = -EINVAL;
+ goto response;
+ }
+
+ if (!uaudio_qdev) {
+ ret = -EINVAL;
+ goto response;
+ }
+
+ direction = (req_msg->usb_token & QMI_STREAM_REQ_DIRECTION);
+ pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
+ pcm_card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
+ if (pcm_card_num >= SNDRV_CARDS) {
+ ret = -EINVAL;
+ goto response;
+ }
+
+ if (req_msg->audio_format > USB_QMI_PCM_FORMAT_U32_BE) {
+ ret = -EINVAL;
+ goto response;
+ }
+
+ subs = find_substream(pcm_card_num, pcm_dev_num, direction);
+ chip = uadev[pcm_card_num].chip;
+ if (!subs || !chip || atomic_read(&chip->shutdown)) {
+ ret = -ENODEV;
+ goto response;
+ }
+
+ info_idx = info_idx_from_ifnum(pcm_card_num, subs->cur_audiofmt ?
+ subs->cur_audiofmt->iface : -1, req_msg->enable);
+ if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm ||
+ !subs->stream->chip) {
+ ret = -ENODEV;
+ 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;
+ }
+ }
+
+ if (req_msg->service_interval_valid) {
+ ret = get_data_interval_from_si(subs,
+ req_msg->service_interval);
+ if (ret == -EINVAL)
+ goto response;
+
+ datainterval = ret;
+ }
+
+ uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
+
+ if (req_msg->enable) {
+ ret = enable_audio_stream(subs,
+ map_pcm_format(req_msg->audio_format),
+ req_msg->number_of_ch, req_msg->bit_rate,
+ datainterval);
+
+ if (!ret)
+ ret = prepare_qmi_response(subs, req_msg, &resp,
+ info_idx);
+ if (ret < 0) {
+ guard(mutex)(&chip->mutex);
+ subs->opened = 0;
+ }
+ } else {
+ info = &uadev[pcm_card_num].info[info_idx];
+ if (info->data_ep_pipe) {
+ ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
+ info->data_ep_pipe);
+ if (ep) {
+ xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
+ ep);
+ xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
+ ep);
+ }
+
+ info->data_ep_pipe = 0;
+ }
+
+ if (info->sync_ep_pipe) {
+ ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
+ info->sync_ep_pipe);
+ if (ep) {
+ xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
+ ep);
+ xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
+ ep);
+ }
+
+ info->sync_ep_pipe = 0;
+ }
+
+ disable_audio_stream(subs);
+ guard(mutex)(&chip->mutex);
+ subs->opened = 0;
+ }
+
+response:
+ if (!req_msg->enable && ret != -EINVAL && ret != -ENODEV) {
+ 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,
+ info);
+ }
+ if (atomic_read(&uadev[pcm_card_num].in_use))
+ kref_put(&uadev[pcm_card_num].kref,
+ uaudio_dev_release);
+ }
+ mutex_unlock(&qdev_mutex);
+
+ resp.usb_token = req_msg->usb_token;
+ resp.usb_token_valid = 1;
+ resp.internal_status = ret;
+ resp.internal_status_valid = 1;
+ resp.status = ret ? USB_QMI_STREAM_REQ_FAILURE_V01 : ret;
+ resp.status_valid = 1;
+ ret = qmi_send_response(svc->uaudio_svc_hdl, sq, txn,
+ QMI_UAUDIO_STREAM_RESP_V01,
+ QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN,
+ qmi_uaudio_stream_resp_msg_v01_ei, &resp);
+}
+
+static struct qmi_msg_handler uaudio_stream_req_handlers = {
+ .type = QMI_REQUEST,
+ .msg_id = QMI_UAUDIO_STREAM_REQ_V01,
+ .ei = qmi_uaudio_stream_req_msg_v01_ei,
+ .decoded_size = QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN,
+ .fn = handle_uaudio_stream_req,
+};
+
+/**
+ * qc_usb_audio_offload_init_qmi_dev() - initializes qmi dev
+ *
+ * Initializes the USB qdev, which is used to carry information pertaining to
+ * the offloading resources. This device is freed only when there are no longer
+ * any offloading candidates. (i.e, when all audio devices are disconnected)
+ *
+ */
+static int qc_usb_audio_offload_init_qmi_dev(void)
+{
+ uaudio_qdev = kzalloc(sizeof(*uaudio_qdev), GFP_KERNEL);
+ if (!uaudio_qdev)
+ return -ENOMEM;
+
+ /* initialize xfer ring and xfer buf iova list */
+ INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list);
+ uaudio_qdev->curr_xfer_ring_iova = IOVA_XFER_RING_BASE;
+ uaudio_qdev->xfer_ring_iova_size =
+ IOVA_XFER_RING_MAX - IOVA_XFER_RING_BASE;
+
+ INIT_LIST_HEAD(&uaudio_qdev->xfer_buf_list);
+ uaudio_qdev->curr_xfer_buf_iova = IOVA_XFER_BUF_BASE;
+ uaudio_qdev->xfer_buf_iova_size =
+ IOVA_XFER_BUF_MAX - IOVA_XFER_BUF_BASE;
+
+ return 0;
+}
+
+/* Populates ppcm_idx array with supported PCM indexes */
+static int qc_usb_audio_offload_fill_avail_pcms(struct snd_usb_audio *chip,
+ struct snd_soc_usb_device *sdev)
+{
+ struct snd_usb_stream *as;
+ struct snd_usb_substream *subs;
+ int idx = 0;
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK];
+ if (subs->ep_num) {
+ sdev->ppcm_idx[idx] = as->pcm->device;
+ idx++;
+ }
+ /*
+ * Break if the current index exceeds the number of possible
+ * playback streams counted from the UAC descriptors.
+ */
+ if (idx >= sdev->num_playback)
+ break;
+ }
+
+ return -1;
+}
+
+/**
+ * qc_usb_audio_offload_probe() - platform op connect handler
+ * @chip: USB SND device
+ *
+ * Platform connect handler when a USB SND device is detected. Will
+ * notify SOC USB about the connection to enable the USB ASoC backend
+ * and populate internal USB chip array.
+ *
+ */
+static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
+{
+ struct usb_interface *intf = chip->intf[chip->num_interfaces - 1];
+ struct usb_interface_descriptor *altsd;
+ struct usb_host_interface *alts;
+ struct snd_soc_usb_device *sdev;
+ struct xhci_sideband *sb;
+
+ /*
+ * If there is no priv_data, or no playback paths, the connected
+ * device doesn't support offloading. Avoid populating entries for
+ * this device.
+ */
+ if (!snd_soc_usb_find_priv_data(uaudio_qdev->auxdev->dev.parent) ||
+ !usb_qmi_get_pcm_num(chip, 0))
+ return;
+
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
+ if (!uadev[chip->card->number].chip) {
+ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return;
+
+ sb = xhci_sideband_register(intf, XHCI_SIDEBAND_VENDOR,
+ uaudio_sideband_notifier);
+ if (!sb)
+ goto free_sdev;
+ } else {
+ sb = uadev[chip->card->number].sb;
+ sdev = uadev[chip->card->number].sdev;
+ }
+
+ uadev[chip->card->number].sb = sb;
+ uadev[chip->card->number].chip = chip;
+ uadev[chip->card->number].sdev = sdev;
+
+ alts = &intf->altsetting[0];
+ altsd = get_iface_desc(alts);
+
+ /* Wait until all PCM devices are populated before notifying soc-usb */
+ if (altsd->bInterfaceNumber == chip->last_iface) {
+ sdev->num_playback = usb_qmi_get_pcm_num(chip, 0);
+
+ /*
+ * Allocate playback pcm index array based on number of possible
+ * playback paths within the UAC descriptors.
+ */
+ sdev->ppcm_idx = kcalloc(sdev->num_playback, sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!sdev->ppcm_idx)
+ goto unreg_xhci;
+
+ qc_usb_audio_offload_fill_avail_pcms(chip, sdev);
+ sdev->card_idx = chip->card->number;
+ sdev->chip_idx = chip->index;
+
+ snd_usb_offload_create_ctl(chip, uaudio_qdev->auxdev->dev.parent);
+ snd_soc_usb_connect(uaudio_qdev->auxdev->dev.parent, sdev);
+ }
+
+ return;
+
+unreg_xhci:
+ xhci_sideband_unregister(sb);
+ uadev[chip->card->number].sb = NULL;
+free_sdev:
+ kfree(sdev);
+ uadev[chip->card->number].sdev = NULL;
+ uadev[chip->card->number].chip = NULL;
+}
+
+/**
+ * qc_usb_audio_cleanup_qmi_dev() - release qmi device
+ *
+ * Frees the USB qdev. Only occurs when there are no longer any potential
+ * devices that can utilize USB audio offloading.
+ *
+ */
+static void qc_usb_audio_cleanup_qmi_dev(void)
+{
+ kfree(uaudio_qdev);
+ uaudio_qdev = NULL;
+}
+
+/**
+ * qc_usb_audio_offload_disconnect() - platform op disconnect handler
+ * @chip: USB SND device
+ *
+ * Platform disconnect handler. Will ensure that any pending stream is
+ * halted by issuing a QMI disconnect indication packet to the adsp.
+ *
+ */
+static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
+{
+ struct uaudio_dev *dev;
+ int card_num;
+
+ if (!chip)
+ return;
+
+ card_num = chip->card->number;
+ if (card_num >= SNDRV_CARDS)
+ return;
+
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
+ dev = &uadev[card_num];
+
+ /* Device has already been cleaned up, or never populated */
+ if (!dev->chip)
+ return;
+
+ /* cleaned up already */
+ if (!dev->udev)
+ goto done;
+
+ uaudio_send_disconnect_ind(chip);
+ uaudio_dev_cleanup(dev);
+done:
+ /*
+ * If num_interfaces == 1, the last USB SND interface is being removed.
+ * This is to accommodate for devices w/ multiple UAC functions.
+ */
+ if (chip->num_interfaces == 1) {
+ snd_soc_usb_disconnect(uaudio_qdev->auxdev->dev.parent, dev->sdev);
+ xhci_sideband_unregister(dev->sb);
+ dev->chip = NULL;
+ kfree(dev->sdev->ppcm_idx);
+ kfree(dev->sdev);
+ dev->sdev = NULL;
+ }
+}
+
+/**
+ * qc_usb_audio_offload_suspend() - USB offload PM suspend handler
+ * @intf: USB interface
+ * @message: suspend type
+ *
+ * PM suspend handler to ensure that the USB offloading driver is able to stop
+ * any pending traffic, so that the bus can be suspended.
+ *
+ */
+static void qc_usb_audio_offload_suspend(struct usb_interface *intf,
+ pm_message_t message)
+{
+ struct snd_usb_audio *chip = usb_get_intfdata(intf);
+ int card_num;
+
+ if (!chip)
+ return;
+
+ card_num = chip->card->number;
+ if (card_num >= SNDRV_CARDS)
+ return;
+
+ guard(mutex)(&qdev_mutex);
+ guard(mutex)(&chip->mutex);
+
+ uaudio_send_disconnect_ind(chip);
+}
+
+static struct snd_usb_platform_ops offload_ops = {
+ .connect_cb = qc_usb_audio_offload_probe,
+ .disconnect_cb = qc_usb_audio_offload_disconnect,
+ .suspend_cb = qc_usb_audio_offload_suspend,
+};
+
+static int qc_usb_audio_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+
+{
+ struct uaudio_qmi_svc *svc;
+ int ret;
+
+ svc = kzalloc(sizeof(*svc), GFP_KERNEL);
+ if (!svc)
+ return -ENOMEM;
+
+ svc->uaudio_svc_hdl = kzalloc(sizeof(*svc->uaudio_svc_hdl), GFP_KERNEL);
+ if (!svc->uaudio_svc_hdl) {
+ ret = -ENOMEM;
+ goto free_svc;
+ }
+
+ ret = qmi_handle_init(svc->uaudio_svc_hdl,
+ QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN,
+ &uaudio_svc_ops_options,
+ &uaudio_stream_req_handlers);
+ ret = qmi_add_server(svc->uaudio_svc_hdl, UAUDIO_STREAM_SERVICE_ID_V01,
+ UAUDIO_STREAM_SERVICE_VERS_V01, 0);
+
+ uaudio_svc = svc;
+
+ qc_usb_audio_offload_init_qmi_dev();
+ uaudio_qdev->auxdev = auxdev;
+
+ ret = snd_usb_register_platform_ops(&offload_ops);
+ if (ret < 0)
+ goto release_qmi;
+
+ snd_usb_rediscover_devices();
+
+ return 0;
+
+release_qmi:
+ qc_usb_audio_cleanup_qmi_dev();
+ qmi_handle_release(svc->uaudio_svc_hdl);
+free_svc:
+ kfree(svc);
+
+ return ret;
+}
+
+static void qc_usb_audio_remove(struct auxiliary_device *auxdev)
+{
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+ int idx;
+
+ /*
+ * Remove all connected devices after unregistering ops, to ensure
+ * that no further connect events will occur. The disconnect routine
+ * will issue the QMI disconnect indication, which results in the
+ * external DSP to stop issuing transfers.
+ */
+ snd_usb_unregister_platform_ops();
+ for (idx = 0; idx < SNDRV_CARDS; idx++)
+ qc_usb_audio_offload_disconnect(uadev[idx].chip);
+
+ qc_usb_audio_cleanup_qmi_dev();
+
+ qmi_handle_release(svc->uaudio_svc_hdl);
+ kfree(svc);
+ uaudio_svc = NULL;
+}
+
+static const struct auxiliary_device_id qc_usb_audio_table[] = {
+ { .name = "q6usb.qc-usb-audio-offload" },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, qc_usb_audio_table);
+
+static struct auxiliary_driver qc_usb_audio_offload_drv = {
+ .name = "qc-usb-audio-offload",
+ .id_table = qc_usb_audio_table,
+ .probe = qc_usb_audio_probe,
+ .remove = qc_usb_audio_remove,
+};
+module_auxiliary_driver(qc_usb_audio_offload_drv);
+
+MODULE_DESCRIPTION("QC USB Audio Offloading");
+MODULE_LICENSE("GPL");
diff --git a/sound/usb/qcom/usb_audio_qmi_v01.c b/sound/usb/qcom/usb_audio_qmi_v01.c
new file mode 100644
index 000000000000..502857612277
--- /dev/null
+++ b/sound/usb/qcom/usb_audio_qmi_v01.c
@@ -0,0 +1,863 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/soc/qcom/qmi.h>
+
+#include "usb_audio_qmi_v01.h"
+
+static const struct qmi_elem_info mem_info_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u64),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct mem_info_v01, iova),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u64),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct mem_info_v01, dma),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct mem_info_v01, size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static const struct qmi_elem_info apps_mem_info_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01, evt_ring),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01, tr_data),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01, tr_sync),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01, xfer_buff),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct apps_mem_info_v01, dcba),
+ .ei_array = mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static const struct qmi_elem_info usb_endpoint_descriptor_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bLength),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bDescriptorType),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bEndpointAddress),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bmAttributes),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u16),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ wMaxPacketSize),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bInterval),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bRefresh),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_endpoint_descriptor_v01,
+ bSynchAddress),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static const struct qmi_elem_info usb_interface_descriptor_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bLength),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bDescriptorType),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceNumber),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bAlternateSetting),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bNumEndpoints),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceClass),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceSubClass),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ bInterfaceProtocol),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct usb_interface_descriptor_v01,
+ iInterface),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+const struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ enable),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ usb_token),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ audio_format_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ audio_format),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ number_of_ch_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ number_of_ch),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ bit_rate_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ bit_rate),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ xfer_buff_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ xfer_buff_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ service_interval_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+ service_interval),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+const struct qmi_elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ resp),
+ .ei_array = qmi_response_type_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ status_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum usb_qmi_audio_stream_status_enum_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ status),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ internal_status_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ internal_status),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ slot_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ slot_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ usb_token_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ usb_token),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_opr_intf_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_interface_descriptor_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_opr_intf_desc),
+ .ei_array = usb_interface_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_data_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_data_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_sync_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ std_as_sync_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_spec_revision_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u16),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_spec_revision),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ data_path_delay_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ data_path_delay),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_subslot_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ usb_audio_subslot_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ xhci_mem_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct apps_mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1A,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ xhci_mem_info),
+ .ei_array = apps_mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ interrupter_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1B,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ interrupter_num),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1C,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ speed_info_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum usb_qmi_audio_device_speed_enum_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1C,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ speed_info),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1D,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ controller_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1D,
+ .offset = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
+ controller_num),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+const struct qmi_elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(
+ enum usb_qmi_audio_device_indication_enum_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ dev_event),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ slot_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_token_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_token),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_opr_intf_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_interface_descriptor_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_opr_intf_desc),
+ .ei_array = usb_interface_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_data_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_data_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_sync_ep_desc_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct usb_endpoint_descriptor_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ std_as_sync_ep_desc),
+ .ei_array = usb_endpoint_descriptor_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_spec_revision_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u16),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_spec_revision),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ data_path_delay_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ data_path_delay),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_subslot_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ usb_audio_subslot_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ xhci_mem_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct apps_mem_info_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ xhci_mem_info),
+ .ei_array = apps_mem_info_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ interrupter_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ interrupter_num),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ controller_num_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+ controller_num),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/sound/usb/qcom/usb_audio_qmi_v01.h b/sound/usb/qcom/usb_audio_qmi_v01.h
new file mode 100644
index 000000000000..a1298d75d9f8
--- /dev/null
+++ b/sound/usb/qcom/usb_audio_qmi_v01.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef USB_QMI_V01_H
+#define USB_QMI_V01_H
+
+#define UAUDIO_STREAM_SERVICE_ID_V01 0x41D
+#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01
+
+#define QMI_UAUDIO_STREAM_RESP_V01 0x0001
+#define QMI_UAUDIO_STREAM_REQ_V01 0x0001
+#define QMI_UAUDIO_STREAM_IND_V01 0x0001
+
+struct mem_info_v01 {
+ u64 iova; /* mapped into sysdev */
+ u64 dma; /* mapped into usb host */
+ u32 size;
+};
+
+struct apps_mem_info_v01 {
+ struct mem_info_v01 evt_ring;
+ struct mem_info_v01 tr_data;
+ struct mem_info_v01 tr_sync;
+ struct mem_info_v01 xfer_buff;
+ struct mem_info_v01 dcba;
+};
+
+struct usb_endpoint_descriptor_v01 {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+ u16 wMaxPacketSize;
+ u8 bInterval;
+ u8 bRefresh;
+ u8 bSynchAddress;
+};
+
+struct usb_interface_descriptor_v01 {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bInterfaceNumber;
+ u8 bAlternateSetting;
+ u8 bNumEndpoints;
+ u8 bInterfaceClass;
+ u8 bInterfaceSubClass;
+ u8 bInterfaceProtocol;
+ u8 iInterface;
+};
+
+enum usb_qmi_audio_stream_status_enum_v01 {
+ USB_QMI_STREAM_STATUS_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_QMI_STREAM_REQ_SUCCESS_V01 = 0,
+ USB_QMI_STREAM_REQ_FAILURE_V01 = 1,
+ USB_QMI_STREAM_REQ_FAILURE_NOT_FOUND_V01 = 2,
+ USB_QMI_STREAM_REQ_FAILURE_INVALID_PARAM_V01 = 3,
+ USB_QMI_STREAM_REQ_FAILURE_MEMALLOC_V01 = 4,
+ USB_QMI_STREAM_STATUS_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+enum usb_qmi_audio_device_indication_enum_v01 {
+ USB_QMI_DEVICE_INDICATION_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_QMI_DEV_CONNECT_V01 = 0,
+ USB_QMI_DEV_DISCONNECT_V01 = 1,
+ USB_QMI_DEV_SUSPEND_V01 = 2,
+ USB_QMI_DEV_RESUME_V01 = 3,
+ USB_QMI_DEVICE_INDICATION_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+enum usb_qmi_audio_device_speed_enum_v01 {
+ USB_QMI_DEVICE_SPEED_ENUM_MIN_VAL_V01 = INT_MIN,
+ USB_QMI_DEVICE_SPEED_INVALID_V01 = 0,
+ USB_QMI_DEVICE_SPEED_LOW_V01 = 1,
+ USB_QMI_DEVICE_SPEED_FULL_V01 = 2,
+ USB_QMI_DEVICE_SPEED_HIGH_V01 = 3,
+ USB_QMI_DEVICE_SPEED_SUPER_V01 = 4,
+ USB_QMI_DEVICE_SPEED_SUPER_PLUS_V01 = 5,
+ USB_QMI_DEVICE_SPEED_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+struct qmi_uaudio_stream_req_msg_v01 {
+ u8 enable;
+ u32 usb_token;
+ u8 audio_format_valid;
+ u32 audio_format;
+ u8 number_of_ch_valid;
+ u32 number_of_ch;
+ u8 bit_rate_valid;
+ u32 bit_rate;
+ u8 xfer_buff_size_valid;
+ u32 xfer_buff_size;
+ u8 service_interval_valid;
+ u32 service_interval;
+};
+
+#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46
+extern const struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[];
+
+struct qmi_uaudio_stream_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u8 status_valid;
+ enum usb_qmi_audio_stream_status_enum_v01 status;
+ u8 internal_status_valid;
+ u32 internal_status;
+ u8 slot_id_valid;
+ u32 slot_id;
+ u8 usb_token_valid;
+ u32 usb_token;
+ u8 std_as_opr_intf_desc_valid;
+ struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+ u8 std_as_data_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+ u8 std_as_sync_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+ u8 usb_audio_spec_revision_valid;
+ u16 usb_audio_spec_revision;
+ u8 data_path_delay_valid;
+ u8 data_path_delay;
+ u8 usb_audio_subslot_size_valid;
+ u8 usb_audio_subslot_size;
+ u8 xhci_mem_info_valid;
+ struct apps_mem_info_v01 xhci_mem_info;
+ u8 interrupter_num_valid;
+ u8 interrupter_num;
+ u8 speed_info_valid;
+ enum usb_qmi_audio_device_speed_enum_v01 speed_info;
+ u8 controller_num_valid;
+ u8 controller_num;
+};
+
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 202
+extern const struct qmi_elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
+
+struct qmi_uaudio_stream_ind_msg_v01 {
+ enum usb_qmi_audio_device_indication_enum_v01 dev_event;
+ u32 slot_id;
+ u8 usb_token_valid;
+ u32 usb_token;
+ u8 std_as_opr_intf_desc_valid;
+ struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+ u8 std_as_data_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+ u8 std_as_sync_ep_desc_valid;
+ struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+ u8 usb_audio_spec_revision_valid;
+ u16 usb_audio_spec_revision;
+ u8 data_path_delay_valid;
+ u8 data_path_delay;
+ u8 usb_audio_subslot_size_valid;
+ u8 usb_audio_subslot_size;
+ u8 xhci_mem_info_valid;
+ struct apps_mem_info_v01 xhci_mem_info;
+ u8 interrupter_num_valid;
+ u8 interrupter_num;
+ u8 controller_num_valid;
+ u8 controller_num;
+};
+
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 181
+extern const struct qmi_elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
+
+#endif
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 73abc38a5400..eafc0d73cca1 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -35,10 +35,87 @@
.bInterfaceClass = USB_CLASS_AUDIO, \
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL
+/* Quirk .driver_info, followed by the definition of the quirk entry;
+ * put like QUIRK_DRIVER_INFO { ... } in each entry of the quirk table
+ */
+#define QUIRK_DRIVER_INFO \
+ .driver_info = (unsigned long)&(const struct snd_usb_audio_quirk)
+
+/*
+ * Macros for quirk data entries
+ */
+
+/* Quirk data entry for ignoring the interface */
+#define QUIRK_DATA_IGNORE(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_IGNORE_INTERFACE
+/* Quirk data entry for a standard audio interface */
+#define QUIRK_DATA_STANDARD_AUDIO(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_AUDIO_STANDARD_INTERFACE
+/* Quirk data entry for a standard MIDI interface */
+#define QUIRK_DATA_STANDARD_MIDI(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_MIDI_STANDARD_INTERFACE
+/* Quirk data entry for a standard mixer interface */
+#define QUIRK_DATA_STANDARD_MIXER(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_AUDIO_STANDARD_MIXER
+
+/* Quirk data entry for Yamaha MIDI */
+#define QUIRK_DATA_MIDI_YAMAHA(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_MIDI_YAMAHA
+/* Quirk data entry for Edirol UAxx */
+#define QUIRK_DATA_EDIROL_UAXX(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_AUDIO_EDIROL_UAXX
+/* Quirk data entry for raw bytes interface */
+#define QUIRK_DATA_RAW_BYTES(_ifno) \
+ .ifnum = (_ifno), .type = QUIRK_MIDI_RAW_BYTES
+
+/* Quirk composite array terminator */
+#define QUIRK_COMPOSITE_END { .ifnum = -1 }
+
+/* Quirk data entry for composite quirks;
+ * followed by the quirk array that is terminated with QUIRK_COMPOSITE_END
+ * e.g. QUIRK_DATA_COMPOSITE { { quirk1 }, { quirk2 },..., QUIRK_COMPOSITE_END }
+ */
+#define QUIRK_DATA_COMPOSITE \
+ .ifnum = QUIRK_ANY_INTERFACE, \
+ .type = QUIRK_COMPOSITE, \
+ .data = &(const struct snd_usb_audio_quirk[])
+
+/* Quirk data entry for a fixed audio endpoint;
+ * followed by audioformat definition
+ * e.g. QUIRK_DATA_AUDIOFORMAT(n) { .formats = xxx, ... }
+ */
+#define QUIRK_DATA_AUDIOFORMAT(_ifno) \
+ .ifnum = (_ifno), \
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT, \
+ .data = &(const struct audioformat)
+
+/* Quirk data entry for a fixed MIDI endpoint;
+ * followed by snd_usb_midi_endpoint_info definition
+ * e.g. QUIRK_DATA_MIDI_FIXED_ENDPOINT(n) { .out_cables = x, .in_cables = y }
+ */
+#define QUIRK_DATA_MIDI_FIXED_ENDPOINT(_ifno) \
+ .ifnum = (_ifno), \
+ .type = QUIRK_MIDI_FIXED_ENDPOINT, \
+ .data = &(const struct snd_usb_midi_endpoint_info)
+/* Quirk data entry for a MIDIMAN MIDI endpoint */
+#define QUIRK_DATA_MIDI_MIDIMAN(_ifno) \
+ .ifnum = (_ifno), \
+ .type = QUIRK_MIDI_MIDIMAN, \
+ .data = &(const struct snd_usb_midi_endpoint_info)
+/* Quirk data entry for a EMAGIC MIDI endpoint */
+#define QUIRK_DATA_MIDI_EMAGIC(_ifno) \
+ .ifnum = (_ifno), \
+ .type = QUIRK_MIDI_EMAGIC, \
+ .data = &(const struct snd_usb_midi_endpoint_info)
+
+/*
+ * Here we go... the quirk table definition begins:
+ */
+
/* FTDI devices */
{
USB_DEVICE(0x0403, 0xb8d8),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "STARR LABS", */
/* .product_name = "Starr Labs MIDI USB device", */
.ifnum = 0,
@@ -49,10 +126,8 @@
{
/* Creative BT-D1 */
USB_DEVICE(0x041e, 0x0005),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 1,
@@ -87,18 +162,11 @@
*/
{
USB_AUDIO_DEVICE(0x041e, 0x4095),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(2) },
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- {
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(3) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.fmt_bits = 16,
@@ -114,9 +182,7 @@
.rate_table = (unsigned int[]) { 48000 },
},
},
- {
- .ifnum = -1
- },
+ QUIRK_COMPOSITE_END
},
},
},
@@ -128,31 +194,18 @@
*/
{
USB_DEVICE(0x0424, 0xb832),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Standard Microsystems Corp.",
.product_name = "HP Wireless Audio",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
/* Mixer */
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE,
- },
+ { QUIRK_DATA_IGNORE(0) },
/* Playback */
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE,
- },
+ { QUIRK_DATA_IGNORE(1) },
/* Capture */
- {
- .ifnum = 2,
- .type = QUIRK_IGNORE_INTERFACE,
- },
+ { QUIRK_DATA_IGNORE(2) },
/* HID Device, .ifnum = 3 */
- {
- .ifnum = -1,
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -175,20 +228,18 @@
#define YAMAHA_DEVICE(id, name) { \
USB_DEVICE(0x0499, id), \
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \
+ QUIRK_DRIVER_INFO { \
.vendor_name = "Yamaha", \
.product_name = name, \
- .ifnum = QUIRK_ANY_INTERFACE, \
- .type = QUIRK_MIDI_YAMAHA \
+ QUIRK_DATA_MIDI_YAMAHA(QUIRK_ANY_INTERFACE) \
} \
}
#define YAMAHA_INTERFACE(id, intf, name) { \
USB_DEVICE_VENDOR_SPEC(0x0499, id), \
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \
+ QUIRK_DRIVER_INFO { \
.vendor_name = "Yamaha", \
.product_name = name, \
- .ifnum = intf, \
- .type = QUIRK_MIDI_YAMAHA \
+ QUIRK_DATA_MIDI_YAMAHA(intf) \
} \
}
YAMAHA_DEVICE(0x1000, "UX256"),
@@ -275,135 +326,80 @@ YAMAHA_DEVICE(0x105c, NULL),
YAMAHA_DEVICE(0x105d, NULL),
{
USB_DEVICE(0x0499, 0x1503),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Yamaha", */
/* .product_name = "MOX6/MOX8", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_YAMAHA
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_MIDI_YAMAHA(3) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0499, 0x1507),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Yamaha", */
/* .product_name = "THR10", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_YAMAHA
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_MIDI_YAMAHA(3) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0499, 0x1509),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Yamaha", */
/* .product_name = "Steinberg UR22", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_YAMAHA
- },
- {
- .ifnum = 4,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_MIDI_YAMAHA(3) },
+ { QUIRK_DATA_IGNORE(4) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0499, 0x150a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Yamaha", */
/* .product_name = "THR5A", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_YAMAHA
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_MIDI_YAMAHA(3) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0499, 0x150c),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Yamaha", */
/* .product_name = "THR10C", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_YAMAHA
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_MIDI_YAMAHA(3) },
+ QUIRK_COMPOSITE_END
+ }
+ }
+},
+{
+ USB_DEVICE(0x0499, 0x1718),
+ QUIRK_DRIVER_INFO {
+ /* .vendor_name = "Yamaha", */
+ /* .product_name = "P-125", */
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_MIDI_YAMAHA(3) },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -437,7 +433,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = 0x0499,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_AUTODETECT
}
@@ -448,16 +444,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
*/
{
USB_DEVICE(0x0582, 0x0000),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "UA-100",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 4,
.iface = 0,
@@ -472,9 +464,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 1,
@@ -489,106 +479,66 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0007,
.in_cables = 0x0007
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0002),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UM-4",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x000f,
.in_cables = 0x000f
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0003),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "SC-8850",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x003f,
.in_cables = 0x003f
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0004),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "U-8",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0005,
.in_cables = 0x0005
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -596,152 +546,92 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Has ID 0x0099 when not in "Advanced Driver" mode.
* The UM-2EX has only one input, but we cannot detect this. */
USB_DEVICE(0x0582, 0x0005),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UM-2",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0003,
.in_cables = 0x0003
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0007),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "SC-8820",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0013,
.in_cables = 0x0013
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0008),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "PC-300",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x009d when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0009),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UM-1",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x000b),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "SK-500",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0013,
.in_cables = 0x0013
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -749,31 +639,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* thanks to Emiliano Grilli <emillo@libero.it>
* for helping researching this data */
USB_DEVICE(0x0582, 0x000c),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "SC-D70",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0007,
.in_cables = 0x0007
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -787,35 +665,23 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* the 96kHz sample rate.
*/
USB_DEVICE(0x0582, 0x0010),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-5",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x0013 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0012),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "XV-5050",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -824,12 +690,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0015 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0014),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UM-880",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x01ff,
.in_cables = 0x01ff
}
@@ -838,74 +702,48 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0017 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0016),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "SD-90",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x000f,
.in_cables = 0x000f
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x001c when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x001b),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "MMP-2",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x001e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x001d),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "V-SYNTH",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -914,12 +752,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0024 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0023),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UM-550",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x003f,
.in_cables = 0x003f
}
@@ -932,20 +768,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* and no MIDI.
*/
USB_DEVICE(0x0582, 0x0025),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-20",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 1,
@@ -960,9 +789,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 2,
@@ -977,28 +804,22 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(3) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x0028 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0027),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "SD-20",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0003,
.in_cables = 0x0007
}
@@ -1007,12 +828,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x002a when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0029),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "SD-80",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x000f,
.in_cables = 0x000f
}
@@ -1025,39 +844,24 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* but offers only 16-bit PCM and no MIDI.
*/
USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-700",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 3,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_EDIROL_UAXX(1) },
+ { QUIRK_DATA_EDIROL_UAXX(2) },
+ { QUIRK_DATA_EDIROL_UAXX(3) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x002e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x002d),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "XV-2020",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1066,12 +870,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0030 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x002f),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "VariOS",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0007,
.in_cables = 0x0007
}
@@ -1080,12 +882,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0034 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0033),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "PCR",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0003,
.in_cables = 0x0007
}
@@ -1097,12 +897,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* later revisions use IDs 0x0054 and 0x00a2.
*/
USB_DEVICE(0x0582, 0x0037),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "Digital Piano",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1115,39 +913,24 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* and no MIDI.
*/
USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "BOSS",
.product_name = "GS-10",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ { QUIRK_DATA_STANDARD_MIDI(3) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x0041 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0040),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "GI-20",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1156,12 +939,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0043 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0042),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "RS-70",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1170,36 +951,24 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0049 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0047),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "EDIROL", */
/* .product_name = "UR-80", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
/* in the 96 kHz modes, only interface 1 is there */
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x004a when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0048),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "EDIROL", */
/* .product_name = "UR-80", */
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0003,
.in_cables = 0x0007
}
@@ -1208,35 +977,23 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x004e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x004c),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "PCR-A",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x004f when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x004d),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "PCR-A",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0003,
.in_cables = 0x0007
}
@@ -1248,76 +1005,52 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* is standard compliant, but has only 16-bit PCM.
*/
USB_DEVICE(0x0582, 0x0050),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-3FX",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0052),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UM-1SX",
- .ifnum = 0,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ QUIRK_DATA_STANDARD_MIDI(0)
}
},
{
USB_DEVICE(0x0582, 0x0060),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "EXR Series",
- .ifnum = 0,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ QUIRK_DATA_STANDARD_MIDI(0)
}
},
{
/* has ID 0x0066 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0064),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "EDIROL", */
/* .product_name = "PCR-1", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x0067 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0065),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "EDIROL", */
/* .product_name = "PCR-1", */
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0003
}
@@ -1326,12 +1059,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x006e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x006d),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "FANTOM-X",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1344,39 +1075,24 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* offers only 16-bit PCM at 44.1 kHz and no MIDI.
*/
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0074),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-25",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_EDIROL_UAXX(0) },
+ { QUIRK_DATA_EDIROL_UAXX(1) },
+ { QUIRK_DATA_EDIROL_UAXX(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* has ID 0x0076 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0075),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "BOSS",
.product_name = "DR-880",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1385,12 +1101,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x007b when not in "Advanced Driver" mode */
USB_DEVICE_VENDOR_SPEC(0x0582, 0x007a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
/* "RD" or "RD-700SX"? */
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0003,
.in_cables = 0x0003
}
@@ -1399,12 +1113,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x0081 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0080),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Roland",
.product_name = "G-70",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1413,12 +1125,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* has ID 0x008c when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x008b),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "PC-50",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1430,56 +1140,31 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* is standard compliant, but has only 16-bit PCM and no MIDI.
*/
USB_DEVICE(0x0582, 0x00a3),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-4FX",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_EDIROL_UAXX(0) },
+ { QUIRK_DATA_EDIROL_UAXX(1) },
+ { QUIRK_DATA_EDIROL_UAXX(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* Edirol M-16DX */
USB_DEVICE(0x0582, 0x00c4),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -1489,37 +1174,22 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* offers only 16-bit PCM at 44.1 kHz and no MIDI.
*/
USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e6),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "EDIROL",
.product_name = "UA-25EX",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_EDIROL_UAXX(0) },
+ { QUIRK_DATA_EDIROL_UAXX(1) },
+ { QUIRK_DATA_EDIROL_UAXX(2) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* Edirol UM-3G */
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0108),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(0) {
.out_cables = 0x0007,
.in_cables = 0x0007
}
@@ -1528,45 +1198,29 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* BOSS ME-25 */
USB_DEVICE(0x0582, 0x0113),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* only 44.1 kHz works at the moment */
USB_DEVICE(0x0582, 0x0120),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Roland", */
/* .product_name = "OCTO-CAPTURE", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 10,
.iface = 0,
@@ -1582,9 +1236,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 12,
.iface = 1,
@@ -1600,40 +1252,26 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = 3,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 4,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_IGNORE(3) },
+ { QUIRK_DATA_IGNORE(4) },
+ QUIRK_COMPOSITE_END
}
}
},
{
/* only 44.1 kHz works at the moment */
USB_DEVICE(0x0582, 0x012f),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Roland", */
/* .product_name = "QUAD-CAPTURE", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 4,
.iface = 0,
@@ -1649,9 +1287,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 6,
.iface = 1,
@@ -1667,54 +1303,32 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = 3,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 4,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_IGNORE(3) },
+ { QUIRK_DATA_IGNORE(4) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0582, 0x0159),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Roland", */
/* .product_name = "UA-22", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(2) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -1722,19 +1336,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* UA101 and co are supported by another driver */
{
USB_DEVICE(0x0582, 0x0044), /* UA-1000 high speed */
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.ifnum = QUIRK_NODEV_INTERFACE
},
},
{
USB_DEVICE(0x0582, 0x007d), /* UA-101 high speed */
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.ifnum = QUIRK_NODEV_INTERFACE
},
},
{
USB_DEVICE(0x0582, 0x008d), /* UA-101 full speed */
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.ifnum = QUIRK_NODEV_INTERFACE
},
},
@@ -1745,7 +1359,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = 0x0582,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_AUTODETECT
}
@@ -1760,12 +1374,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* compliant USB MIDI ports for external MIDI and controls.
*/
USB_DEVICE_VENDOR_SPEC(0x06f8, 0xb000),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Hercules",
.product_name = "DJ Console (WE)",
- .ifnum = 4,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(4) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1775,12 +1387,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Midiman/M-Audio devices */
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1002),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "MidiSport 2x2",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x0003,
.in_cables = 0x0003
}
@@ -1788,12 +1398,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1011),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "MidiSport 1x1",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1801,12 +1409,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1015),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "Keystation",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1814,12 +1420,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1021),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "MidiSport 4x4",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x000f,
.in_cables = 0x000f
}
@@ -1832,12 +1436,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* Thanks to Olaf Giesbrecht <Olaf_Giesbrecht@yahoo.de>
*/
USB_DEVICE_VER(0x0763, 0x1031, 0x0100, 0x0109),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "MidiSport 8x8",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x01ff,
.in_cables = 0x01ff
}
@@ -1845,12 +1447,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1033),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "MidiSport 8x8",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x01ff,
.in_cables = 0x01ff
}
@@ -1858,12 +1458,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1041),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "MidiSport 2x4",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(QUIRK_ANY_INTERFACE) {
.out_cables = 0x000f,
.in_cables = 0x0003
}
@@ -1871,76 +1469,41 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "Quattro",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
/*
* Interfaces 0-2 are "Windows-compatible", 16-bit only,
* and share endpoints with the other interfaces.
* Ignore them. The other interfaces can do 24 bits,
* but captured samples are big-endian (see usbaudio.c).
*/
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 4,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 5,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 6,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 7,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 8,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 9,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
+ { QUIRK_DATA_IGNORE(2) },
+ { QUIRK_DATA_IGNORE(3) },
+ { QUIRK_DATA_STANDARD_AUDIO(4) },
+ { QUIRK_DATA_STANDARD_AUDIO(5) },
+ { QUIRK_DATA_IGNORE(6) },
+ { QUIRK_DATA_STANDARD_AUDIO(7) },
+ { QUIRK_DATA_STANDARD_AUDIO(8) },
+ {
+ QUIRK_DATA_MIDI_MIDIMAN(9) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "AudioPhile",
- .ifnum = 6,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(6) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1948,12 +1511,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2008),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "Ozone",
- .ifnum = 3,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(3) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
@@ -1961,93 +1522,45 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x200d),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "M-Audio",
.product_name = "OmniStudio",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 4,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 5,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 6,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 7,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 8,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 9,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
+ { QUIRK_DATA_IGNORE(2) },
+ { QUIRK_DATA_IGNORE(3) },
+ { QUIRK_DATA_STANDARD_AUDIO(4) },
+ { QUIRK_DATA_STANDARD_AUDIO(5) },
+ { QUIRK_DATA_IGNORE(6) },
+ { QUIRK_DATA_STANDARD_AUDIO(7) },
+ { QUIRK_DATA_STANDARD_AUDIO(8) },
+ {
+ QUIRK_DATA_MIDI_MIDIMAN(9) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x0763, 0x2019),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "M-Audio", */
/* .product_name = "Ozone Academic", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
{
- .ifnum = 3,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(3) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2057,21 +1570,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track C400", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(1) },
/* Playback */
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 6,
.iface = 2,
@@ -2095,9 +1601,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
/* Capture */
{
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(3) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4,
.iface = 3,
@@ -2119,30 +1623,21 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.clock = 0x80,
}
},
- /* MIDI */
- {
- .ifnum = -1 /* Interface = 4 */
- }
+ /* MIDI: Interface = 4*/
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2031),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track C600", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(1) },
/* Playback */
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 2,
@@ -2166,9 +1661,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
/* Capture */
{
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(3) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 6,
.iface = 3,
@@ -2190,29 +1683,20 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.clock = 0x80,
}
},
- /* MIDI */
- {
- .ifnum = -1 /* Interface = 4 */
- }
+ /* MIDI: Interface = 4 */
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 1,
@@ -2234,9 +1718,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 2,
@@ -2258,28 +1740,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
/* interface 3 (MIDI) is standard compliant */
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8R", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 1,
@@ -2301,9 +1774,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 2,
@@ -2325,9 +1796,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
/* interface 3 (MIDI) is standard compliant */
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2335,21 +1804,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Casio devices */
{
USB_DEVICE(0x07cf, 0x6801),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Casio",
.product_name = "PL-40R",
- .ifnum = 0,
- .type = QUIRK_MIDI_YAMAHA
+ QUIRK_DATA_MIDI_YAMAHA(0)
}
},
{
/* this ID is used by several devices without a product ID */
USB_DEVICE(0x07cf, 0x6802),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Casio",
.product_name = "Keyboard",
- .ifnum = 0,
- .type = QUIRK_MIDI_YAMAHA
+ QUIRK_DATA_MIDI_YAMAHA(0)
}
},
@@ -2362,23 +1829,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.idVendor = 0x07fd,
.idProduct = 0x0001,
.bDeviceSubClass = 2,
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "MOTU",
.product_name = "Fastlane",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_MIDI_RAW_BYTES
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_RAW_BYTES(0) },
+ { QUIRK_DATA_IGNORE(1) },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2386,12 +1843,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Emagic devices */
{
USB_DEVICE(0x086a, 0x0001),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Emagic",
.product_name = "Unitor8",
- .ifnum = 2,
- .type = QUIRK_MIDI_EMAGIC,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_EMAGIC(2) {
.out_cables = 0x80ff,
.in_cables = 0x80ff
}
@@ -2399,12 +1854,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE(0x086a, 0x0002),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Emagic",
/* .product_name = "AMT8", */
- .ifnum = 2,
- .type = QUIRK_MIDI_EMAGIC,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_EMAGIC(2) {
.out_cables = 0x80ff,
.in_cables = 0x80ff
}
@@ -2412,12 +1865,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE(0x086a, 0x0003),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Emagic",
/* .product_name = "MT4", */
- .ifnum = 2,
- .type = QUIRK_MIDI_EMAGIC,
- .data = & (const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_EMAGIC(2) {
.out_cables = 0x800f,
.in_cables = 0x8003
}
@@ -2427,38 +1878,35 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* KORG devices */
{
USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "KORG, Inc.",
/* .product_name = "PANDORA PX5D", */
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ QUIRK_DATA_STANDARD_MIDI(3)
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0944, 0x0201),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "KORG, Inc.",
/* .product_name = "ToneLab ST", */
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ QUIRK_DATA_STANDARD_MIDI(3)
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0944, 0x0204),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "KORG, Inc.",
/* .product_name = "ToneLab EX", */
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ QUIRK_DATA_STANDARD_MIDI(3)
}
},
/* AKAI devices */
{
USB_DEVICE(0x09e8, 0x0062),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "AKAI",
.product_name = "MPD16",
.ifnum = 0,
@@ -2469,21 +1917,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* Akai MPC Element */
USB_DEVICE(0x09e8, 0x0021),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_STANDARD_MIDI(1) },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2492,66 +1930,36 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* Steinberg MI2 */
USB_DEVICE_VENDOR_SPEC(0x0a4e, 0x2040),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = &(const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(3) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* Steinberg MI4 */
USB_DEVICE_VENDOR_SPEC(0x0a4e, 0x4040),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = & (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
{
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = &(const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(3) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2559,34 +1967,31 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* TerraTec devices */
{
USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0012),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "TerraTec",
.product_name = "PHASE 26",
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ QUIRK_DATA_STANDARD_MIDI(3)
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "TerraTec",
.product_name = "PHASE 26",
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ QUIRK_DATA_STANDARD_MIDI(3)
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0014),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "TerraTec",
.product_name = "PHASE 26",
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ QUIRK_DATA_STANDARD_MIDI(3)
}
},
{
USB_DEVICE(0x0ccd, 0x0035),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Miditech",
.product_name = "Play'n Roll",
.ifnum = 0,
@@ -2594,10 +1999,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* Stanton ScratchAmp */
+{ USB_DEVICE(0x103d, 0x0100) },
+{ USB_DEVICE(0x103d, 0x0101) },
+
/* Novation EMS devices */
{
USB_DEVICE_VENDOR_SPEC(0x1235, 0x0001),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Novation",
.product_name = "ReMOTE Audio/XStation",
.ifnum = 4,
@@ -2606,7 +2015,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE_VENDOR_SPEC(0x1235, 0x0002),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Novation",
.product_name = "Speedio",
.ifnum = 3,
@@ -2615,38 +2024,29 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
USB_DEVICE(0x1235, 0x000a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Novation", */
/* .product_name = "Nocturn", */
- .ifnum = 0,
- .type = QUIRK_MIDI_RAW_BYTES
+ QUIRK_DATA_RAW_BYTES(0)
}
},
{
USB_DEVICE(0x1235, 0x000e),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
/* .vendor_name = "Novation", */
/* .product_name = "Launchpad", */
- .ifnum = 0,
- .type = QUIRK_MIDI_RAW_BYTES
+ QUIRK_DATA_RAW_BYTES(0)
}
},
{
USB_DEVICE(0x1235, 0x0010),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Focusrite",
.product_name = "Saffire 6 USB",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4,
.iface = 0,
@@ -2673,9 +2073,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 0,
@@ -2697,28 +2095,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = 1,
- .type = QUIRK_MIDI_RAW_BYTES
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_RAW_BYTES(1) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x1235, 0x0018),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Novation",
.product_name = "Twitch",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = & (const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4,
.iface = 0,
@@ -2737,19 +2126,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = 1,
- .type = QUIRK_MIDI_RAW_BYTES
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_RAW_BYTES(1) },
+ QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE_VENDOR_SPEC(0x1235, 0x4661),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Novation",
.product_name = "ReMOTE25",
.ifnum = 0,
@@ -2761,25 +2145,16 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* VirusTI Desktop */
USB_DEVICE_VENDOR_SPEC(0x133e, 0x0815),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 3,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = &(const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(3) {
.out_cables = 0x0003,
.in_cables = 0x0003
}
},
- {
- .ifnum = 4,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_IGNORE(4) },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2807,7 +2182,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* QinHeng devices */
{
USB_DEVICE(0x1a86, 0x752d),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "QinHeng",
.product_name = "CH345",
.ifnum = 1,
@@ -2821,7 +2196,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Miditech devices */
{
USB_DEVICE(0x4752, 0x0011),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Miditech",
.product_name = "Midistart-2",
.ifnum = 0,
@@ -2833,7 +2208,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* this ID used by both Miditech MidiStudio-2 and CME UF-x */
USB_DEVICE(0x7104, 0x2202),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.ifnum = 0,
.type = QUIRK_MIDI_CME
}
@@ -2843,20 +2218,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
/* Thanks to Clemens Ladisch <clemens@ladisch.de> */
USB_DEVICE(0x0dba, 0x1000),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Digidesign",
.product_name = "MBox",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]){
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
+ QUIRK_DATA_COMPOSITE{
+ { QUIRK_DATA_STANDARD_MIXER(0) },
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2,
.iface = 1,
@@ -2877,9 +2245,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2,
.iface = 1,
@@ -2900,9 +2266,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -2910,24 +2274,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* DIGIDESIGN MBOX 2 */
{
USB_DEVICE(0x0dba, 0x3000),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Digidesign",
.product_name = "Mbox 2",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2,
.iface = 2,
@@ -2945,15 +2299,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+ { QUIRK_DATA_IGNORE(3) },
{
- .ifnum = 3,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 4,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
- .formats = SNDRV_PCM_FMTBIT_S24_3BE,
+ QUIRK_DATA_AUDIOFORMAT(4) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2,
.iface = 4,
.altsetting = 2,
@@ -2970,14 +2319,9 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+ { QUIRK_DATA_IGNORE(5) },
{
- .ifnum = 5,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 6,
- .type = QUIRK_MIDI_MIDIMAN,
- .data = &(const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_MIDIMAN(6) {
.out_ep = 0x02,
.out_cables = 0x0001,
.in_ep = 0x81,
@@ -2985,33 +2329,21 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
/* DIGIDESIGN MBOX 3 */
{
USB_DEVICE(0x0dba, 0x5000),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Digidesign",
.product_name = "Mbox 3",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_IGNORE(1) },
{
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.fmt_bits = 24,
.channels = 4,
@@ -3038,9 +2370,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(3) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.fmt_bits = 24,
.channels = 4,
@@ -3064,36 +2394,25 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 4,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = &(const struct snd_usb_midi_endpoint_info) {
+ QUIRK_DATA_MIDI_FIXED_ENDPOINT(4) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
{
/* Tascam US122 MKII - playback-only support */
USB_DEVICE_VENDOR_SPEC(0x0644, 0x8021),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "TASCAM",
.product_name = "US122 MKII",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 1,
@@ -3114,9 +2433,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3124,20 +2441,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
/* Denon DN-X1600 */
{
USB_AUDIO_DEVICE(0x154e, 0x500e),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Denon",
.product_name = "DN-X1600",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]){
+ QUIRK_DATA_COMPOSITE{
+ { QUIRK_DATA_IGNORE(0) },
{
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE,
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 1,
@@ -3158,9 +2468,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 2,
@@ -3180,13 +2488,8 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = 4,
- .type = QUIRK_MIDI_STANDARD_INTERFACE,
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_STANDARD_MIDI(4) },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3195,17 +2498,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
USB_DEVICE(0x045e, 0x0283),
.bInterfaceClass = USB_CLASS_PER_INTERFACE,
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Microsoft",
.product_name = "XboxLive Headset/Xbox Communicator",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
{
/* playback */
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 1,
.iface = 0,
@@ -3221,9 +2520,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
{
/* capture */
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 1,
.iface = 1,
@@ -3237,9 +2534,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_max = 16000
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3248,18 +2543,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
{
USB_DEVICE(0x200c, 0x100b),
.bInterfaceClass = USB_CLASS_PER_INTERFACE,
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4,
.iface = 1,
@@ -3278,9 +2566,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3293,28 +2579,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* enabled in create_standard_audio_quirk().
*/
USB_DEVICE(0x1686, 0x00dd),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- /* Playback */
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE,
- },
- {
- /* Capture */
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE,
- },
- {
- /* Midi */
- .ifnum = 3,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(1) }, /* Playback */
+ { QUIRK_DATA_STANDARD_AUDIO(2) }, /* Capture */
+ { QUIRK_DATA_STANDARD_MIDI(3) }, /* Midi */
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3328,18 +2598,16 @@ YAMAHA_DEVICE(0x7010, "UB99"),
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_MIDI_STANDARD_INTERFACE
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_STANDARD_MIDI(QUIRK_ANY_INTERFACE)
}
},
/* Rane SL-1 */
{
USB_DEVICE(0x13e5, 0x0001),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_STANDARD_AUDIO(QUIRK_ANY_INTERFACE)
}
},
@@ -3355,24 +2623,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* and only the 48 kHz sample rate works for the playback interface.
*/
USB_DEVICE(0x0a12, 0x1243),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- /* Capture */
- {
- .ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE,
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
+ { QUIRK_DATA_IGNORE(1) }, /* Capture */
/* Playback */
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 2,
@@ -3391,9 +2648,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3406,19 +2661,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* even on windows.
*/
USB_DEVICE(0x19b5, 0x0021),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
/* Playback */
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 1,
@@ -3437,29 +2685,20 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- },
+ QUIRK_COMPOSITE_END
}
}
},
/* MOTU Microbook II */
{
USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "MOTU",
.product_name = "MicroBookII",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(0) },
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 6,
.iface = 0,
@@ -3480,9 +2719,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 8,
.iface = 0,
@@ -3503,9 +2740,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3517,14 +2752,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* The feedback for the output is the input.
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0023),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 12,
.iface = 0,
@@ -3541,9 +2772,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 10,
.iface = 0,
@@ -3561,9 +2790,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3606,14 +2833,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* but not for DVS (Digital Vinyl Systems) like in Mixxx.
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0017),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8, // outputs
.iface = 0,
@@ -3630,9 +2853,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8, // inputs
.iface = 0,
@@ -3650,9 +2871,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 48000 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3663,14 +2882,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* The feedback for the output is the dummy input.
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000e),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4,
.iface = 0,
@@ -3687,9 +2902,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 0,
@@ -3707,9 +2920,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3720,14 +2931,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* PCM is 6 channels out & 4 channels in @ 44.1 fixed
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000d),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 6, //Master, Headphones & Booth
.iface = 0,
@@ -3744,9 +2951,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4, //2x RCA inputs (CH1 & CH2)
.iface = 0,
@@ -3764,9 +2969,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3778,14 +2981,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* The Feedback for the output is the input
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x001e),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 4,
.iface = 0,
@@ -3802,9 +3001,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 6,
.iface = 0,
@@ -3822,9 +3019,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3835,14 +3030,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000a),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 10,
.iface = 0,
@@ -3863,9 +3054,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 12,
.iface = 0,
@@ -3887,9 +3076,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3901,14 +3088,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* The Feedback for the output is the input
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0029),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 6,
.iface = 0,
@@ -3925,9 +3108,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 6,
.iface = 0,
@@ -3945,9 +3126,115 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100 }
}
},
+ QUIRK_COMPOSITE_END
+ }
+ }
+},
+
+{
+ /*
+ * Pioneer DJ / AlphaTheta DJM-A9
+ * 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x003c),
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ {
+ QUIRK_DATA_AUDIOFORMAT(0) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100|
+ SNDRV_PCM_RATE_48000|
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 3,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 96000
+ }
+ }
+ },
{
- .ifnum = -1
- }
+ QUIRK_DATA_AUDIOFORMAT(0) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 12,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x82,
+ .ep_idx = 1,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC|
+ USB_ENDPOINT_USAGE_IMPLICIT_FB,
+ .rates = SNDRV_PCM_RATE_44100|
+ SNDRV_PCM_RATE_48000|
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 3,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 96000
+ }
+ }
+ },
+ QUIRK_COMPOSITE_END
+ }
+ }
+},
+{
+ /*
+ * Pioneer DJ DJM-V10
+ */
+ USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0034),
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
+ {
+ QUIRK_DATA_AUDIOFORMAT(0) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 12, // outputs
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_44100|
+ SNDRV_PCM_RATE_48000|
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 3,
+ .rate_table = (unsigned int[]) { 44100, 48000, 96000 }
+ }
+ },
+ {
+ QUIRK_DATA_AUDIOFORMAT(0) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 12, // inputs
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x82,
+ .ep_idx = 1,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC|
+ USB_ENDPOINT_SYNC_ASYNC|
+ USB_ENDPOINT_USAGE_IMPLICIT_FB,
+ .rates = SNDRV_PCM_RATE_44100|
+ SNDRV_PCM_RATE_48000|
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 3,
+ .rate_table = (unsigned int[]) { 44100, 48000, 96000 }
+ }
+ },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -3965,20 +3252,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
*/
{
USB_AUDIO_DEVICE(0x534d, 0x0021),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "MacroSilicon",
.product_name = "MS210x",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(2) },
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- {
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(3) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 3,
@@ -3993,9 +3273,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_max = 48000,
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4013,20 +3291,13 @@ YAMAHA_DEVICE(0x7010, "UB99"),
*/
{
USB_AUDIO_DEVICE(0x534d, 0x2109),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "MacroSilicon",
.product_name = "MS2109",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_MIXER(2) },
{
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(3) {
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 2,
.iface = 3,
@@ -4041,9 +3312,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_max = 48000,
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4053,14 +3322,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* 8 channels playback & 8 channels capture @ 44.1/48/96kHz S24LE
*/
USB_DEVICE_VENDOR_SPEC(0x08e4, 0x017f),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 0,
@@ -4079,9 +3344,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 0,
@@ -4101,9 +3364,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100, 48000, 96000 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4113,14 +3374,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* 10 channels playback & 12 channels capture @ 48kHz S24LE
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x001b),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 10,
.iface = 0,
@@ -4139,9 +3396,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 12,
.iface = 0,
@@ -4159,9 +3414,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 48000 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4173,14 +3426,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* Capture on EP 0x86
*/
USB_DEVICE_VENDOR_SPEC(0x08e4, 0x0163),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 0,
@@ -4200,9 +3449,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8,
.iface = 0,
@@ -4222,9 +3469,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 44100, 48000, 96000 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4235,14 +3480,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* and 8 channels in @ 48 fixed (endpoint 0x82).
*/
USB_DEVICE_VENDOR_SPEC(0x2b73, 0x0013),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8, // outputs
.iface = 0,
@@ -4259,9 +3500,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = 0,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(0) {
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 8, // inputs
.iface = 0,
@@ -4279,9 +3518,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rate_table = (unsigned int[]) { 48000 }
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4292,28 +3529,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
*/
USB_DEVICE(0x1395, 0x0300),
.bInterfaceClass = USB_CLASS_PER_INTERFACE,
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
+ QUIRK_DRIVER_INFO {
+ QUIRK_DATA_COMPOSITE {
// Communication
- {
- .ifnum = 3,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ { QUIRK_DATA_STANDARD_AUDIO(3) },
// Recording
- {
- .ifnum = 4,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ { QUIRK_DATA_STANDARD_AUDIO(4) },
// Main
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4322,21 +3546,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* Fiero SC-01 (firmware v1.0.0 @ 48 kHz)
*/
USB_DEVICE(0x2b53, 0x0023),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Fiero",
.product_name = "SC-01",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
/* Playback */
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 2,
.fmt_bits = 24,
@@ -4356,9 +3573,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
/* Capture */
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 2,
.fmt_bits = 24,
@@ -4377,9 +3592,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.clock = 0x29
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4388,21 +3601,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* Fiero SC-01 (firmware v1.0.0 @ 96 kHz)
*/
USB_DEVICE(0x2b53, 0x0024),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Fiero",
.product_name = "SC-01",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
/* Playback */
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 2,
.fmt_bits = 24,
@@ -4422,9 +3628,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
/* Capture */
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 2,
.fmt_bits = 24,
@@ -4443,9 +3647,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.clock = 0x29
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4454,21 +3656,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* Fiero SC-01 (firmware v1.1.0)
*/
USB_DEVICE(0x2b53, 0x0031),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Fiero",
.product_name = "SC-01",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_STANDARD_AUDIO(0) },
/* Playback */
{
- .ifnum = 1,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(1) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 2,
.fmt_bits = 24,
@@ -4489,9 +3684,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
},
/* Capture */
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
+ QUIRK_DATA_AUDIOFORMAT(2) {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.channels = 2,
.fmt_bits = 24,
@@ -4511,9 +3704,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.clock = 0x29
}
},
- {
- .ifnum = -1
- }
+ QUIRK_COMPOSITE_END
}
}
},
@@ -4522,30 +3713,192 @@ YAMAHA_DEVICE(0x7010, "UB99"),
* For the standard mode, Mythware XA001AU has ID ffad:a001
*/
USB_DEVICE_VENDOR_SPEC(0xffad, 0xa001),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ QUIRK_DRIVER_INFO {
.vendor_name = "Mythware",
.product_name = "XA001AU",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE,
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE,
- },
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE,
- },
- {
- .ifnum = -1
- }
+ QUIRK_DATA_COMPOSITE {
+ { QUIRK_DATA_IGNORE(0) },
+ { QUIRK_DATA_STANDARD_AUDIO(1) },
+ { QUIRK_DATA_STANDARD_AUDIO(2) },
+ QUIRK_COMPOSITE_END
}
}
},
+#define QUIRK_RME_DIGIFACE(pid) \
+{ \
+ /* Only claim interface 0 */ \
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | \
+ USB_DEVICE_ID_MATCH_PRODUCT | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_NUMBER, \
+ .idVendor = 0x2a39, \
+ .idProduct = pid, \
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+ .bInterfaceNumber = 0, \
+ QUIRK_DRIVER_INFO { \
+ QUIRK_DATA_COMPOSITE { \
+ /*
+ * Three modes depending on sample rate band,
+ * with different channel counts for in/out
+ */ \
+ { QUIRK_DATA_STANDARD_MIXER(0) }, \
+ { \
+ QUIRK_DATA_AUDIOFORMAT(0) { \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels = 34, /* outputs */ \
+ .fmt_bits = 24, \
+ .iface = 0, \
+ .altsetting = 1, \
+ .altset_idx = 1, \
+ .endpoint = 0x02, \
+ .ep_idx = 1, \
+ .ep_attr = USB_ENDPOINT_XFER_ISOC | \
+ USB_ENDPOINT_SYNC_ASYNC, \
+ .rates = SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000, \
+ .rate_min = 32000, \
+ .rate_max = 48000, \
+ .nr_rates = 3, \
+ .rate_table = (unsigned int[]) { \
+ 32000, 44100, 48000, \
+ }, \
+ .sync_ep = 0x81, \
+ .sync_iface = 0, \
+ .sync_altsetting = 1, \
+ .sync_ep_idx = 0, \
+ .implicit_fb = 1, \
+ }, \
+ }, \
+ { \
+ QUIRK_DATA_AUDIOFORMAT(0) { \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels = 18, /* outputs */ \
+ .fmt_bits = 24, \
+ .iface = 0, \
+ .altsetting = 1, \
+ .altset_idx = 1, \
+ .endpoint = 0x02, \
+ .ep_idx = 1, \
+ .ep_attr = USB_ENDPOINT_XFER_ISOC | \
+ USB_ENDPOINT_SYNC_ASYNC, \
+ .rates = SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000, \
+ .rate_min = 64000, \
+ .rate_max = 96000, \
+ .nr_rates = 3, \
+ .rate_table = (unsigned int[]) { \
+ 64000, 88200, 96000, \
+ }, \
+ .sync_ep = 0x81, \
+ .sync_iface = 0, \
+ .sync_altsetting = 1, \
+ .sync_ep_idx = 0, \
+ .implicit_fb = 1, \
+ }, \
+ }, \
+ { \
+ QUIRK_DATA_AUDIOFORMAT(0) { \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels = 10, /* outputs */ \
+ .fmt_bits = 24, \
+ .iface = 0, \
+ .altsetting = 1, \
+ .altset_idx = 1, \
+ .endpoint = 0x02, \
+ .ep_idx = 1, \
+ .ep_attr = USB_ENDPOINT_XFER_ISOC | \
+ USB_ENDPOINT_SYNC_ASYNC, \
+ .rates = SNDRV_PCM_RATE_KNOT | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000, \
+ .rate_min = 128000, \
+ .rate_max = 192000, \
+ .nr_rates = 3, \
+ .rate_table = (unsigned int[]) { \
+ 128000, 176400, 192000, \
+ }, \
+ .sync_ep = 0x81, \
+ .sync_iface = 0, \
+ .sync_altsetting = 1, \
+ .sync_ep_idx = 0, \
+ .implicit_fb = 1, \
+ }, \
+ }, \
+ { \
+ QUIRK_DATA_AUDIOFORMAT(0) { \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels = 32, /* inputs */ \
+ .fmt_bits = 24, \
+ .iface = 0, \
+ .altsetting = 1, \
+ .altset_idx = 1, \
+ .endpoint = 0x81, \
+ .ep_attr = USB_ENDPOINT_XFER_ISOC | \
+ USB_ENDPOINT_SYNC_ASYNC, \
+ .rates = SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000, \
+ .rate_min = 32000, \
+ .rate_max = 48000, \
+ .nr_rates = 3, \
+ .rate_table = (unsigned int[]) { \
+ 32000, 44100, 48000, \
+ } \
+ } \
+ }, \
+ { \
+ QUIRK_DATA_AUDIOFORMAT(0) { \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels = 16, /* inputs */ \
+ .fmt_bits = 24, \
+ .iface = 0, \
+ .altsetting = 1, \
+ .altset_idx = 1, \
+ .endpoint = 0x81, \
+ .ep_attr = USB_ENDPOINT_XFER_ISOC | \
+ USB_ENDPOINT_SYNC_ASYNC, \
+ .rates = SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000, \
+ .rate_min = 64000, \
+ .rate_max = 96000, \
+ .nr_rates = 3, \
+ .rate_table = (unsigned int[]) { \
+ 64000, 88200, 96000, \
+ } \
+ } \
+ }, \
+ { \
+ QUIRK_DATA_AUDIOFORMAT(0) { \
+ .formats = SNDRV_PCM_FMTBIT_S32_LE, \
+ .channels = 8, /* inputs */ \
+ .fmt_bits = 24, \
+ .iface = 0, \
+ .altsetting = 1, \
+ .altset_idx = 1, \
+ .endpoint = 0x81, \
+ .ep_attr = USB_ENDPOINT_XFER_ISOC | \
+ USB_ENDPOINT_SYNC_ASYNC, \
+ .rates = SNDRV_PCM_RATE_KNOT | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000, \
+ .rate_min = 128000, \
+ .rate_max = 192000, \
+ .nr_rates = 3, \
+ .rate_table = (unsigned int[]) { \
+ 128000, 176400, 192000, \
+ } \
+ } \
+ }, \
+ QUIRK_COMPOSITE_END \
+ } \
+ } \
+}
+
+QUIRK_RME_DIGIFACE(0x3f8c),
+QUIRK_RME_DIGIFACE(0x3fa0),
#undef USB_DEVICE_VENDOR_SPEC
#undef USB_AUDIO_DEVICE
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 58156fbca02c..61bd61ffb1b2 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -167,8 +167,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
return -EINVAL;
}
if (fp->nr_rates > 0) {
- rate_table = kmemdup(fp->rate_table,
- sizeof(int) * fp->nr_rates, GFP_KERNEL);
+ rate_table = kmemdup_array(fp->rate_table, fp->nr_rates, sizeof(int),
+ GFP_KERNEL);
if (!rate_table) {
kfree(fp);
return -ENOMEM;
@@ -555,6 +555,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
{
struct usb_host_config *config = dev->actconfig;
+ struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL;
int err;
if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
@@ -565,11 +566,19 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac
0x10, 0x43, 0x0001, 0x000a, NULL, 0);
if (err < 0)
dev_dbg(&dev->dev, "error sending boot message: %d\n", err);
+
+ new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
+ if (!new_device_descriptor)
+ return -ENOMEM;
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
- &dev->descriptor, sizeof(dev->descriptor));
- config = dev->actconfig;
+ new_device_descriptor, sizeof(*new_device_descriptor));
if (err < 0)
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
+ if (new_device_descriptor->bNumConfigurations > dev->descriptor.bNumConfigurations)
+ dev_dbg(&dev->dev, "error too large bNumConfigurations: %d\n",
+ new_device_descriptor->bNumConfigurations);
+ else
+ memcpy(&dev->descriptor, new_device_descriptor, sizeof(dev->descriptor));
err = usb_reset_configuration(dev);
if (err < 0)
dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
@@ -901,6 +910,7 @@ static void mbox2_setup_48_24_magic(struct usb_device *dev)
static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
{
struct usb_host_config *config = dev->actconfig;
+ struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL;
int err;
u8 bootresponse[0x12];
int fwsize;
@@ -935,11 +945,19 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
dev_dbg(&dev->dev, "device initialised!\n");
+ new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
+ if (!new_device_descriptor)
+ return -ENOMEM;
+
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
- &dev->descriptor, sizeof(dev->descriptor));
- config = dev->actconfig;
+ new_device_descriptor, sizeof(*new_device_descriptor));
if (err < 0)
dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
+ if (new_device_descriptor->bNumConfigurations > dev->descriptor.bNumConfigurations)
+ dev_dbg(&dev->dev, "error too large bNumConfigurations: %d\n",
+ new_device_descriptor->bNumConfigurations);
+ else
+ memcpy(&dev->descriptor, new_device_descriptor, sizeof(dev->descriptor));
err = usb_reset_configuration(dev);
if (err < 0)
@@ -1249,6 +1267,7 @@ static void mbox3_setup_defaults(struct usb_device *dev)
static int snd_usb_mbox3_boot_quirk(struct usb_device *dev)
{
struct usb_host_config *config = dev->actconfig;
+ struct usb_device_descriptor *new_device_descriptor __free(kfree) = NULL;
int err;
int descriptor_size;
@@ -1261,11 +1280,19 @@ static int snd_usb_mbox3_boot_quirk(struct usb_device *dev)
dev_dbg(&dev->dev, "MBOX3: device initialised!\n");
+ new_device_descriptor = kmalloc(sizeof(*new_device_descriptor), GFP_KERNEL);
+ if (!new_device_descriptor)
+ return -ENOMEM;
+
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
- &dev->descriptor, sizeof(dev->descriptor));
- config = dev->actconfig;
+ new_device_descriptor, sizeof(*new_device_descriptor));
if (err < 0)
dev_dbg(&dev->dev, "MBOX3: error usb_get_descriptor: %d\n", err);
+ if (new_device_descriptor->bNumConfigurations > dev->descriptor.bNumConfigurations)
+ dev_dbg(&dev->dev, "MBOX3: error too large bNumConfigurations: %d\n",
+ new_device_descriptor->bNumConfigurations);
+ else
+ memcpy(&dev->descriptor, new_device_descriptor, sizeof(dev->descriptor));
err = usb_reset_configuration(dev);
if (err < 0)
@@ -1389,6 +1416,27 @@ static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev)
return 0;
}
+static int snd_usb_rme_digiface_boot_quirk(struct usb_device *dev)
+{
+ /* Disable mixer, internal clock, all outputs ADAT, 48kHz, TMS off */
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 16, 0x40, 0x2410, 0x7fff, NULL, 0);
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 18, 0x40, 0x0104, 0xffff, NULL, 0);
+
+ /* Disable loopback for all inputs */
+ for (int ch = 0; ch < 32; ch++)
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 22, 0x40, 0x400, ch, NULL, 0);
+
+ /* Unity gain for all outputs */
+ for (int ch = 0; ch < 34; ch++)
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 21, 0x40, 0x9000, 0x100 + ch, NULL, 0);
+
+ return 0;
+}
+
/*
* Setup quirks
*/
@@ -1552,7 +1600,6 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
if (chip->usb_id == USB_ID(0x194f, 0x010c))
return s1810c_skip_setting_quirk(chip, iface, altno);
-
return 0;
}
@@ -1616,6 +1663,9 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
get_iface_desc(intf->altsetting)->bInterfaceNumber < 3)
return snd_usb_motu_microbookii_boot_quirk(dev);
break;
+ case USB_ID(0x2a39, 0x3f8c): /* RME Digiface USB */
+ case USB_ID(0x2a39, 0x3fa0): /* RME Digiface USB (alternate) */
+ return snd_usb_rme_digiface_boot_quirk(dev);
}
return 0;
@@ -1771,6 +1821,38 @@ static void mbox3_set_format_quirk(struct snd_usb_substream *subs,
dev_warn(&subs->dev->dev, "MBOX3: Couldn't set the sample rate");
}
+static const int rme_digiface_rate_table[] = {
+ 32000, 44100, 48000, 0,
+ 64000, 88200, 96000, 0,
+ 128000, 176400, 192000, 0,
+};
+
+static int rme_digiface_set_format_quirk(struct snd_usb_substream *subs)
+{
+ unsigned int cur_rate = subs->data_endpoint->cur_rate;
+ u16 val;
+ int speed_mode;
+ int id;
+
+ for (id = 0; id < ARRAY_SIZE(rme_digiface_rate_table); id++) {
+ if (rme_digiface_rate_table[id] == cur_rate)
+ break;
+ }
+
+ if (id >= ARRAY_SIZE(rme_digiface_rate_table))
+ return -EINVAL;
+
+ /* 2, 3, 4 for 1x, 2x, 4x */
+ speed_mode = (id >> 2) + 2;
+ val = (id << 3) | (speed_mode << 12);
+
+ /* Set the sample rate */
+ snd_usb_ctl_msg(subs->stream->chip->dev,
+ usb_sndctrlpipe(subs->stream->chip->dev, 0),
+ 16, 0x40, val, 0x7078, NULL, 0);
+ return 0;
+}
+
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
const struct audioformat *fmt)
{
@@ -1785,7 +1867,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */
subs->stream_offset_adj = 2;
break;
+ case USB_ID(0x2b73, 0x000a): /* Pioneer DJM-900NXS2 */
case USB_ID(0x2b73, 0x0013): /* Pioneer DJM-450 */
+ case USB_ID(0x2b73, 0x0034): /* Pioneer DJM-V10 */
pioneer_djm_set_format_quirk(subs, 0x0082);
break;
case USB_ID(0x08e4, 0x017f): /* Pioneer DJM-750 */
@@ -1795,6 +1879,10 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
case USB_ID(0x0dba, 0x5000):
mbox3_set_format_quirk(subs, fmt); /* Digidesign Mbox 3 */
break;
+ case USB_ID(0x2a39, 0x3f8c): /* RME Digiface USB */
+ case USB_ID(0x2a39, 0x3fa0): /* RME Digiface USB (alternate) */
+ rme_digiface_set_format_quirk(subs);
+ break;
}
}
@@ -1934,12 +2022,15 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case USB_ID(0x16d0, 0x09d8): /* NuPrime IDA-8 */
case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
+ case USB_ID(0x16d0, 0x0ab1): /* PureAudio APA DAC */
+ case USB_ID(0x16d0, 0xeca1): /* PureAudio Lotus DAC5, DAC5 SE, DAC5 Pro */
case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */
case USB_ID(0x20a0, 0x4143): /* WaveIO USB Audio 2.0 */
case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */
case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
case USB_ID(0x2622, 0x0041): /* Audiolab M-DAC+ */
+ case USB_ID(0x2622, 0x0061): /* LEAK Stereo 230 */
case USB_ID(0x278b, 0x5100): /* Rotel RC-1590 */
case USB_ID(0x27f7, 0x3002): /* W4S DAC-2v2SE */
case USB_ID(0x29a2, 0x0086): /* Mutec MC3+ USB */
@@ -2056,6 +2147,8 @@ struct usb_audio_quirk_flags_table {
static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
/* Device matches */
+ DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */
@@ -2063,12 +2156,40 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY |
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
+ DEVICE_FLG(0x045e, 0x070f, /* MS LifeChat LX-3000 Headset */
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x046d, 0x0807, /* Logitech Webcam C500 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x0808, /* Logitech Webcam C600 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x0809,
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x0819, /* Logitech Webcam C210 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x081b, /* HD Webcam c310 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x081d, /* HD Webcam c510 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x0825, /* HD Webcam c270 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x0826, /* HD Webcam c525 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
DEVICE_FLG(0x046d, 0x084c, /* Logitech ConferenceCam Connect */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_CTL_MSG_DELAY_1M),
+ DEVICE_FLG(0x046d, 0x08ca, /* Logitech Quickcam Fusion */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
DEVICE_FLG(0x046d, 0x0991, /* Logitech QuickCam Pro */
- QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR |
+ QUIRK_FLAG_MIC_RES_384),
+ DEVICE_FLG(0x046d, 0x09a2, /* QuickCam Communicate Deluxe/S7500 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
DEVICE_FLG(0x046d, 0x09a4, /* Logitech QuickCam E 3500 */
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
+ DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M |
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */
+ QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0499, 0x3108, /* Yamaha YIT-W12TX */
@@ -2083,6 +2204,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_SET_IFACE_FIRST),
DEVICE_FLG(0x0556, 0x0014, /* Phoenix Audio TMX320VC */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x0572, 0x1b08, /* Conexant Systems (Rockwell), Inc. */
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x0572, 0x1b09, /* Conexant Systems (Rockwell), Inc. */
+ 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 */
@@ -2123,16 +2248,36 @@ 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_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_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_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
- DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
+ DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x12d1, 0x3a07, /* Huawei Technologies Co., Ltd. */
+ 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 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */
@@ -2157,18 +2302,30 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_IGNORE_CLOCK_SOURCE),
DEVICE_FLG(0x1686, 0x00dd, /* Zoom R16/24 */
QUIRK_FLAG_TX_LENGTH | QUIRK_FLAG_CTL_MSG_DELAY_1M),
+ DEVICE_FLG(0x16d0, 0x0ab1, /* PureAudio APA DAC */
+ QUIRK_FLAG_DSD_RAW),
+ DEVICE_FLG(0x16d0, 0xeca1, /* PureAudio Lotus DAC5, DAC5 SE and DAC5 Pro */
+ QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x17aa, 0x1046, /* Lenovo ThinkStation P620 Rear Line-in, Line-out and Microphone */
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
DEVICE_FLG(0x17aa, 0x104d, /* Lenovo ThinkStation P620 Internal Speaker + Front Headset */
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
+ DEVICE_FLG(0x17ef, 0x3083, /* Lenovo TBT3 dock */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x1852, 0x5062, /* Luxman D-08u */
+ QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
DEVICE_FLG(0x1852, 0x5065, /* Luxman DA-06 */
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
DEVICE_FLG(0x1901, 0x0191, /* GE B850V3 CP2114 audio interface */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x19f7, 0x0003, /* RODE NT-USB */
+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x1bcf, 0x2281, /* HD Webcam */
+ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
- QUIRK_FLAG_GET_SAMPLE_RATE),
+ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */
@@ -2207,18 +2364,32 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x2522, 0x0007, /* LH Labs Geek Out HD Audio 1V5 */
QUIRK_FLAG_SET_IFACE_FIRST),
+ DEVICE_FLG(0x262a, 0x9302, /* ddHiFi TC44C */
+ QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
+ DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */
+ 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) */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x2d95, 0x8011, /* VIVO USB-C HEADSET */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M),
+ 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_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_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x534d, 0x0021, /* MacroSilicon MS2100/MS2106 */
@@ -2258,6 +2429,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x25ce, /* Mytek devices */
QUIRK_FLAG_DSD_RAW),
+ VENDOR_FLG(0x2622, /* IAG Limited devices */
+ QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x278b, /* Rotel? */
QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x292b, /* Gustard/Ess based devices */
@@ -2270,6 +2443,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x2d87, /* Cayin device */
QUIRK_FLAG_DSD_RAW),
+ VENDOR_FLG(0x2fc6, /* Comture-inc devices */
+ QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x3336, /* HEM devices */
QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x3353, /* Khadas devices */
@@ -2284,7 +2459,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;
@@ -2292,12 +2545,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/stream.c b/sound/usb/stream.c
index d5409f387945..ec7d756d78d1 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -244,8 +244,8 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
SNDRV_CHMAP_FR, /* right front */
SNDRV_CHMAP_FC, /* center front */
SNDRV_CHMAP_LFE, /* LFE */
- SNDRV_CHMAP_SL, /* left surround */
- SNDRV_CHMAP_SR, /* right surround */
+ SNDRV_CHMAP_RL, /* left surround */
+ SNDRV_CHMAP_RR, /* right surround */
SNDRV_CHMAP_FLC, /* left of center */
SNDRV_CHMAP_FRC, /* right of center */
SNDRV_CHMAP_RC, /* surround */
@@ -341,20 +341,28 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
len = le16_to_cpu(cluster->wLength);
c = 0;
- p += sizeof(struct uac3_cluster_header_descriptor);
+ p += sizeof(*cluster);
+ len -= sizeof(*cluster);
- while (((p - (void *)cluster) < len) && (c < channels)) {
+ while (len > 0 && (c < channels)) {
struct uac3_cluster_segment_descriptor *cs_desc = p;
u16 cs_len;
u8 cs_type;
+ if (len < sizeof(*cs_desc))
+ break;
cs_len = le16_to_cpu(cs_desc->wLength);
+ if (len < cs_len)
+ break;
cs_type = cs_desc->bSegmentType;
if (cs_type == UAC3_CHANNEL_INFORMATION) {
struct uac3_cluster_information_segment_descriptor *is = p;
unsigned char map;
+ if (cs_len < sizeof(*is))
+ break;
+
/*
* TODO: this conversion is not complete, update it
* after adding UAC3 values to asound.h
@@ -456,6 +464,7 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
chmap->map[c++] = map;
}
p += cs_len;
+ len -= cs_len;
}
if (channels < c)
@@ -536,9 +545,10 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
pcm->private_free = snd_usb_audio_pcm_free;
pcm->info_flags = 0;
if (chip->pcm_devs > 0)
- sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+ scnprintf(pcm->name, sizeof(pcm->name), "USB Audio #%d",
+ chip->pcm_devs);
else
- strcpy(pcm->name, "USB Audio");
+ strscpy(pcm->name, "USB Audio");
snd_usb_init_substream(as, stream, fp, pd);
@@ -680,6 +690,7 @@ audio_format_alloc_init(struct snd_usb_audio *chip,
int protocol, int iface_no, int altset_idx,
int altno, int num_channels, int clock)
{
+ struct usb_host_endpoint *ep = &alts->endpoint[0];
struct audioformat *fp;
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
@@ -693,11 +704,8 @@ audio_format_alloc_init(struct snd_usb_audio *chip,
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
fp->protocol = protocol;
- fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ fp->maxpacksize = usb_endpoint_max_periodic_payload(chip->dev, ep);
fp->channels = num_channels;
- if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH)
- fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
- * (fp->maxpacksize & 0x7ff);
fp->clock = clock;
INIT_LIST_HEAD(&fp->list);
@@ -713,10 +721,13 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
struct usb_device *dev = chip->dev;
struct uac_format_type_i_continuous_descriptor *fmt;
unsigned int num_channels = 0, chconfig = 0;
+ struct usb_host_interface *ctrl_intf;
struct audioformat *fp;
int clock = 0;
u64 format;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, iface_no);
+
/* get audio formats */
if (protocol == UAC_VERSION_1) {
struct uac1_as_header_descriptor *as =
@@ -740,7 +751,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
format = le16_to_cpu(as->wFormatTag); /* remember the format value */
- iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ iterm = snd_usb_find_input_terminal_descriptor(ctrl_intf,
as->bTerminalLink,
protocol);
if (iterm) {
@@ -776,7 +787,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
* lookup the terminal associated to this interface
* to extract the clock
*/
- input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ input_term = snd_usb_find_input_terminal_descriptor(ctrl_intf,
as->bTerminalLink,
protocol);
if (input_term) {
@@ -786,7 +797,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
goto found_clock;
}
- output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+ output_term = snd_usb_find_output_terminal_descriptor(ctrl_intf,
as->bTerminalLink,
protocol);
if (output_term) {
@@ -870,17 +881,19 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
struct uac3_cluster_header_descriptor *cluster;
struct uac3_as_header_descriptor *as = NULL;
struct uac3_hc_descriptor_header hc_header;
+ struct usb_host_interface *ctrl_intf;
struct snd_pcm_chmap_elem *chmap;
struct snd_usb_power_domain *pd;
unsigned char badd_profile;
u64 badd_formats = 0;
unsigned int num_channels;
struct audioformat *fp;
- u16 cluster_id, wLength;
+ u16 cluster_id, wLength, cluster_wLength;
int clock = 0;
int err;
badd_profile = chip->badd_profile;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, iface_no);
if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) {
unsigned int maxpacksize =
@@ -966,7 +979,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
cluster_id,
- snd_usb_ctrl_intf(chip),
+ snd_usb_ctrl_intf(ctrl_intf),
&hc_header, sizeof(hc_header));
if (err < 0)
return ERR_PTR(err);
@@ -982,6 +995,8 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
* and request Cluster Descriptor
*/
wLength = le16_to_cpu(hc_header.wLength);
+ if (wLength < sizeof(cluster))
+ return NULL;
cluster = kzalloc(wLength, GFP_KERNEL);
if (!cluster)
return ERR_PTR(-ENOMEM);
@@ -990,7 +1005,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
cluster_id,
- snd_usb_ctrl_intf(chip),
+ snd_usb_ctrl_intf(ctrl_intf),
cluster, wLength);
if (err < 0) {
kfree(cluster);
@@ -1003,6 +1018,16 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
return ERR_PTR(-EIO);
}
+ cluster_wLength = le16_to_cpu(cluster->wLength);
+ if (cluster_wLength < sizeof(*cluster) ||
+ cluster_wLength > wLength) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid Cluster Descriptor size\n",
+ iface_no, altno);
+ kfree(cluster);
+ return ERR_PTR(-EIO);
+ }
+
num_channels = cluster->bNrChannels;
chmap = convert_chmap_v3(cluster);
kfree(cluster);
@@ -1011,7 +1036,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
* lookup the terminal associated to this interface
* to extract the clock
*/
- input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ input_term = snd_usb_find_input_terminal_descriptor(ctrl_intf,
as->bTerminalLink,
UAC_VERSION_3);
if (input_term) {
@@ -1019,7 +1044,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
goto found_clock;
}
- output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+ output_term = snd_usb_find_output_terminal_descriptor(ctrl_intf,
as->bTerminalLink,
UAC_VERSION_3);
if (output_term) {
@@ -1062,13 +1087,14 @@ found_clock:
UAC3_BADD_PD_ID10 : UAC3_BADD_PD_ID11;
pd->pd_d1d0_rec = UAC3_BADD_PD_RECOVER_D1D0;
pd->pd_d2d0_rec = UAC3_BADD_PD_RECOVER_D2D0;
+ pd->ctrl_iface = ctrl_intf;
} else {
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
UAC_VERSION_3,
iface_no);
- pd = snd_usb_find_power_domain(chip->ctrl_intf,
+ pd = snd_usb_find_power_domain(ctrl_intf,
as->bTerminalLink);
/* ok, let's parse further... */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 43d4029edab4..79978cae9799 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -21,6 +21,15 @@ struct media_intf_devnode;
#define MAX_CARD_INTERFACES 16
+/*
+ * Structure holding assosiation between Audio Control Interface
+ * and given Streaming or Midi Interface.
+ */
+struct snd_intf_to_ctrl {
+ u8 interface;
+ struct usb_host_interface *ctrl_intf;
+};
+
struct snd_usb_audio {
int index;
struct usb_device *dev;
@@ -63,6 +72,9 @@ struct snd_usb_audio {
struct usb_host_interface *ctrl_intf; /* the audio control interface */
struct media_device *media_dev;
struct media_intf_devnode *ctl_intf_media_devnode;
+
+ unsigned int num_intf_to_ctrl;
+ struct snd_intf_to_ctrl intf_to_ctrl[MAX_CARD_INTERFACES];
};
#define USB_AUDIO_IFACE_UNUSED ((void *)-1L)
@@ -127,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;
@@ -182,29 +217,72 @@ extern bool snd_usb_skip_validation;
* QUIRK_FLAG_FIXED_RATE
* Do not set PCM rate (frequency) when only one rate is available
* 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_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)
+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 709ccad972e2..011ea96e9779 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -84,20 +84,9 @@ static int us144_create_usbmidi(struct snd_card *card)
static void pt_info_set(struct usb_device *dev, u8 v)
{
- int ret;
-
- ret = usb_control_msg_send(dev, 0, 'I',
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- v, 0, NULL, 0, 1000, GFP_NOIO);
- snd_printdd(KERN_DEBUG "%i\n", ret);
-}
-
-static void usb_stream_hwdep_vm_open(struct vm_area_struct *area)
-{
- struct us122l *us122l = area->vm_private_data;
-
- atomic_inc(&us122l->mmap_count);
- snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
+ usb_control_msg_send(dev, 0, 'I',
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ v, 0, NULL, 0, 1000, GFP_NOIO);
}
static vm_fault_t usb_stream_hwdep_vm_fault(struct vm_fault *vmf)
@@ -108,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)) {
@@ -119,35 +108,22 @@ 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;
}
-static void usb_stream_hwdep_vm_close(struct vm_area_struct *area)
-{
- struct us122l *us122l = area->vm_private_data;
-
- atomic_dec(&us122l->mmap_count);
- snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
-}
static const struct vm_operations_struct usb_stream_hwdep_vm_ops = {
- .open = usb_stream_hwdep_vm_open,
.fault = usb_stream_hwdep_vm_fault,
- .close = usb_stream_hwdep_vm_close,
};
static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
@@ -155,7 +131,6 @@ static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
struct us122l *us122l = hw->private_data;
struct usb_interface *iface;
- snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
if (hw->used >= 2)
return -EBUSY;
@@ -176,8 +151,6 @@ static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file)
struct us122l *us122l = hw->private_data;
struct usb_interface *iface;
- snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
-
if (us122l->is_us144) {
iface = usb_ifnum_to_if(us122l->dev, 0);
usb_autopm_put_interface(iface);
@@ -186,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;
}
@@ -202,25 +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;
- }
- snd_printdd(KERN_DEBUG "%lu %u\n", size,
- read ? s->read_size : s->write_size);
+ 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)) {
- snd_printk(KERN_WARNING "%lu > %u\n", size,
- read ? s->read_size : s->write_size);
- err = -EINVAL;
- goto out;
+ dev_warn(hw->card->dev, "%s: size %lu > %u\n", __func__,
+ size, read ? s->read_size : s->write_size);
+ return -EINVAL;
}
area->vm_ops = &usb_stream_hwdep_vm_ops;
@@ -228,10 +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;
- atomic_inc(&us122l->mmap_count);
-out:
- mutex_unlock(&us122l->mutex);
- return err;
+ return 0;
}
static __poll_t usb_stream_hwdep_poll(struct snd_hwdep *hw,
@@ -289,8 +252,8 @@ static int us122l_set_sample_rate(struct usb_device *dev, int rate)
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3,
1000, GFP_NOIO);
if (err)
- snd_printk(KERN_ERR "%d: cannot set freq %d to ep 0x%x\n",
- dev->devnum, rate, ep);
+ dev_err(&dev->dev, "%d: cannot set freq %d to ep 0x%x\n",
+ dev->devnum, rate, ep);
return err;
}
@@ -326,13 +289,13 @@ static bool us122l_start(struct us122l *us122l,
err = us122l_set_sample_rate(us122l->dev, rate);
if (err < 0) {
us122l_stop(us122l);
- snd_printk(KERN_ERR "us122l_set_sample_rate error\n");
+ dev_err(&us122l->dev->dev, "us122l_set_sample_rate error\n");
goto out;
}
err = usb_stream_start(&us122l->sk);
if (err < 0) {
us122l_stop(us122l);
- snd_printk(KERN_ERR "%s error %i\n", __func__, err);
+ dev_err(&us122l->dev->dev, "%s error %i\n", __func__, err);
goto out;
}
list_for_each(p, &us122l->midi_list)
@@ -387,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;
@@ -407,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;
}
@@ -445,13 +407,13 @@ static bool us122l_create_card(struct snd_card *card)
if (us122l->is_us144) {
err = usb_set_interface(us122l->dev, 0, 1);
if (err) {
- snd_printk(KERN_ERR "usb_set_interface error\n");
+ dev_err(card->dev, "usb_set_interface error\n");
return false;
}
}
err = usb_set_interface(us122l->dev, 1, 1);
if (err) {
- snd_printk(KERN_ERR "usb_set_interface error\n");
+ dev_err(card->dev, "usb_set_interface error\n");
return false;
}
@@ -466,7 +428,7 @@ static bool us122l_create_card(struct snd_card *card)
else
err = us122l_create_usbmidi(card);
if (err < 0) {
- snd_printk(KERN_ERR "us122l_create_usbmidi error %i\n", err);
+ dev_err(card->dev, "us122l_create_usbmidi error %i\n", err);
goto stop;
}
err = usb_stream_hwdep_new(card);
@@ -517,10 +479,11 @@ static int usx2y_create_card(struct usb_device *device,
card->private_free = snd_us122l_free;
US122L(card)->dev = device;
mutex_init(&US122L(card)->mutex);
+ US122L(card)->sk.dev = device;
init_waitqueue_head(&US122L(card)->sk.sleep);
US122L(card)->is_us144 = flags & US122L_FLAG_US144;
INIT_LIST_HEAD(&US122L(card)->midi_list);
- strcpy(card->driver, "USB "NAME_ALLCAPS"");
+ strscpy(card->driver, "USB "NAME_ALLCAPS"");
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
card->shortname,
@@ -572,12 +535,10 @@ static int snd_us122l_probe(struct usb_interface *intf,
if (id->driver_info & US122L_FLAG_US144 &&
device->speed == USB_SPEED_HIGH) {
- snd_printk(KERN_ERR "disable ehci-hcd to run US-144\n");
+ dev_err(&device->dev, "disable ehci-hcd to run US-144\n");
return -ENODEV;
}
- snd_printdd(KERN_DEBUG"%p:%i\n",
- intf, intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
return 0;
@@ -604,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) {
@@ -617,10 +578,7 @@ static void snd_us122l_disconnect(struct usb_interface *intf)
usb_put_intf(usb_ifnum_to_if(us122l->dev, 1));
usb_put_dev(us122l->dev);
- while (atomic_read(&us122l->mmap_count))
- msleep(500);
-
- snd_card_free(card);
+ snd_card_free_when_closed(card);
}
static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message)
@@ -641,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;
}
@@ -663,18 +620,18 @@ 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);
if (err) {
- snd_printk(KERN_ERR "usb_set_interface error\n");
+ dev_err(&us122l->dev->dev, "usb_set_interface error\n");
goto unlock;
}
}
err = usb_set_interface(us122l->dev, 1, 1);
if (err) {
- snd_printk(KERN_ERR "usb_set_interface error\n");
+ dev_err(&us122l->dev->dev, "usb_set_interface error\n");
goto unlock;
}
@@ -684,7 +641,7 @@ static int snd_us122l_resume(struct usb_interface *intf)
err = us122l_set_sample_rate(us122l->dev,
us122l->sk.s->cfg.sample_rate);
if (err < 0) {
- snd_printk(KERN_ERR "us122l_set_sample_rate error\n");
+ dev_err(&us122l->dev->dev, "us122l_set_sample_rate error\n");
goto unlock;
}
err = usb_stream_start(&us122l->sk);
@@ -694,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;
}
@@ -716,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/us122l.h b/sound/usb/usx2y/us122l.h
index c32ae5e981e9..8e59b78d3514 100644
--- a/sound/usb/usx2y/us122l.h
+++ b/sound/usb/usx2y/us122l.h
@@ -16,8 +16,6 @@ struct us122l {
struct file *slave;
struct list_head midi_list;
- atomic_t mmap_count;
-
bool is_us144;
};
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/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 4937ede0b5d7..4d7925184826 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -24,19 +24,12 @@ static vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf)
struct page *page;
void *vaddr;
- snd_printdd("ENTER, start %lXh, pgoff %ld\n",
- vmf->vma->vm_start,
- vmf->pgoff);
-
offset = vmf->pgoff << PAGE_SHIFT;
vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset;
page = virt_to_page(vaddr);
get_page(page);
vmf->page = page;
- snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n",
- vaddr, page);
-
return 0;
}
@@ -56,7 +49,8 @@ static int snd_us428ctls_mmap(struct snd_hwdep *hw, struct file *filp, struct vm
/* if userspace tries to mmap beyond end of our buffer, fail */
if (size > US428_SHAREDMEM_PAGES) {
- snd_printd("%lu > %lu\n", size, (unsigned long)US428_SHAREDMEM_PAGES);
+ dev_dbg(hw->card->dev, "%s: mmap size %lu > %lu\n", __func__,
+ size, (unsigned long)US428_SHAREDMEM_PAGES);
return -EINVAL;
}
@@ -108,7 +102,7 @@ static int snd_usx2y_hwdep_dsp_status(struct snd_hwdep *hw,
}
if (id < 0)
return -ENODEV;
- strcpy(info->id, type_ids[id]);
+ strscpy(info->id, type_ids[id]);
info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code
if (us428->chip_status & USX2Y_STAT_CHIP_INIT)
info->chip_ready = 1;
@@ -150,7 +144,6 @@ static int usx2y_create_usbmidi(struct snd_card *card)
le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ?
&quirk_2 : &quirk_1;
- snd_printdd("%s\n", __func__);
return snd_usbmidi_create(card, iface, &usx2y(card)->midi_list, quirk);
}
@@ -160,7 +153,8 @@ static int usx2y_create_alsa_devices(struct snd_card *card)
err = usx2y_create_usbmidi(card);
if (err < 0) {
- snd_printk(KERN_ERR "%s: usx2y_create_usbmidi error %i\n", __func__, err);
+ dev_err(card->dev, "%s: usx2y_create_usbmidi error %i\n",
+ __func__, err);
return err;
}
err = usx2y_audio_create(card);
@@ -183,15 +177,13 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
int lret, err;
char *buf;
- snd_printdd("dsp_load %s\n", dsp->name);
-
buf = memdup_user(dsp->image, dsp->length);
if (IS_ERR(buf))
return PTR_ERR(buf);
err = usb_set_interface(dev, 0, 1);
if (err)
- snd_printk(KERN_ERR "usb_set_interface error\n");
+ dev_err(&dev->dev, "usb_set_interface error\n");
else
err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6000);
kfree(buf);
@@ -201,21 +193,20 @@ static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw,
msleep(250); // give the device some time
err = usx2y_async_seq04_init(priv);
if (err) {
- snd_printk(KERN_ERR "usx2y_async_seq04_init error\n");
+ dev_err(&dev->dev, "usx2y_async_seq04_init error\n");
return err;
}
err = usx2y_in04_init(priv);
if (err) {
- snd_printk(KERN_ERR "usx2y_in04_init error\n");
+ dev_err(&dev->dev, "usx2y_in04_init error\n");
return err;
}
err = usx2y_create_alsa_devices(hw->card);
if (err) {
- snd_printk(KERN_ERR "usx2y_create_alsa_devices error %i\n", err);
+ dev_err(&dev->dev, "usx2y_create_alsa_devices error %i\n", err);
return err;
}
priv->chip_status |= USX2Y_STAT_CHIP_INIT;
- snd_printdd("%s: alsa all started\n", hw->name);
}
return err;
}
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index a4d32e8a1d36..3122cf653273 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -34,14 +34,11 @@ static void playback_prep_freqn(struct usb_stream_kernel *sk, struct urb *urb)
urb->iso_frame_desc[pack].length = l;
lb += l;
}
- snd_printdd(KERN_DEBUG "%i\n", lb);
check:
urb->number_of_packets = pack;
urb->transfer_buffer_length = lb;
s->idle_outsize += lb - s->period_size;
- snd_printdd(KERN_DEBUG "idle=%i ul=%i ps=%i\n", s->idle_outsize,
- lb, s->period_size);
}
static int init_pipe_urbs(struct usb_stream_kernel *sk,
@@ -191,14 +188,14 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
write_size = max_packsize * packets * USB_STREAM_URBDEPTH;
if (read_size >= 256*PAGE_SIZE || write_size >= 256*PAGE_SIZE) {
- snd_printk(KERN_WARNING "a size exceeds 128*PAGE_SIZE\n");
+ dev_warn(&dev->dev, "%s: a size exceeds 128*PAGE_SIZE\n", __func__);
goto out;
}
sk->s = alloc_pages_exact(read_size,
GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
if (!sk->s) {
- pr_warn("us122l: couldn't allocate read buffer\n");
+ dev_warn(&dev->dev, "us122l: couldn't allocate read buffer\n");
goto out;
}
sk->s->cfg.version = USB_STREAM_INTERFACE_VERSION;
@@ -217,7 +214,7 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
sk->write_page = alloc_pages_exact(write_size,
GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
if (!sk->write_page) {
- pr_warn("us122l: couldn't allocate write buffer\n");
+ dev_warn(&dev->dev, "us122l: couldn't allocate write buffer\n");
usb_stream_free(sk);
return NULL;
}
@@ -246,7 +243,8 @@ static bool balance_check(struct usb_stream_kernel *sk, struct urb *urb)
if (unlikely(urb->status)) {
if (urb->status != -ESHUTDOWN && urb->status != -ENOENT)
- snd_printk(KERN_WARNING "status=%i\n", urb->status);
+ dev_warn(&sk->dev->dev, "%s: status=%i\n", __func__,
+ urb->status);
sk->iso_frame_balance = 0x7FFFFFFF;
return false;
}
@@ -318,17 +316,18 @@ static int usb_stream_prepare_playback(struct usb_stream_kernel *sk,
check_ok:
s->sync_packet -= inurb->number_of_packets;
if (unlikely(s->sync_packet < -2 || s->sync_packet > 0)) {
- snd_printk(KERN_WARNING "invalid sync_packet = %i;"
- " p=%i nop=%i %i %x %x %x > %x\n",
- s->sync_packet, p, inurb->number_of_packets,
- s->idle_outsize + lb + l,
- s->idle_outsize, lb, l,
- s->period_size);
+ dev_warn(&sk->dev->dev,
+ "%s: invalid sync_packet = %i; p=%i nop=%i %i %x %x %x > %x\n",
+ __func__,
+ s->sync_packet, p, inurb->number_of_packets,
+ s->idle_outsize + lb + l,
+ s->idle_outsize, lb, l,
+ s->period_size);
return -1;
}
if (unlikely(lb % s->cfg.frame_size)) {
- snd_printk(KERN_WARNING"invalid outsize = %i\n",
- lb);
+ dev_warn(&sk->dev->dev, "%s: invalid outsize = %i\n",
+ __func__, lb);
return -1;
}
s->idle_outsize += lb - s->period_size;
@@ -337,7 +336,7 @@ check_ok:
if (s->idle_outsize <= 0)
return 0;
- snd_printk(KERN_WARNING "idle=%i\n", s->idle_outsize);
+ dev_warn(&sk->dev->dev, "%s: idle=%i\n", __func__, s->idle_outsize);
return -1;
}
@@ -377,7 +376,7 @@ static int submit_urbs(struct usb_stream_kernel *sk,
return 0;
report_failure:
- snd_printk(KERN_ERR "%i\n", err);
+ dev_err(&sk->dev->dev, "%s error: %i\n", __func__, err);
return err;
}
@@ -424,8 +423,8 @@ loop:
}
if (iu == sk->completed_inurb) {
if (l != s->period_size)
- printk(KERN_DEBUG"%s:%i %i\n", __func__, __LINE__,
- l/(int)s->cfg.frame_size);
+ dev_dbg(&sk->dev->dev, "%s:%i %i\n", __func__, __LINE__,
+ l/(int)s->cfg.frame_size);
return;
}
@@ -460,8 +459,8 @@ static void stream_idle(struct usb_stream_kernel *sk,
l = id[p].actual_length;
if (unlikely(l == 0 || id[p].status)) {
- snd_printk(KERN_WARNING "underrun, status=%u\n",
- id[p].status);
+ dev_warn(&sk->dev->dev, "%s: underrun, status=%u\n",
+ __func__, id[p].status);
goto err_out;
}
s->inpacket_head++;
@@ -482,8 +481,8 @@ static void stream_idle(struct usb_stream_kernel *sk,
}
s->idle_insize += urb_size - s->period_size;
if (s->idle_insize < 0) {
- snd_printk(KERN_WARNING "%i\n",
- (s->idle_insize)/(int)s->cfg.frame_size);
+ dev_warn(&sk->dev->dev, "%s error: %i\n", __func__,
+ (s->idle_insize)/(int)s->cfg.frame_size);
goto err_out;
}
s->insize_done += urb_size;
@@ -558,19 +557,15 @@ static void stream_start(struct usb_stream_kernel *sk,
min_frames += frames_per_packet;
diff = urb_size -
(min_frames >> 8) * s->cfg.frame_size;
- if (diff < max_diff) {
- snd_printdd(KERN_DEBUG "%i %i %i %i\n",
- s->insize_done,
- urb_size / (int)s->cfg.frame_size,
- inurb->number_of_packets, diff);
+ if (diff < max_diff)
max_diff = diff;
- }
}
s->idle_insize -= max_diff - max_diff_0;
s->idle_insize += urb_size - s->period_size;
if (s->idle_insize < 0) {
- snd_printk(KERN_WARNING "%i %i %i\n",
- s->idle_insize, urb_size, s->period_size);
+ dev_warn(&sk->dev->dev, "%s idle_insize: %i %i %i\n",
+ __func__,
+ s->idle_insize, urb_size, s->period_size);
return;
} else if (s->idle_insize == 0) {
s->next_inpacket_split =
@@ -620,7 +615,7 @@ static void i_capture_start(struct urb *urb)
int empty = 0;
if (urb->status) {
- snd_printk(KERN_WARNING "status=%i\n", urb->status);
+ dev_warn(&sk->dev->dev, "%s: status=%i\n", __func__, urb->status);
return;
}
@@ -630,7 +625,7 @@ static void i_capture_start(struct urb *urb)
if (l < s->cfg.frame_size) {
++empty;
if (s->state >= usb_stream_sync0) {
- snd_printk(KERN_WARNING "%i\n", l);
+ dev_warn(&sk->dev->dev, "%s: length %i\n", __func__, l);
return;
}
}
@@ -642,8 +637,8 @@ static void i_capture_start(struct urb *urb)
}
#ifdef SHOW_EMPTY
if (empty) {
- printk(KERN_DEBUG"%s:%i: %i", __func__, __LINE__,
- urb->iso_frame_desc[0].actual_length);
+ dev_dbg(&sk->dev->dev, "%s:%i: %i", __func__, __LINE__,
+ urb->iso_frame_desc[0].actual_length);
for (pack = 1; pack < urb->number_of_packets; ++pack) {
int l = urb->iso_frame_desc[pack].actual_length;
@@ -670,7 +665,7 @@ static void i_playback_start(struct urb *urb)
int usb_stream_start(struct usb_stream_kernel *sk)
{
struct usb_stream *s = sk->s;
- int frame = 0, iters = 0;
+ int frame = 0;
int u, err;
int try = 0;
@@ -705,43 +700,42 @@ dotry:
frame = usb_get_current_frame_number(dev);
do {
now = usb_get_current_frame_number(dev);
- ++iters;
} while (now > -1 && now == frame);
}
err = usb_submit_urb(inurb, GFP_ATOMIC);
if (err < 0) {
- snd_printk(KERN_ERR
- "usb_submit_urb(sk->inurb[%i]) returned %i\n",
- u, err);
+ dev_err(&sk->dev->dev,
+ "%s: usb_submit_urb(sk->inurb[%i]) returned %i\n",
+ __func__, u, err);
return err;
}
err = usb_submit_urb(outurb, GFP_ATOMIC);
if (err < 0) {
- snd_printk(KERN_ERR
- "usb_submit_urb(sk->outurb[%i]) returned %i\n",
- u, err);
+ dev_err(&sk->dev->dev,
+ "%s: usb_submit_urb(sk->outurb[%i]) returned %i\n",
+ __func__, u, err);
return err;
}
if (inurb->start_frame != outurb->start_frame) {
- snd_printd(KERN_DEBUG
- "u[%i] start_frames differ in:%u out:%u\n",
- u, inurb->start_frame, outurb->start_frame);
+ dev_dbg(&sk->dev->dev,
+ "%s: u[%i] start_frames differ in:%u out:%u\n",
+ __func__, u, inurb->start_frame, outurb->start_frame);
goto check_retry;
}
}
- snd_printdd(KERN_DEBUG "%i %i\n", frame, iters);
try = 0;
check_retry:
if (try) {
usb_stream_stop(sk);
if (try < 5) {
msleep(1500);
- snd_printd(KERN_DEBUG "goto dotry;\n");
+ dev_dbg(&sk->dev->dev, "goto dotry;\n");
goto dotry;
}
- snd_printk(KERN_WARNING
- "couldn't start all urbs on the same start_frame.\n");
+ dev_warn(&sk->dev->dev,
+ "%s: couldn't start all urbs on the same start_frame.\n",
+ __func__);
return -EFAULT;
}
@@ -755,7 +749,6 @@ check_retry:
int wait_ms = 3000;
while (s->state != usb_stream_ready && wait_ms > 0) {
- snd_printdd(KERN_DEBUG "%i\n", s->state);
msleep(200);
wait_ms -= 200;
}
diff --git a/sound/usb/usx2y/usb_stream.h b/sound/usb/usx2y/usb_stream.h
index 73e57b341adc..2ed10add49f1 100644
--- a/sound/usb/usx2y/usb_stream.h
+++ b/sound/usb/usx2y/usb_stream.h
@@ -9,6 +9,7 @@
struct usb_stream_kernel {
struct usb_stream *s;
+ struct usb_device *dev;
void *write_page;
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 52f4e6652407..f34e78910200 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -151,6 +151,12 @@ static int snd_usx2y_card_used[SNDRV_CARDS];
static void snd_usx2y_card_private_free(struct snd_card *card);
static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s);
+#ifdef USX2Y_NRPACKS_VARIABLE
+int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
+module_param(nrpacks, int, 0444);
+MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
+#endif
+
/*
* pipe 4 is used for switching the lamps, setting samplerate, volumes ....
*/
@@ -163,7 +169,7 @@ static void i_usx2y_out04_int(struct urb *urb)
for (i = 0; i < 10 && usx2y->as04.urb[i] != urb; i++)
;
- snd_printdd("%s urb %i status=%i\n", __func__, i, urb->status);
+ dev_dbg(&urb->dev->dev, "%s urb %i status=%i\n", __func__, i, urb->status);
}
#endif
}
@@ -179,11 +185,10 @@ static void i_usx2y_in04_int(struct urb *urb)
usx2y->in04_int_calls++;
if (urb->status) {
- snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status);
+ dev_dbg(&urb->dev->dev, "Interrupt Pipe 4 came back with status=%i\n", urb->status);
return;
}
- // printk("%i:0x%02X ", 8, (int)((unsigned char*)usx2y->in04_buf)[8]); Master volume shows 0 here if fader is at max during boot ?!?
if (us428ctls) {
diff = -1;
if (us428ctls->ctl_snapshot_last == -2) {
@@ -239,7 +244,7 @@ static void i_usx2y_in04_int(struct urb *urb)
}
if (err)
- snd_printk(KERN_ERR "in04_int() usb_submit_urb err=%i\n", err);
+ dev_err(&urb->dev->dev, "in04_int() usb_submit_urb err=%i\n", err);
urb->dev = usx2y->dev;
usb_submit_urb(urb, GFP_ATOMIC);
@@ -377,7 +382,7 @@ static int usx2y_create_card(struct usb_device *device,
init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head);
mutex_init(&usx2y(card)->pcm_mutex);
INIT_LIST_HEAD(&usx2y(card)->midi_list);
- strcpy(card->driver, "USB "NAME_ALLCAPS"");
+ strscpy(card->driver, "USB "NAME_ALLCAPS"");
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
card->shortname,
@@ -423,7 +428,7 @@ static void snd_usx2y_disconnect(struct usb_interface *intf)
}
if (usx2y->us428ctls_sharedmem)
wake_up(&usx2y->us428ctls_wait_queue_head);
- snd_card_free(card);
+ snd_card_free_when_closed(card);
}
static int snd_usx2y_probe(struct usb_interface *intf,
@@ -433,6 +438,11 @@ static int snd_usx2y_probe(struct usb_interface *intf,
struct snd_card *card;
int err;
+#ifdef USX2Y_NRPACKS_VARIABLE
+ if (nrpacks < 0 || nrpacks > USX2Y_NRPACKS_MAX)
+ return -EINVAL;
+#endif
+
if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||
(le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
index 391fd7b4ed5e..6a76d04bf1c7 100644
--- a/sound/usb/usx2y/usbusx2y.h
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -7,6 +7,32 @@
#define NRURBS 2
+/* Default value used for nr of packs per urb.
+ * 1 to 4 have been tested ok on uhci.
+ * To use 3 on ohci, you'd need a patch:
+ * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on
+ * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"
+ *
+ * 1, 2 and 4 work out of the box on ohci, if I recall correctly.
+ * Bigger is safer operation, smaller gives lower latencies.
+ */
+#define USX2Y_NRPACKS 4
+
+#define USX2Y_NRPACKS_MAX 1024
+
+/* If your system works ok with this module's parameter
+ * nrpacks set to 1, you might as well comment
+ * this define out, and thereby produce smaller, faster code.
+ * You'd also set USX2Y_NRPACKS to 1 then.
+ */
+#define USX2Y_NRPACKS_VARIABLE 1
+
+#ifdef USX2Y_NRPACKS_VARIABLE
+extern int nrpacks;
+#define nr_of_packs() nrpacks
+#else
+#define nr_of_packs() USX2Y_NRPACKS
+#endif
#define URBS_ASYNC_SEQ 10
#define URB_DATA_LEN_ASYNC_SEQ 32
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index ca7888495a9f..c7c7ec9c228b 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -28,33 +28,6 @@
#include "usx2y.h"
#include "usbusx2y.h"
-/* Default value used for nr of packs per urb.
- * 1 to 4 have been tested ok on uhci.
- * To use 3 on ohci, you'd need a patch:
- * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on
- * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"
- *
- * 1, 2 and 4 work out of the box on ohci, if I recall correctly.
- * Bigger is safer operation, smaller gives lower latencies.
- */
-#define USX2Y_NRPACKS 4
-
-/* If your system works ok with this module's parameter
- * nrpacks set to 1, you might as well comment
- * this define out, and thereby produce smaller, faster code.
- * You'd also set USX2Y_NRPACKS to 1 then.
- */
-#define USX2Y_NRPACKS_VARIABLE 1
-
-#ifdef USX2Y_NRPACKS_VARIABLE
-static int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
-#define nr_of_packs() nrpacks
-module_param(nrpacks, int, 0444);
-MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
-#else
-#define nr_of_packs() USX2Y_NRPACKS
-#endif
-
static int usx2y_urb_capt_retire(struct snd_usx2y_substream *subs)
{
struct urb *urb = subs->completed_urb;
@@ -67,14 +40,15 @@ static int usx2y_urb_capt_retire(struct snd_usx2y_substream *subs)
for (i = 0; i < nr_of_packs(); i++) {
cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
- snd_printk(KERN_ERR
- "active frame status %i. Most probably some hardware problem.\n",
- urb->iso_frame_desc[i].status);
+ dev_err(&usx2y->dev->dev,
+ "%s: active frame status %i. Most probably some hardware problem.\n",
+ __func__,
+ urb->iso_frame_desc[i].status);
return urb->iso_frame_desc[i].status;
}
len = urb->iso_frame_desc[i].actual_length / usx2y->stride;
if (!len) {
- snd_printd("0 == len ERROR!\n");
+ dev_dbg(&usx2y->dev->dev, "%s: 0 == len ERROR!\n", __func__);
continue;
}
@@ -128,7 +102,8 @@ static int usx2y_urb_play_prepare(struct snd_usx2y_substream *subs,
counts = cap_urb->iso_frame_desc[pack].actual_length / usx2y->stride;
count += counts;
if (counts < 43 || counts > 50) {
- snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
+ dev_err(&usx2y->dev->dev, "%s: should not be here with counts=%i\n",
+ __func__, counts);
return -EPIPE;
}
/* set up descriptor */
@@ -196,7 +171,8 @@ static int usx2y_urb_submit(struct snd_usx2y_substream *subs, struct urb *urb, i
urb->dev = subs->usx2y->dev; /* we need to set this at each time */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- snd_printk(KERN_ERR "usb_submit_urb() returned %i\n", err);
+ dev_err(&urb->dev->dev, "%s: usb_submit_urb() returned %i\n",
+ __func__, err);
return err;
}
return 0;
@@ -264,7 +240,8 @@ static void usx2y_clients_stop(struct usx2ydev *usx2y)
for (s = 0; s < 4; s++) {
subs = usx2y->subs[s];
if (subs) {
- snd_printdd("%i %p state=%i\n", s, subs, atomic_read(&subs->state));
+ dev_dbg(&usx2y->dev->dev, "%s: %i %p state=%i\n",
+ __func__, s, subs, atomic_read(&subs->state));
atomic_set(&subs->state, STATE_STOPPED);
}
}
@@ -276,8 +253,9 @@ static void usx2y_clients_stop(struct usx2ydev *usx2y)
for (u = 0; u < NRURBS; u++) {
urb = subs->urb[u];
if (urb)
- snd_printdd("%i status=%i start_frame=%i\n",
- u, urb->status, urb->start_frame);
+ dev_dbg(&usx2y->dev->dev,
+ "%s: %i status=%i start_frame=%i\n",
+ __func__, u, urb->status, urb->start_frame);
}
}
}
@@ -288,7 +266,8 @@ static void usx2y_clients_stop(struct usx2ydev *usx2y)
static void usx2y_error_urb_status(struct usx2ydev *usx2y,
struct snd_usx2y_substream *subs, struct urb *urb)
{
- snd_printk(KERN_ERR "ep=%i stalled with status=%i\n", subs->endpoint, urb->status);
+ dev_err(&usx2y->dev->dev, "%s: ep=%i stalled with status=%i\n",
+ __func__, subs->endpoint, urb->status);
urb->status = 0;
usx2y_clients_stop(usx2y);
}
@@ -300,10 +279,12 @@ static void i_usx2y_urb_complete(struct urb *urb)
struct snd_usx2y_substream *capsubs, *playbacksubs;
if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) {
- snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
- usb_get_current_frame_number(usx2y->dev),
- subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
- urb->status, urb->start_frame);
+ dev_dbg(&usx2y->dev->dev,
+ "%s: hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
+ __func__,
+ usb_get_current_frame_number(usx2y->dev),
+ subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
+ urb->status, urb->start_frame);
return;
}
if (unlikely(urb->status)) {
@@ -323,7 +304,6 @@ static void i_usx2y_urb_complete(struct urb *urb)
if (!usx2y_usbframe_complete(capsubs, playbacksubs, urb->start_frame)) {
usx2y->wait_iso_frame += nr_of_packs();
} else {
- snd_printdd("\n");
usx2y_clients_stop(usx2y);
}
}
@@ -373,8 +353,9 @@ static void i_usx2y_subs_startup(struct urb *urb)
static void usx2y_subs_prepare(struct snd_usx2y_substream *subs)
{
- snd_printdd("usx2y_substream_prepare(%p) ep=%i urb0=%p urb1=%p\n",
- subs, subs->endpoint, subs->urb[0], subs->urb[1]);
+ dev_dbg(&subs->usx2y->dev->dev,
+ "%s(%p) ep=%i urb0=%p urb1=%p\n",
+ __func__, subs, subs->endpoint, subs->urb[0], subs->urb[1]);
/* reset the pointer */
subs->hwptr = 0;
subs->hwptr_done = 0;
@@ -399,7 +380,7 @@ static void usx2y_urbs_release(struct snd_usx2y_substream *subs)
{
int i;
- snd_printdd("%s %i\n", __func__, subs->endpoint);
+ dev_dbg(&subs->usx2y->dev->dev, "%s %i\n", __func__, subs->endpoint);
for (i = 0; i < NRURBS; i++)
usx2y_urb_release(subs->urb + i,
subs != subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]);
@@ -505,7 +486,8 @@ static int usx2y_urbs_start(struct snd_usx2y_substream *subs)
urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);
+ dev_err(&urb->dev->dev, "%s: cannot submit datapipe for urb %d, err = %d\n",
+ __func__, i, err);
err = -EPIPE;
goto cleanup;
} else {
@@ -550,17 +532,16 @@ static int snd_usx2y_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_printdd("%s(START)\n", __func__);
+ dev_dbg(&subs->usx2y->dev->dev, "%s(START)\n", __func__);
if (atomic_read(&subs->state) == STATE_PREPARED &&
atomic_read(&subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]->state) >= STATE_PREPARED) {
atomic_set(&subs->state, STATE_PRERUNNING);
} else {
- snd_printdd("\n");
return -EPIPE;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_printdd("%s(STOP)\n", __func__);
+ dev_dbg(&subs->usx2y->dev->dev, "%s(STOP)\n", __func__);
if (atomic_read(&subs->state) >= STATE_PRERUNNING)
atomic_set(&subs->state, STATE_PREPARED);
break;
@@ -661,7 +642,8 @@ static void i_usx2y_04int(struct urb *urb)
struct usx2ydev *usx2y = urb->context;
if (urb->status)
- snd_printk(KERN_ERR "snd_usx2y_04int() urb->status=%i\n", urb->status);
+ dev_err(&urb->dev->dev, "%s() urb->status=%i\n",
+ __func__, urb->status);
if (!--usx2y->us04->len)
wake_up(&usx2y->in04_wait_queue);
}
@@ -751,7 +733,8 @@ static int usx2y_format_set(struct usx2ydev *usx2y, snd_pcm_format_t format)
usb_kill_urb(usx2y->in04_urb);
err = usb_set_interface(usx2y->dev, 0, alternate);
if (err) {
- snd_printk(KERN_ERR "usb_set_interface error\n");
+ dev_err(&usx2y->dev->dev, "%s: usb_set_interface error\n",
+ __func__);
return err;
}
usx2y->in04_urb->dev = usx2y->dev;
@@ -768,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;
@@ -777,8 +759,8 @@ 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);
- snd_printdd("snd_usx2y_hw_params(%p, %p)\n", substream, hw_params);
+ 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
*/
@@ -794,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;
}
/*
@@ -813,8 +792,8 @@ 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);
- snd_printdd("snd_usx2y_hw_free(%p)\n", substream);
+ guard(mutex)(&subs->usx2y->pcm_mutex);
+ dev_dbg(&subs->usx2y->dev->dev, "%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
@@ -833,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;
}
@@ -850,9 +828,9 @@ static int snd_usx2y_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
int err = 0;
- snd_printdd("%s(%p)\n", __func__, 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....
@@ -860,24 +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;
}
- snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
+ 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 36f2e31168fb..7c90214485d9 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -59,13 +59,13 @@ static int usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream *subs)
if (head >= ARRAY_SIZE(usx2y->hwdep_pcm_shm->captured_iso))
head = 0;
usx2y->hwdep_pcm_shm->capture_iso_start = head;
- snd_printdd("cap start %i\n", head);
+ dev_dbg(&usx2y->dev->dev, "cap start %i\n", head);
}
for (i = 0; i < nr_of_packs(); i++) {
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
- snd_printk(KERN_ERR
- "active frame status %i. Most probably some hardware problem.\n",
- urb->iso_frame_desc[i].status);
+ dev_err(&usx2y->dev->dev,
+ "active frame status %i. Most probably some hardware problem.\n",
+ urb->iso_frame_desc[i].status);
return urb->iso_frame_desc[i].status;
}
lens += urb->iso_frame_desc[i].actual_length / usx2y->stride;
@@ -120,7 +120,7 @@ static int usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream *subs,
/* calculate the size of a packet */
counts = shm->captured_iso[shm->playback_iso_head].length / usx2y->stride;
if (counts < 43 || counts > 50) {
- snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
+ dev_err(&usx2y->dev->dev, "should not be here with counts=%i\n", counts);
return -EPIPE;
}
/* set up descriptor */
@@ -234,10 +234,11 @@ static void i_usx2y_usbpcm_urb_complete(struct urb *urb)
struct snd_usx2y_substream *capsubs, *capsubs2, *playbacksubs;
if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) {
- snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
- usb_get_current_frame_number(usx2y->dev),
- subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
- urb->status, urb->start_frame);
+ dev_dbg(&usx2y->dev->dev,
+ "hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
+ usb_get_current_frame_number(usx2y->dev),
+ subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
+ urb->status, urb->start_frame);
return;
}
if (unlikely(urb->status)) {
@@ -255,7 +256,6 @@ static void i_usx2y_usbpcm_urb_complete(struct urb *urb)
if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
usx2y->wait_iso_frame += nr_of_packs();
} else {
- snd_printdd("\n");
usx2y_clients_stop(usx2y);
}
}
@@ -275,7 +275,8 @@ static void usx2y_usbpcm_urbs_release(struct snd_usx2y_substream *subs)
{
int i;
- snd_printdd("snd_usx2y_urbs_release() %i\n", subs->endpoint);
+ dev_dbg(&subs->usx2y->dev->dev,
+ "snd_usx2y_urbs_release() %i\n", subs->endpoint);
for (i = 0; i < NRURBS; i++)
usx2y_hwdep_urb_release(subs->urb + i);
}
@@ -364,8 +365,8 @@ 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);
- snd_printdd("%s(%p)\n", __func__, substream);
+ 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];
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -393,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;
}
@@ -456,11 +456,12 @@ static int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs)
urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
err = usb_submit_urb(urb, GFP_KERNEL);
if (err < 0) {
- snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
+ dev_err(&urb->dev->dev,
+ "cannot usb_submit_urb() for urb %d, err = %d\n",
+ u, err);
err = -EPIPE;
goto cleanup;
} else {
- snd_printdd("%i\n", urb->start_frame);
if (!u)
usx2y->wait_iso_frame = urb->start_frame;
}
@@ -500,17 +501,15 @@ static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
int err = 0;
- snd_printdd("snd_usx2y_pcm_prepare(%p)\n", 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);
}
@@ -521,18 +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;
}
- snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
- "self" : "playpipe");
+ 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) {
@@ -540,27 +540,25 @@ static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
if (atomic_read(&subs->state) < STATE_PREPARED) {
while (usx2y_iso_frames_per_buffer(runtime, usx2y) >
usx2y->hwdep_pcm_shm->captured_iso_frames) {
- snd_printdd("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;
- }
+ dev_dbg(&usx2y->dev->dev,
+ "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))
+ return -ERESTARTSYS;
}
err = usx2y_usbpcm_urbs_start(subs);
if (err < 0)
- goto up_prepare_mutex;
+ return err;
}
- snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
- usx2y_iso_frames_per_buffer(runtime, usx2y),
- usx2y->hwdep_pcm_shm->captured_iso_frames);
+ dev_dbg(&usx2y->dev->dev,
+ "Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
+ usx2y_iso_frames_per_buffer(runtime, usx2y),
+ usx2y->hwdep_pcm_shm->captured_iso_frames);
} else {
usx2y->hwdep_pcm_shm->capture_iso_start = -1;
}
- up_prepare_mutex:
- mutex_unlock(&usx2y->pcm_mutex);
return err;
}
@@ -641,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;
}
@@ -654,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;
}
@@ -698,7 +694,8 @@ static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, str
/* if userspace tries to mmap beyond end of our buffer, fail */
if (size > USX2Y_HWDEP_PCM_PAGES) {
- snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
+ dev_dbg(hw->card->dev, "%s: %lu > %lu\n", __func__,
+ size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
return -EINVAL;
}
diff --git a/sound/usb/validate.c b/sound/usb/validate.c
index 6fe206f6e911..4bb4893f6e74 100644
--- a/sound/usb/validate.c
+++ b/sound/usb/validate.c
@@ -221,6 +221,17 @@ static bool validate_uac3_feature_unit(const void *p,
return d->bLength >= sizeof(*d) + 4 + 2;
}
+static bool validate_uac3_power_domain_unit(const void *p,
+ const struct usb_desc_validator *v)
+{
+ const struct uac3_power_domain_descriptor *d = p;
+
+ if (d->bLength < sizeof(*d))
+ return false;
+ /* baEntities[] + wPDomainDescrStr */
+ return d->bLength >= sizeof(*d) + d->bNrEntities + 2;
+}
+
static bool validate_midi_out_jack(const void *p,
const struct usb_desc_validator *v)
{
@@ -255,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,
@@ -274,8 +289,9 @@ static const struct usb_desc_validator audio_validators[] = {
/* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */
FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit),
FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit),
- FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit),
- /* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_FEATURE_UNIT, validate_uac3_feature_unit),
+ 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,
@@ -285,6 +301,7 @@ static const struct usb_desc_validator audio_validators[] = {
struct uac3_clock_multiplier_descriptor),
/* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */
/* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */
+ FUNC(UAC_VERSION_3, UAC3_POWER_DOMAIN, validate_uac3_power_domain_unit),
{ } /* terminator */
};