diff options
Diffstat (limited to 'sound/usb')
47 files changed, 3408 insertions, 824 deletions
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index de1f030eaf72..20f34d2ace5f 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -397,7 +397,7 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new vol_elements[] = { +static const struct snd_kcontrol_new vol_elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Playback Volume", @@ -437,7 +437,7 @@ static struct snd_kcontrol_new vol_elements[] = { {} }; -static struct snd_kcontrol_new mute_elements[] = { +static const struct snd_kcontrol_new mute_elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Playback Switch", @@ -471,7 +471,7 @@ static struct snd_kcontrol_new mute_elements[] = { {} }; -static struct snd_kcontrol_new elements[] = { +static const struct snd_kcontrol_new elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line/Phono Capture Route", @@ -517,7 +517,7 @@ static int usb6fire_control_add_virtual( struct control_runtime *rt, struct snd_card *card, char *name, - struct snd_kcontrol_new *elems) + const struct snd_kcontrol_new *elems) { int ret; int i; diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 88ac1c4ee163..7168f1c6a37a 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -446,18 +446,6 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub) return 0; } -static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub, - params_buffer_bytes(hw_params)); -} - -static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub) -{ - return snd_pcm_lib_free_vmalloc_buffer(alsa_sub); -} - static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) { struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); @@ -554,13 +542,9 @@ static snd_pcm_uframes_t usb6fire_pcm_pointer( static const struct snd_pcm_ops pcm_ops = { .open = usb6fire_pcm_open, .close = usb6fire_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = usb6fire_pcm_hw_params, - .hw_free = usb6fire_pcm_hw_free, .prepare = usb6fire_pcm_prepare, .trigger = usb6fire_pcm_trigger, .pointer = usb6fire_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, }; static void usb6fire_pcm_init_urb(struct pcm_urb *urb, @@ -659,14 +643,8 @@ int usb6fire_pcm_init(struct sfire_chip *chip) strcpy(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); - if (ret) { - usb6fire_pcm_buffers_destroy(rt); - kfree(rt); - dev_err(&chip->dev->dev, - "error preallocating pcm buffers.\n"); - return ret; - } rt->instance = pcm; chip->pcm = rt; diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index e2c53a0841da..059242f15d75 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -107,24 +107,24 @@ config SND_USB_US122L will be called snd-usb-us122l. config SND_USB_6FIRE - tristate "TerraTec DMX 6Fire USB" - select FW_LOADER - select BITREVERSE - select SND_RAWMIDI - select SND_PCM - select SND_VMASTER - help - Say Y here to include support for TerraTec 6fire DMX USB interface. - - You will need firmware files in order to be able to use the device - after it has been coldstarted. An install script for the firmware - and further help can be found at - http://sixfireusb.sourceforge.net + tristate "TerraTec DMX 6Fire USB" + select FW_LOADER + select BITREVERSE + select SND_RAWMIDI + select SND_PCM + select SND_VMASTER + help + Say Y here to include support for TerraTec 6fire DMX USB interface. + + You will need firmware files in order to be able to use the device + after it has been coldstarted. An install script for the firmware + and further help can be found at + http://sixfireusb.sourceforge.net config SND_USB_HIFACE - tristate "M2Tech hiFace USB-SPDIF driver" - select SND_PCM - help + tristate "M2Tech hiFace USB-SPDIF driver" + select SND_PCM + help Select this option to include support for M2Tech hiFace USB-SPDIF interface. diff --git a/sound/usb/Makefile b/sound/usb/Makefile index e1ce257ab705..78edd7d2f418 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -11,12 +11,14 @@ snd-usb-audio-objs := card.o \ mixer.o \ mixer_quirks.o \ mixer_scarlett.o \ + mixer_scarlett_gen2.o \ mixer_us16x08.o \ pcm.o \ power.o \ proc.o \ quirks.o \ - stream.o + stream.o \ + validate.o snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index 6e3175826faf..010976d9ceb2 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -25,9 +25,9 @@ static const struct usb_device_id id_table[] = { { }, }; -static unsigned char device_cmd_prefix[] = {0x03, 0x00}; +static const unsigned char device_cmd_prefix[] = {0x03, 0x00}; -static unsigned char bcd2000_init_sequence[] = { +static const unsigned char bcd2000_init_sequence[] = { 0x07, 0x00, 0x00, 0x00, 0x78, 0x48, 0x1c, 0x81, 0xc4, 0x00, 0x00, 0x00, 0x5e, 0x53, 0x4a, 0xf7, 0x18, 0xfa, 0x11, 0xff, 0x6c, 0xf3, 0x90, 0xff, diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 444bb637ce13..e9243d53a107 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -30,7 +30,7 @@ #define MAKE_CHECKBYTE(cdev,stream,i) \ (stream << 1) | (~(i / (cdev->n_streams * BYTES_PER_SAMPLE_USB)) & 1) -static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = { +static const struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), .formats = SNDRV_PCM_FMTBIT_S24_3BE, @@ -167,18 +167,11 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) return 0; } -static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_alloc_vmalloc_buffer(sub, - params_buffer_bytes(hw_params)); -} - static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) { struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub); deactivate_substream(cdev, sub); - return snd_pcm_lib_free_vmalloc_buffer(sub); + return 0; } /* this should probably go upstream */ @@ -186,7 +179,7 @@ static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) #error "Change this table" #endif -static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, +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) @@ -328,13 +321,10 @@ unlock: static const struct snd_pcm_ops snd_usb_caiaq_ops = { .open = snd_usb_caiaq_substream_open, .close = snd_usb_caiaq_substream_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_usb_caiaq_pcm_hw_params, .hw_free = snd_usb_caiaq_pcm_hw_free, .prepare = snd_usb_caiaq_pcm_prepare, .trigger = snd_usb_caiaq_pcm_trigger, .pointer = snd_usb_caiaq_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, }; static void check_for_elapsed_periods(struct snd_usb_caiaqdev *cdev, @@ -843,6 +833,8 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) &snd_usb_caiaq_ops); snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_caiaq_ops); + snd_pcm_set_managed_buffer_all(cdev->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); cdev->data_cb_info = kmalloc_array(N_URBS, sizeof(struct snd_usb_caiaq_cb_info), diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index 532e354f6124..af459c49baf4 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -163,14 +163,14 @@ struct caiaq_controller { int index; }; -static struct caiaq_controller ak1_controller[] = { +static const struct caiaq_controller ak1_controller[] = { { "LED left", 2 }, { "LED middle", 1 }, { "LED right", 0 }, { "LED ring", 3 } }; -static struct caiaq_controller rk2_controller[] = { +static const struct caiaq_controller rk2_controller[] = { { "LED 1", 5 }, { "LED 2", 4 }, { "LED 3", 3 }, @@ -196,7 +196,7 @@ static struct caiaq_controller rk2_controller[] = { { "LED 7seg_3g", 23 } }; -static struct caiaq_controller rk3_controller[] = { +static const struct caiaq_controller rk3_controller[] = { { "LED 7seg_1a", 0 + 0 }, { "LED 7seg_1b", 0 + 1 }, { "LED 7seg_1c", 0 + 2 }, @@ -244,7 +244,7 @@ static struct caiaq_controller rk3_controller[] = { { "LED pedal", 32 + 8 } }; -static struct caiaq_controller kore_controller[] = { +static const struct caiaq_controller kore_controller[] = { { "LED F1", 8 | CNT_INTVAL }, { "LED F2", 12 | CNT_INTVAL }, { "LED F3", 0 | CNT_INTVAL }, @@ -278,7 +278,7 @@ static struct caiaq_controller kore_controller[] = { { "LED control", 26 | CNT_INTVAL } }; -static struct caiaq_controller a8dj_controller[] = { +static const struct caiaq_controller a8dj_controller[] = { { "Current input mode", 0 | CNT_INTVAL }, { "GND lift for TC Vinyl mode", 24 + 0 }, { "GND lift for TC CD/Line mode", 24 + 1 }, @@ -286,11 +286,11 @@ static struct caiaq_controller a8dj_controller[] = { { "Software lock", 40 } }; -static struct caiaq_controller a4dj_controller[] = { +static const struct caiaq_controller a4dj_controller[] = { { "Current input mode", 0 | CNT_INTVAL } }; -static struct caiaq_controller kontrolx1_controller[] = { +static const struct caiaq_controller kontrolx1_controller[] = { { "LED FX A: ON", 7 | CNT_INTVAL }, { "LED FX A: 1", 6 | CNT_INTVAL }, { "LED FX A: 2", 5 | CNT_INTVAL }, @@ -327,7 +327,7 @@ static struct caiaq_controller kontrolx1_controller[] = { { "LED Deck B: SYNC", 8 | CNT_INTVAL }, }; -static struct caiaq_controller kontrols4_controller[] = { +static const struct caiaq_controller kontrols4_controller[] = { { "LED: Master: Quant", 10 | CNT_INTVAL }, { "LED: Master: Headphone", 11 | CNT_INTVAL }, { "LED: Master: Master", 12 | CNT_INTVAL }, @@ -500,7 +500,7 @@ static struct caiaq_controller kontrols4_controller[] = { { "LED: FX2: Mode", 133 | CNT_INTVAL }, }; -static struct caiaq_controller maschine_controller[] = { +static const struct caiaq_controller maschine_controller[] = { { "LED: Pad 1", 3 | CNT_INTVAL }, { "LED: Pad 2", 2 | CNT_INTVAL }, { "LED: Pad 3", 1 | CNT_INTVAL }, @@ -568,7 +568,7 @@ static struct caiaq_controller maschine_controller[] = { { "Backlight Display", 59 | CNT_INTVAL } }; -static int add_controls(struct caiaq_controller *c, int num, +static int add_controls(const struct caiaq_controller *c, int num, struct snd_usb_caiaqdev *cdev) { int i, ret; diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 533eb69fe4e6..1e2cf2f08eec 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -14,13 +14,13 @@ #include "device.h" #include "input.h" -static unsigned short keycode_ak1[] = { KEY_C, KEY_B, KEY_A }; -static unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4, +static const unsigned short keycode_ak1[] = { KEY_C, KEY_B, KEY_A }; +static const unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7 }; -static unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4, +static const unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 }; -static unsigned short keycode_kore[] = { +static const unsigned short keycode_kore[] = { KEY_FN_F1, /* "menu" */ KEY_FN_F7, /* "lcd backlight */ KEY_FN_F2, /* "control" */ @@ -60,7 +60,7 @@ static unsigned short keycode_kore[] = { #define MASCHINE_PADS (16) #define MASCHINE_PAD(X) ((X) + ABS_PRESSURE) -static unsigned short keycode_maschine[] = { +static const unsigned short keycode_maschine[] = { MASCHINE_BUTTON(40), /* mute */ MASCHINE_BUTTON(39), /* solo */ MASCHINE_BUTTON(38), /* select */ diff --git a/sound/usb/card.c b/sound/usb/card.c index db91dc76cc91..827fb0bc8b56 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -74,6 +74,7 @@ static bool autoclock = true; static char *quirk_alias[SNDRV_CARDS]; bool snd_usb_use_vmalloc = true; +bool snd_usb_skip_validation; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); @@ -96,6 +97,8 @@ module_param_array(quirk_alias, charp, NULL, 0444); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); 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)."); /* * we keep the snd_usb_audio_t instances by ourselves for merging @@ -597,6 +600,10 @@ static int usb_audio_probe(struct usb_interface *intf, } } if (! chip) { + err = snd_usb_apply_boot_quirk_once(dev, intf, quirk, id); + if (err < 0) + goto __error; + /* it's a fresh one. * now look for an empty slot and create a new card instance */ diff --git a/sound/usb/card.h b/sound/usb/card.h index 2991b9986f66..395403a2d33f 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -145,6 +145,7 @@ struct snd_usb_substream { struct snd_usb_endpoint *sync_endpoint; unsigned long flags; bool need_setup_ep; /* (re)configure EP at prepare? */ + bool need_setup_fmt; /* (re)configure fmt after resume? */ unsigned int speed; /* USB_SPEED_XXX */ u64 formats; /* format bitmasks (all or'ed) */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 72e9bdf76115..a48313dfa967 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -38,39 +38,37 @@ static void *find_uac_clock_desc(struct usb_host_interface *iface, int id, static bool validate_clock_source_v2(void *p, int id) { struct uac_clock_source_descriptor *cs = p; - return cs->bLength == sizeof(*cs) && cs->bClockID == id; + return cs->bClockID == id; } static bool validate_clock_source_v3(void *p, int id) { struct uac3_clock_source_descriptor *cs = p; - return cs->bLength == sizeof(*cs) && cs->bClockID == id; + return cs->bClockID == id; } static bool validate_clock_selector_v2(void *p, int id) { struct uac_clock_selector_descriptor *cs = p; - return cs->bLength >= sizeof(*cs) && cs->bClockID == id && - cs->bLength == 7 + cs->bNrInPins; + return cs->bClockID == id; } static bool validate_clock_selector_v3(void *p, int id) { struct uac3_clock_selector_descriptor *cs = p; - return cs->bLength >= sizeof(*cs) && cs->bClockID == id && - cs->bLength == 11 + cs->bNrInPins; + return cs->bClockID == id; } static bool validate_clock_multiplier_v2(void *p, int id) { struct uac_clock_multiplier_descriptor *cs = p; - return cs->bLength == sizeof(*cs) && cs->bClockID == id; + return cs->bClockID == id; } static bool validate_clock_multiplier_v3(void *p, int id) { struct uac3_clock_multiplier_descriptor *cs = p; - return cs->bLength == sizeof(*cs) && cs->bClockID == id; + return cs->bClockID == id; } #define DEFINE_FIND_HELPER(name, obj, validator, type) \ @@ -153,8 +151,34 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i return ret; } +/* + * Assume the clock is valid if clock source supports only one single sample + * rate, the terminal is connected directly to it (there is no clock selector) + * and clock type is internal. This is to deal with some Denon DJ controllers + * that always reports that clock is invalid. + */ +static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + int source_id) +{ + if (fmt->protocol == UAC_VERSION_2) { + struct uac_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source(chip->ctrl_intf, source_id); + + if (!cs_desc) + return false; + + return (fmt->nr_rates == 1 && + (fmt->clock & 0xff) == cs_desc->bClockID && + (cs_desc->bmAttributes & 0x3) != + UAC_CLOCK_SOURCE_TYPE_EXT); + } + + return false; +} + static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, - int protocol, + struct audioformat *fmt, int source_id) { int err; @@ -162,26 +186,26 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, struct usb_device *dev = chip->dev; u32 bmControls; - if (protocol == UAC_VERSION_3) { + if (fmt->protocol == UAC_VERSION_3) { struct uac3_clock_source_descriptor *cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id); if (!cs_desc) - return 0; + return false; bmControls = le32_to_cpu(cs_desc->bmControls); } else { /* UAC_VERSION_1/2 */ struct uac_clock_source_descriptor *cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, source_id); if (!cs_desc) - return 0; + return false; bmControls = cs_desc->bmControls; } /* If a clock source can't tell us whether it's valid, we assume it is */ if (!uac_v2v3_control_is_readable(bmControls, UAC2_CS_CONTROL_CLOCK_VALID)) - return 1; + return true; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, @@ -193,13 +217,17 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, dev_warn(&dev->dev, "%s(): cannot get clock validity for id %d\n", __func__, source_id); - return 0; + return false; } - return !!data; + if (data) + return true; + else + return uac_clock_source_is_valid_quirk(chip, fmt, source_id); } -static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, +static int __uac_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, int entity_id, unsigned long *visited, bool validate) { struct uac_clock_source_descriptor *source; @@ -219,7 +247,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); if (source) { entity_id = source->bClockID; - if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2, + if (validate && !uac_clock_source_is_valid(chip, fmt, entity_id)) { usb_audio_err(chip, "clock source %d is not valid, cannot use\n", @@ -250,8 +278,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, } cur = ret; - ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1], - visited, validate); + ret = __uac_clock_find_source(chip, fmt, + selector->baCSourceID[ret - 1], + visited, validate); if (!validate || ret > 0 || !chip->autoclock) return ret; @@ -262,8 +291,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, if (i == cur) continue; - ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1], - visited, true); + ret = __uac_clock_find_source(chip, fmt, + selector->baCSourceID[i - 1], + visited, true); if (ret < 0) continue; @@ -283,14 +313,16 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, /* FIXME: multipliers only act as pass-thru element for now */ multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); if (multiplier) - return __uac_clock_find_source(chip, multiplier->bCSourceID, - visited, validate); + return __uac_clock_find_source(chip, fmt, + multiplier->bCSourceID, + visited, validate); return -EINVAL; } -static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, - unsigned long *visited, bool validate) +static int __uac3_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, int entity_id, + unsigned long *visited, bool validate) { struct uac3_clock_source_descriptor *source; struct uac3_clock_selector_descriptor *selector; @@ -309,7 +341,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id); if (source) { entity_id = source->bClockID; - if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3, + if (validate && !uac_clock_source_is_valid(chip, fmt, entity_id)) { usb_audio_err(chip, "clock source %d is not valid, cannot use\n", @@ -340,7 +372,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, } cur = ret; - ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1], + ret = __uac3_clock_find_source(chip, fmt, + selector->baCSourceID[ret - 1], visited, validate); if (!validate || ret > 0 || !chip->autoclock) return ret; @@ -352,8 +385,9 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, if (i == cur) continue; - ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1], - visited, true); + ret = __uac3_clock_find_source(chip, fmt, + selector->baCSourceID[i - 1], + visited, true); if (ret < 0) continue; @@ -374,7 +408,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf, entity_id); if (multiplier) - return __uac3_clock_find_source(chip, multiplier->bCSourceID, + return __uac3_clock_find_source(chip, fmt, + multiplier->bCSourceID, visited, validate); return -EINVAL; @@ -391,18 +426,18 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, * * Returns the clock source UnitID (>=0) on success, or an error. */ -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, - int entity_id, bool validate) +int snd_usb_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, bool validate) { DECLARE_BITMAP(visited, 256); memset(visited, 0, sizeof(visited)); - switch (protocol) { + switch (fmt->protocol) { case UAC_VERSION_2: - return __uac_clock_find_source(chip, entity_id, visited, + return __uac_clock_find_source(chip, fmt, fmt->clock, visited, validate); case UAC_VERSION_3: - return __uac3_clock_find_source(chip, entity_id, visited, + return __uac3_clock_find_source(chip, fmt, fmt->clock, visited, validate); default: return -EINVAL; @@ -503,8 +538,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, * automatic clock selection if the current clock is not * valid. */ - clock = snd_usb_clock_find_source(chip, fmt->protocol, - fmt->clock, true); + clock = snd_usb_clock_find_source(chip, fmt, true); if (clock < 0) { /* We did not find a valid clock, but that might be * because the current sample rate does not match an @@ -512,8 +546,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, * and we will do another validation after setting the * rate. */ - clock = snd_usb_clock_find_source(chip, fmt->protocol, - fmt->clock, false); + clock = snd_usb_clock_find_source(chip, fmt, false); if (clock < 0) return clock; } @@ -579,7 +612,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, validation: /* validate clock after rate change */ - if (!uac_clock_source_is_valid(chip, fmt->protocol, clock)) + if (!uac_clock_source_is_valid(chip, fmt, clock)) return -ENXIO; return 0; } diff --git a/sound/usb/clock.h b/sound/usb/clock.h index 076e31b79ee0..68df0fbe09d0 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate); -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, - int entity_id, bool validate); +int snd_usb_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, bool validate); #endif /* __USBAUDIO_CLOCK_H */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a2ab8e8d3a93..4a9a2f6ef5a4 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -388,6 +388,9 @@ static void snd_complete_urb(struct urb *urb) } prepare_outbound_urb(ep, ctx); + /* can be stopped during prepare callback */ + if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) + goto exit_clear; } else { retire_inbound_urb(ep, ctx); /* can be stopped during retire callback */ diff --git a/sound/usb/format.c b/sound/usb/format.c index d79db71305f6..9f5cb4ed3a0c 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -151,6 +151,19 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, return pcm_formats; } +static int set_fixed_rate(struct audioformat *fp, int rate, int rate_bits) +{ + kfree(fp->rate_table); + fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL); + if (!fp->rate_table) + return -ENOMEM; + fp->nr_rates = 1; + fp->rate_min = rate; + fp->rate_max = rate; + fp->rates = rate_bits; + fp->rate_table[0] = rate; + return 0; +} /* * parse the format descriptor and stores the possible sample rates @@ -223,6 +236,14 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof fp->rate_min = combine_triple(&fmt[offset + 1]); fp->rate_max = combine_triple(&fmt[offset + 4]); } + + /* Jabra Evolve 65 headset */ + if (chip->usb_id == USB_ID(0x0b0e, 0x030b)) { + /* only 48kHz for playback while keeping 16kHz for capture */ + if (fp->nr_rates != 1) + return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000); + } + return 0; } @@ -292,21 +313,14 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip, struct audioformat *fp) { switch (chip->usb_id) { - case USB_ID(0x0E41, 0x4241): /* Line6 Helix */ - case USB_ID(0x0E41, 0x4242): /* Line6 Helix Rack */ - case USB_ID(0x0E41, 0x4244): /* Line6 Helix LT */ - case USB_ID(0x0E41, 0x4246): /* Line6 HX-Stomp */ - /* supported rates: 48Khz */ - kfree(fp->rate_table); - fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL); - if (!fp->rate_table) - return -ENOMEM; - fp->nr_rates = 1; - fp->rate_min = 48000; - fp->rate_max = 48000; - fp->rates = SNDRV_PCM_RATE_48000; - fp->rate_table[0] = 48000; - return 0; + case USB_ID(0x0e41, 0x4241): /* Line6 Helix */ + case USB_ID(0x0e41, 0x4242): /* Line6 Helix Rack */ + case USB_ID(0x0e41, 0x4244): /* Line6 Helix LT */ + case USB_ID(0x0e41, 0x4246): /* Line6 HX-Stomp */ + case USB_ID(0x0e41, 0x4248): /* Line6 Helix >= fw 2.82 */ + case USB_ID(0x0e41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */ + case USB_ID(0x0e41, 0x424a): /* Line6 Helix LT >= fw 2.82 */ + return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000); } return -ENODEV; @@ -322,8 +336,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, struct usb_device *dev = chip->dev; unsigned char tmp[2], *data; int nr_triplets, data_size, ret = 0, ret_l6; - int clock = snd_usb_clock_find_source(chip, fp->protocol, - fp->clock, false); + int clock = snd_usb_clock_find_source(chip, fp, false); if (clock < 0) { dev_err(&dev->dev, diff --git a/sound/usb/helper.h b/sound/usb/helper.h index 6afb70156ec4..5e8a18b4e7b9 100644 --- a/sound/usb/helper.h +++ b/sound/usb/helper.h @@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip) return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber; } +/* in validate.c */ +bool snd_usb_validate_audio_desc(void *p, int protocol); +bool snd_usb_validate_midi_desc(void *p); + #endif /* __USBAUDIO_HELPER_H */ diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c index c406497c5919..a148caa5f48e 100644 --- a/sound/usb/hiface/pcm.c +++ b/sound/usb/hiface/pcm.c @@ -415,18 +415,6 @@ static int hiface_pcm_close(struct snd_pcm_substream *alsa_sub) return 0; } -static int hiface_pcm_hw_params(struct snd_pcm_substream *alsa_sub, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub, - params_buffer_bytes(hw_params)); -} - -static int hiface_pcm_hw_free(struct snd_pcm_substream *alsa_sub) -{ - return snd_pcm_lib_free_vmalloc_buffer(alsa_sub); -} - static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub) { struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); @@ -512,13 +500,9 @@ static snd_pcm_uframes_t hiface_pcm_pointer(struct snd_pcm_substream *alsa_sub) static const struct snd_pcm_ops pcm_ops = { .open = hiface_pcm_open, .close = hiface_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = hiface_pcm_hw_params, - .hw_free = hiface_pcm_hw_free, .prepare = hiface_pcm_prepare, .trigger = hiface_pcm_trigger, .pointer = hiface_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, }; static int hiface_pcm_init_urb(struct pcm_urb *urb, @@ -614,6 +598,8 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) strlcpy(pcm->name, "USB-SPDIF Audio", sizeof(pcm->name)); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); rt->instance = pcm; diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c index 82abef3fe90d..663d608c4287 100644 --- a/sound/usb/line6/capture.c +++ b/sound/usb/line6/capture.c @@ -247,7 +247,6 @@ static int snd_line6_capture_close(struct snd_pcm_substream *substream) const struct snd_pcm_ops snd_line6_capture_ops = { .open = snd_line6_capture_open, .close = snd_line6_capture_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_line6_hw_params, .hw_free = snd_line6_hw_free, .prepare = snd_line6_prepare, diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index ab2ec896f49c..b5a3f754a4f1 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -342,7 +342,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, if (address > 0xffff || datalen > 0xff) return -EINVAL; - len = kmalloc(sizeof(*len), GFP_KERNEL); + len = kmalloc(1, GFP_KERNEL); if (!len) return -ENOMEM; @@ -418,7 +418,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, if (address > 0xffff || datalen > 0xffff) return -EINVAL; - status = kmalloc(sizeof(*status), GFP_KERNEL); + status = kmalloc(1, GFP_KERNEL); if (!status) return -ENOMEM; diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index 2c03e0f6bf72..fdbdfb7bce92 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -359,13 +359,6 @@ int snd_line6_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto error; - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); - goto error; - } - pstr->period = params_period_bytes(hw_params); error: mutex_unlock(&line6pcm->state_mutex); @@ -381,7 +374,7 @@ int snd_line6_hw_free(struct snd_pcm_substream *substream) mutex_lock(&line6pcm->state_mutex); line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); mutex_unlock(&line6pcm->state_mutex); - return snd_pcm_lib_free_pages(substream); + return 0; } @@ -501,10 +494,8 @@ static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); /* pre-allocation of buffers */ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data - (GFP_KERNEL), 64 * 1024, - 128 * 1024); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + NULL, 64 * 1024, 128 * 1024); return 0; } @@ -550,6 +541,15 @@ int line6_init_pcm(struct usb_line6 *line6, line6pcm->volume_monitor = 255; line6pcm->line6 = line6; + spin_lock_init(&line6pcm->out.lock); + spin_lock_init(&line6pcm->in.lock); + line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; + + line6->line6pcm = line6pcm; + + pcm->private_data = line6pcm; + pcm->private_free = line6_cleanup_pcm; + line6pcm->max_packet_size_in = usb_maxpacket(line6->usbdev, usb_rcvisocpipe(line6->usbdev, ep_read), 0); @@ -562,15 +562,6 @@ int line6_init_pcm(struct usb_line6 *line6, return -EINVAL; } - spin_lock_init(&line6pcm->out.lock); - spin_lock_init(&line6pcm->in.lock); - line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; - - line6->line6pcm = line6pcm; - - pcm->private_data = line6pcm; - pcm->private_free = line6_cleanup_pcm; - err = line6_create_audio_out_urbs(line6pcm); if (err < 0) return err; diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 2e8ead3f9bc2..01930ce7bd75 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -392,7 +392,6 @@ static int snd_line6_playback_close(struct snd_pcm_substream *substream) const struct snd_pcm_ops snd_line6_playback_ops = { .open = snd_line6_playback_open, .close = snd_line6_playback_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_line6_hw_params, .hw_free = snd_line6_hw_free, .prepare = snd_line6_prepare, diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c index ee4c9d220fdf..cd44cb5f1310 100644 --- a/sound/usb/line6/pod.c +++ b/sound/usb/line6/pod.c @@ -110,7 +110,7 @@ enum { POD_BUSY_MIDISEND }; -static struct snd_ratden pod_ratden = { +static const struct snd_ratden pod_ratden = { .num_min = 78125, .num_max = 78125, .num_step = 1, diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 27bf61c177c0..d37db32ecd3b 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -42,7 +42,7 @@ struct usb_line6_podhd { #define line6_to_podhd(x) container_of(x, struct usb_line6_podhd, line6) -static struct snd_ratden podhd_ratden = { +static const struct snd_ratden podhd_ratden = { .num_min = 48000, .num_max = 48000, .num_step = 1, diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index d0a555dbe324..94dd5e7ab2e6 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -63,7 +63,7 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); #define TONEPORT_PCM_DELAY 1 -static struct snd_ratden toneport_ratden = { +static const struct snd_ratden toneport_ratden = { .num_min = 44100, .num_max = 44100, .num_step = 1, diff --git a/sound/usb/midi.c b/sound/usb/midi.c index b737f0ec77d0..392e5fda680c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1408,8 +1408,8 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, /* * Some devices only work with 9 bytes packet size: */ - case USB_ID(0x0644, 0x800E): /* Tascam US-122L */ - case USB_ID(0x0644, 0x800F): /* Tascam US-144 */ + case USB_ID(0x0644, 0x800e): /* Tascam US-122L */ + case USB_ID(0x0644, 0x800f): /* Tascam US-144 */ ep->max_transfer = 9; break; } diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 307b72d5fffa..884e740a785c 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -730,11 +730,7 @@ static int capture_pcm_hw_params(struct snd_pcm_substream *substream, mutex_lock(&ua->mutex); err = start_usb_capture(ua); mutex_unlock(&ua->mutex); - if (err < 0) - return err; - - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + return err; } static int playback_pcm_hw_params(struct snd_pcm_substream *substream, @@ -748,16 +744,7 @@ static int playback_pcm_hw_params(struct snd_pcm_substream *substream, if (err >= 0) err = start_usb_playback(ua); mutex_unlock(&ua->mutex); - if (err < 0) - return err; - - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); -} - -static int ua101_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_vmalloc_buffer(substream); + return err; } static int capture_pcm_prepare(struct snd_pcm_substream *substream) @@ -883,25 +870,19 @@ static snd_pcm_uframes_t playback_pcm_pointer(struct snd_pcm_substream *subs) static const struct snd_pcm_ops capture_pcm_ops = { .open = capture_pcm_open, .close = capture_pcm_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = capture_pcm_hw_params, - .hw_free = ua101_pcm_hw_free, .prepare = capture_pcm_prepare, .trigger = capture_pcm_trigger, .pointer = capture_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_pcm_ops = { .open = playback_pcm_open, .close = playback_pcm_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = playback_pcm_hw_params, - .hw_free = ua101_pcm_hw_free, .prepare = playback_pcm_prepare, .trigger = playback_pcm_trigger, .pointer = playback_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, }; static const struct uac_format_type_i_discrete_descriptor * @@ -1296,6 +1277,8 @@ static int ua101_probe(struct usb_interface *interface, strcpy(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, + NULL, 0, 0); err = snd_usbmidi_create(card, ua->intf[INTF_MIDI], &ua->midi_list, &midi_quirk); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index b5927c3d5bc0..81b2db0edd5f 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -739,14 +739,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc) { int mu_channels; - void *c; - - if (desc->bLength < sizeof(*desc)) - return -EINVAL; - if (!desc->bNrInPins) - return -EINVAL; - if (desc->bLength < sizeof(*desc) + desc->bNrInPins) - return -EINVAL; switch (state->mixer->protocol) { case UAC_VERSION_1: @@ -762,233 +754,254 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, break; } - if (!mu_channels) - return 0; - - c = uac_mixer_unit_bmControls(desc, state->mixer->protocol); - if (c - (void *)desc + (mu_channels - 1) / 8 >= desc->bLength) - return 0; /* no bmControls -> skip */ - return mu_channels; } /* - * parse the source unit recursively until it reaches to a terminal - * or a branched unit. + * Parse Input Terminal Unit */ static int __check_input_term(struct mixer_build *state, int id, - struct usb_audio_term *term) + struct usb_audio_term *term); + +static int parse_term_uac1_iterm_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) { - int protocol = state->mixer->protocol; + struct uac_input_terminal_descriptor *d = p1; + + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le16_to_cpu(d->wChannelConfig); + term->name = d->iTerminal; + return 0; +} + +static int parse_term_uac2_iterm_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + struct uac2_input_terminal_descriptor *d = p1; int err; - void *p1; - unsigned char *hdr; - memset(term, 0, sizeof(*term)); - for (;;) { - /* a loop in the terminal chain? */ - if (test_and_set_bit(id, state->termbitmap)) - return -EINVAL; + /* call recursively to verify the referenced clock entity */ + err = __check_input_term(state, d->bCSourceID, term); + if (err < 0) + return err; - p1 = find_audio_control_unit(state, id); - if (!p1) - break; + /* save input term properties after recursion, + * to ensure they are not overriden by the recursion calls + */ + term->id = id; + term->type = le16_to_cpu(d->wTerminalType); + term->channels = d->bNrChannels; + term->chconfig = le32_to_cpu(d->bmChannelConfig); + term->name = d->iTerminal; + return 0; +} - hdr = p1; - term->id = id; +static int parse_term_uac3_iterm_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + struct uac3_input_terminal_descriptor *d = p1; + int err; - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - switch (hdr[2]) { - case UAC_INPUT_TERMINAL: - if (protocol == UAC_VERSION_1) { - struct uac_input_terminal_descriptor *d = p1; - - term->type = le16_to_cpu(d->wTerminalType); - term->channels = d->bNrChannels; - term->chconfig = le16_to_cpu(d->wChannelConfig); - term->name = d->iTerminal; - } else { /* UAC_VERSION_2 */ - struct uac2_input_terminal_descriptor *d = p1; - - /* call recursively to verify that the - * referenced clock entity is valid */ - err = __check_input_term(state, d->bCSourceID, term); - if (err < 0) - return err; + /* call recursively to verify the referenced clock entity */ + err = __check_input_term(state, d->bCSourceID, term); + if (err < 0) + return err; - /* save input term properties after recursion, - * to ensure they are not overriden by the - * recursion calls */ - term->id = id; - term->type = le16_to_cpu(d->wTerminalType); - term->channels = d->bNrChannels; - term->chconfig = le32_to_cpu(d->bmChannelConfig); - term->name = d->iTerminal; - } - return 0; - case UAC_FEATURE_UNIT: { - /* the header is the same for v1 and v2 */ - struct uac_feature_unit_descriptor *d = p1; + /* save input term properties after recursion, + * to ensure they are not overriden by the recursion calls + */ + term->id = id; + term->type = le16_to_cpu(d->wTerminalType); - id = d->bSourceID; - break; /* continue to parse */ - } - case UAC_MIXER_UNIT: { - struct uac_mixer_unit_descriptor *d = p1; - - term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ - term->channels = uac_mixer_unit_bNrChannels(d); - term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol); - term->name = uac_mixer_unit_iMixer(d); - return 0; - } - case UAC_SELECTOR_UNIT: - case UAC2_CLOCK_SELECTOR: { - struct uac_selector_unit_descriptor *d = p1; - /* call recursively to retrieve the channel info */ - err = __check_input_term(state, d->baSourceID[0], term); - if (err < 0) - return err; - term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ - term->id = id; - term->name = uac_selector_unit_iSelector(d); - return 0; - } - case UAC1_PROCESSING_UNIT: - /* UAC2_EFFECT_UNIT */ - if (protocol == UAC_VERSION_1) - term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ - else /* UAC_VERSION_2 */ - term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ - /* fall through */ - case UAC1_EXTENSION_UNIT: - /* UAC2_PROCESSING_UNIT_V2 */ - if (protocol == UAC_VERSION_1 && !term->type) - term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */ - else if (protocol == UAC_VERSION_2 && !term->type) - term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ - /* fall through */ - case UAC2_EXTENSION_UNIT_V2: { - struct uac_processing_unit_descriptor *d = p1; - - if (protocol == UAC_VERSION_2 && - hdr[2] == UAC2_EFFECT_UNIT) { - /* UAC2/UAC1 unit IDs overlap here in an - * uncompatible way. Ignore this unit for now. - */ - return 0; - } + err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); + if (err < 0) + return err; + term->channels = err; - if (d->bNrInPins) { - id = d->baSourceID[0]; - break; /* continue to parse */ - } - if (!term->type) - term->type = UAC3_EXTENSION_UNIT << 16; /* virtual type */ + /* REVISIT: UAC3 IT doesn't have channels cfg */ + term->chconfig = 0; - term->channels = uac_processing_unit_bNrChannels(d); - term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); - term->name = uac_processing_unit_iProcessing(d, protocol); - return 0; - } - case UAC2_CLOCK_SOURCE: { - struct uac_clock_source_descriptor *d = p1; + term->name = le16_to_cpu(d->wTerminalDescrStr); + return 0; +} - term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ - term->id = id; - term->name = d->iClockSource; - return 0; - } - default: - return -ENODEV; - } - } else { /* UAC_VERSION_3 */ - switch (hdr[2]) { - case UAC_INPUT_TERMINAL: { - struct uac3_input_terminal_descriptor *d = p1; - - /* call recursively to verify that the - * referenced clock entity is valid */ - err = __check_input_term(state, d->bCSourceID, term); - if (err < 0) - return err; +static int parse_term_mixer_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + struct uac_mixer_unit_descriptor *d = p1; + int protocol = state->mixer->protocol; + int err; - /* save input term properties after recursion, - * to ensure they are not overriden by the - * recursion calls */ - term->id = id; - term->type = le16_to_cpu(d->wTerminalType); + err = uac_mixer_unit_get_channels(state, d); + if (err <= 0) + return err; - err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); - if (err < 0) - return err; - term->channels = err; + term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ + term->channels = err; + if (protocol != UAC_VERSION_3) { + term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol); + term->name = uac_mixer_unit_iMixer(d); + } + return 0; +} - /* REVISIT: UAC3 IT doesn't have channels cfg */ - term->chconfig = 0; +static int parse_term_selector_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + struct uac_selector_unit_descriptor *d = p1; + int err; - term->name = le16_to_cpu(d->wTerminalDescrStr); - return 0; - } - case UAC3_FEATURE_UNIT: { - struct uac3_feature_unit_descriptor *d = p1; + /* call recursively to retrieve the channel info */ + err = __check_input_term(state, d->baSourceID[0], term); + if (err < 0) + return err; + term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ + term->id = id; + if (state->mixer->protocol != UAC_VERSION_3) + term->name = uac_selector_unit_iSelector(d); + return 0; +} - id = d->bSourceID; - break; /* continue to parse */ - } - case UAC3_CLOCK_SOURCE: { - struct uac3_clock_source_descriptor *d = p1; +static int parse_term_proc_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id, int vtype) +{ + struct uac_processing_unit_descriptor *d = p1; + int protocol = state->mixer->protocol; + int err; - term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ - term->id = id; - term->name = le16_to_cpu(d->wClockSourceStr); - return 0; - } - case UAC3_MIXER_UNIT: { - struct uac_mixer_unit_descriptor *d = p1; + if (d->bNrInPins) { + /* call recursively to retrieve the channel info */ + err = __check_input_term(state, d->baSourceID[0], term); + if (err < 0) + return err; + } - err = uac_mixer_unit_get_channels(state, d); - if (err <= 0) - return err; + term->type = vtype << 16; /* virtual type */ + term->id = id; - term->channels = err; - term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ + if (protocol == UAC_VERSION_3) + return 0; - return 0; - } - case UAC3_SELECTOR_UNIT: - case UAC3_CLOCK_SELECTOR: { - struct uac_selector_unit_descriptor *d = p1; - /* call recursively to retrieve the channel info */ - err = __check_input_term(state, d->baSourceID[0], term); - if (err < 0) - return err; - term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ - term->id = id; - term->name = 0; /* TODO: UAC3 Class-specific strings */ + if (!term->channels) { + term->channels = uac_processing_unit_bNrChannels(d); + term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); + } + term->name = uac_processing_unit_iProcessing(d, protocol); + return 0; +} - return 0; - } - case UAC3_PROCESSING_UNIT: { - struct uac_processing_unit_descriptor *d = p1; +static int parse_term_effect_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ + term->id = id; + return 0; +} - if (!d->bNrInPins) - return -EINVAL; +static int parse_term_uac2_clock_source(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + struct uac_clock_source_descriptor *d = p1; - /* call recursively to retrieve the channel info */ - err = __check_input_term(state, d->baSourceID[0], term); - if (err < 0) - return err; + term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ + term->id = id; + term->name = d->iClockSource; + return 0; +} - term->type = UAC3_PROCESSING_UNIT << 16; /* virtual type */ - term->id = id; - term->name = 0; /* TODO: UAC3 Class-specific strings */ +static int parse_term_uac3_clock_source(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + struct uac3_clock_source_descriptor *d = p1; - return 0; - } - default: - return -ENODEV; - } + term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ + term->id = id; + term->name = le16_to_cpu(d->wClockSourceStr); + return 0; +} + +#define PTYPE(a, b) ((a) << 8 | (b)) + +/* + * parse the source unit recursively until it reaches to a terminal + * or a branched unit. + */ +static int __check_input_term(struct mixer_build *state, int id, + struct usb_audio_term *term) +{ + int protocol = state->mixer->protocol; + void *p1; + unsigned char *hdr; + + for (;;) { + /* a loop in the terminal chain? */ + if (test_and_set_bit(id, state->termbitmap)) + return -EINVAL; + + p1 = find_audio_control_unit(state, id); + if (!p1) + break; + if (!snd_usb_validate_audio_desc(p1, protocol)) + break; /* bad descriptor */ + + hdr = p1; + term->id = id; + + switch (PTYPE(protocol, hdr[2])) { + case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): + case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): { + /* the header is the same for all versions */ + struct uac_feature_unit_descriptor *d = p1; + + id = d->bSourceID; + break; /* continue to parse */ + } + case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): + return parse_term_uac1_iterm_unit(state, term, p1, id); + case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): + return parse_term_uac2_iterm_unit(state, term, p1, id); + case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): + return parse_term_uac3_iterm_unit(state, term, p1, id); + case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): + case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): + return parse_term_mixer_unit(state, term, p1, id); + case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): + case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): + case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): + return parse_term_selector_unit(state, term, p1, id); + case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): + case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): + return parse_term_proc_unit(state, term, p1, id, + UAC3_PROCESSING_UNIT); + case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): + return parse_term_effect_unit(state, term, p1, id); + case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): + case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): + return parse_term_proc_unit(state, term, p1, id, + UAC3_EXTENSION_UNIT); + case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): + return parse_term_uac2_clock_source(state, term, p1, id); + case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): + return parse_term_uac3_clock_source(state, term, p1, id); + default: + return -ENODEV; } } return -ENODEV; @@ -1015,7 +1028,7 @@ struct usb_feature_control_info { int type_uac2; /* data type for uac2 if different from uac1, else -1 */ }; -static struct usb_feature_control_info audio_feature_info[] = { +static const struct usb_feature_control_info audio_feature_info[] = { { UAC_FU_MUTE, "Mute", USB_MIXER_INV_BOOLEAN, -1 }, { UAC_FU_VOLUME, "Volume", USB_MIXER_S16, -1 }, { UAC_FU_BASS, "Tone Control - Bass", USB_MIXER_S8, -1 }, @@ -1032,10 +1045,15 @@ static struct usb_feature_control_info audio_feature_info[] = { { UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 }, }; +static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval) +{ + kfree(cval); +} + /* private_free callback */ void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) { - kfree(kctl->private_data); + usb_mixer_elem_info_free(kctl->private_data); kctl->private_data = NULL; } @@ -1219,7 +1237,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, if (cval->min + cval->res < cval->max) { int last_valid_res = cval->res; int saved, test, check; - get_cur_mix_raw(cval, minchn, &saved); + if (get_cur_mix_raw(cval, minchn, &saved) < 0) + goto no_res_check; for (;;) { test = saved; if (test < cval->max) @@ -1239,6 +1258,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } +no_res_check: cval->initialized = 1; } @@ -1433,7 +1453,7 @@ error: return 0; } -static struct snd_kcontrol_new usb_feature_unit_ctl = { +static const struct snd_kcontrol_new usb_feature_unit_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later manually */ .info = mixer_ctl_feature_info, @@ -1454,7 +1474,7 @@ static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = { * A control which shows the boolean value from reading a UAC control on * the master channel. */ -static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = { +static const struct snd_kcontrol_new usb_bool_master_control_ctl_ro = { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "", /* will be filled later manually */ .access = SNDRV_CTL_ELEM_ACCESS_READ, @@ -1476,7 +1496,7 @@ static const struct snd_kcontrol_new usb_connector_ctl_ro = { * This symbol is exported in order to allow the mixer quirks to * hook up to the standard feature unit control mechanism */ -struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl; +const struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl; /* * build a feature control @@ -1514,7 +1534,7 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl, strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name)); } -static struct usb_feature_control_info *get_feature_control_info(int control) +static const struct usb_feature_control_info *get_feature_control_info(int control) { int i; @@ -1532,7 +1552,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, struct usb_audio_term *oterm, int unitid, int nameid, int readonly_mask) { - struct usb_feature_control_info *ctl_info; + const struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; struct snd_kcontrol *kctl; @@ -1558,7 +1578,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, ctl_info = get_feature_control_info(control); if (!ctl_info) { - kfree(cval); + usb_mixer_elem_info_free(cval); return; } if (mixer->protocol == UAC_VERSION_1) @@ -1591,7 +1611,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, if (!kctl) { usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); - kfree(cval); + usb_mixer_elem_info_free(cval); return; } kctl->private_free = snd_usb_mixer_elem_free; @@ -1761,7 +1781,7 @@ static void build_connector_control(struct usb_mixer_interface *mixer, kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); if (!kctl) { usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); - kfree(cval); + usb_mixer_elem_info_free(cval); return; } get_connector_control_name(mixer, term, is_input, kctl->id.name, @@ -1782,13 +1802,6 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, if (state->mixer->protocol != UAC_VERSION_2) return -EINVAL; - if (hdr->bLength != sizeof(*hdr)) { - usb_audio_dbg(state->chip, - "Bogus clock source descriptor length of %d, ignoring.\n", - hdr->bLength); - return 0; - } - /* * The only property of this unit we are interested in is the * clock source validity. If that isn't readable, just bail out. @@ -1814,7 +1827,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); if (!kctl) { - kfree(cval); + usb_mixer_elem_info_free(cval); return -ENOMEM; } @@ -1847,62 +1860,20 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, __u8 *bmaControls; if (state->mixer->protocol == UAC_VERSION_1) { - if (hdr->bLength < 7) { - usb_audio_err(state->chip, - "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", - unitid); - return -EINVAL; - } csize = hdr->bControlSize; - if (!csize) { - usb_audio_dbg(state->chip, - "unit %u: invalid bControlSize == 0\n", - unitid); - return -EINVAL; - } channels = (hdr->bLength - 7) / csize - 1; bmaControls = hdr->bmaControls; - if (hdr->bLength < 7 + csize) { - usb_audio_err(state->chip, - "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", - unitid); - return -EINVAL; - } } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_feature_unit_descriptor *ftr = _ftr; - if (hdr->bLength < 6) { - usb_audio_err(state->chip, - "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", - unitid); - return -EINVAL; - } csize = 4; channels = (hdr->bLength - 6) / 4 - 1; bmaControls = ftr->bmaControls; - if (hdr->bLength < 6 + csize) { - usb_audio_err(state->chip, - "unit %u: invalid UAC_FEATURE_UNIT descriptor\n", - unitid); - return -EINVAL; - } } else { /* UAC_VERSION_3 */ struct uac3_feature_unit_descriptor *ftr = _ftr; - if (hdr->bLength < 7) { - usb_audio_err(state->chip, - "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n", - unitid); - return -EINVAL; - } csize = 4; channels = (ftr->bLength - 7) / 4 - 1; bmaControls = ftr->bmaControls; - if (hdr->bLength < 7 + csize) { - usb_audio_err(state->chip, - "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n", - unitid); - return -EINVAL; - } } /* parse the source unit */ @@ -2009,6 +1980,31 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, * Mixer Unit */ +/* check whether the given in/out overflows bmMixerControls matrix */ +static bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc, + int protocol, int num_ins, int num_outs) +{ + u8 *hdr = (u8 *)desc; + u8 *c = uac_mixer_unit_bmControls(desc, protocol); + size_t rest; /* remaining bytes after bmMixerControls */ + + switch (protocol) { + case UAC_VERSION_1: + default: + rest = 1; /* iMixer */ + break; + case UAC_VERSION_2: + rest = 2; /* bmControls + iMixer */ + break; + case UAC_VERSION_3: + rest = 6; /* bmControls + wMixerDescrStr */ + break; + } + + /* overflow? */ + return c + (num_ins * num_outs + 7) / 8 + rest > hdr + hdr[0]; +} + /* * build a mixer unit control * @@ -2051,7 +2047,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (!kctl) { usb_audio_err(state->chip, "cannot malloc kcontrol\n"); - kfree(cval); + usb_mixer_elem_info_free(cval); return; } kctl->private_free = snd_usb_mixer_elem_free; @@ -2077,15 +2073,11 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid, if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_input_terminal_descriptor *d_v2 = raw_desc; - if (d_v2->bLength < sizeof(*d_v2)) - return -EINVAL; control = UAC2_TE_CONNECTOR; term_id = d_v2->bTerminalID; bmctls = le16_to_cpu(d_v2->bmControls); } else if (state->mixer->protocol == UAC_VERSION_3) { struct uac3_input_terminal_descriptor *d_v3 = raw_desc; - if (d_v3->bLength < sizeof(*d_v3)) - return -EINVAL; control = UAC3_TE_INSERTION; term_id = d_v3->bTerminalID; bmctls = le32_to_cpu(d_v3->bmControls); @@ -2137,6 +2129,9 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, if (err < 0) return err; num_ins += iterm.channels; + if (mixer_bitmap_overflow(desc, state->mixer->protocol, + num_ins, num_outs)) + break; for (; ich < num_ins; ich++) { int och, ich_has_controls = 0; @@ -2211,7 +2206,7 @@ static const struct snd_kcontrol_new mixer_procunit_ctl = { */ struct procunit_value_info { int control; - char *suffix; + const char *suffix; int val_type; int min_value; }; @@ -2219,44 +2214,44 @@ struct procunit_value_info { struct procunit_info { int type; char *name; - struct procunit_value_info *values; + const struct procunit_value_info *values; }; -static struct procunit_value_info undefined_proc_info[] = { +static const struct procunit_value_info undefined_proc_info[] = { { 0x00, "Control Undefined", 0 }, { 0 } }; -static struct procunit_value_info updown_proc_info[] = { +static const struct procunit_value_info updown_proc_info[] = { { UAC_UD_ENABLE, "Switch", USB_MIXER_BOOLEAN }, { UAC_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 }, { 0 } }; -static struct procunit_value_info prologic_proc_info[] = { +static const struct procunit_value_info prologic_proc_info[] = { { UAC_DP_ENABLE, "Switch", USB_MIXER_BOOLEAN }, { UAC_DP_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 }, { 0 } }; -static struct procunit_value_info threed_enh_proc_info[] = { +static const struct procunit_value_info threed_enh_proc_info[] = { { UAC_3D_ENABLE, "Switch", USB_MIXER_BOOLEAN }, { UAC_3D_SPACE, "Spaciousness", USB_MIXER_U8 }, { 0 } }; -static struct procunit_value_info reverb_proc_info[] = { +static const struct procunit_value_info reverb_proc_info[] = { { UAC_REVERB_ENABLE, "Switch", USB_MIXER_BOOLEAN }, { UAC_REVERB_LEVEL, "Level", USB_MIXER_U8 }, { UAC_REVERB_TIME, "Time", USB_MIXER_U16 }, { UAC_REVERB_FEEDBACK, "Feedback", USB_MIXER_U8 }, { 0 } }; -static struct procunit_value_info chorus_proc_info[] = { +static const struct procunit_value_info chorus_proc_info[] = { { UAC_CHORUS_ENABLE, "Switch", USB_MIXER_BOOLEAN }, { UAC_CHORUS_LEVEL, "Level", USB_MIXER_U8 }, { UAC_CHORUS_RATE, "Rate", USB_MIXER_U16 }, { UAC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 }, { 0 } }; -static struct procunit_value_info dcr_proc_info[] = { +static const struct procunit_value_info dcr_proc_info[] = { { UAC_DCR_ENABLE, "Switch", USB_MIXER_BOOLEAN }, { UAC_DCR_RATE, "Ratio", USB_MIXER_U16 }, { UAC_DCR_MAXAMPL, "Max Amp", USB_MIXER_S16 }, @@ -2266,7 +2261,7 @@ static struct procunit_value_info dcr_proc_info[] = { { 0 } }; -static struct procunit_info procunits[] = { +static const struct procunit_info procunits[] = { { UAC_PROCESS_UP_DOWNMIX, "Up Down", updown_proc_info }, { UAC_PROCESS_DOLBY_PROLOGIC, "Dolby Prologic", prologic_proc_info }, { UAC_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", threed_enh_proc_info }, @@ -2276,16 +2271,16 @@ static struct procunit_info procunits[] = { { 0 }, }; -static struct procunit_value_info uac3_updown_proc_info[] = { +static const struct procunit_value_info uac3_updown_proc_info[] = { { UAC3_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 }, { 0 } }; -static struct procunit_value_info uac3_stereo_ext_proc_info[] = { +static const struct procunit_value_info uac3_stereo_ext_proc_info[] = { { UAC3_EXT_WIDTH_CONTROL, "Width Control", USB_MIXER_U8 }, { 0 } }; -static struct procunit_info uac3_procunits[] = { +static const struct procunit_info uac3_procunits[] = { { UAC3_PROCESS_UP_DOWNMIX, "Up Down", uac3_updown_proc_info }, { UAC3_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", uac3_stereo_ext_proc_info }, { UAC3_PROCESS_MULTI_FUNCTION, "Multi-Function", undefined_proc_info }, @@ -2295,23 +2290,23 @@ static struct procunit_info uac3_procunits[] = { /* * predefined data for extension units */ -static struct procunit_value_info clock_rate_xu_info[] = { +static const struct procunit_value_info clock_rate_xu_info[] = { { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 }, { 0 } }; -static struct procunit_value_info clock_source_xu_info[] = { +static const struct procunit_value_info clock_source_xu_info[] = { { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN }, { 0 } }; -static struct procunit_value_info spdif_format_xu_info[] = { +static const struct procunit_value_info spdif_format_xu_info[] = { { USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN }, { 0 } }; -static struct procunit_value_info soft_limit_xu_info[] = { +static const struct procunit_value_info soft_limit_xu_info[] = { { USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN }, { 0 } }; -static struct procunit_info extunits[] = { +static const struct procunit_info extunits[] = { { USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info }, { USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info }, { USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info }, @@ -2323,7 +2318,7 @@ static struct procunit_info extunits[] = { * build a processing/extension unit */ static int build_audio_procunit(struct mixer_build *state, int unitid, - void *raw_desc, struct procunit_info *list, + void *raw_desc, const struct procunit_info *list, bool extension_unit) { struct uac_processing_unit_descriptor *desc = raw_desc; @@ -2331,31 +2326,20 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; int i, err, nameid, type, len; - struct procunit_info *info; - struct procunit_value_info *valinfo; + const struct procunit_info *info; + const struct procunit_value_info *valinfo; const struct usbmix_name_map *map; - static struct procunit_value_info default_value_info[] = { + static const struct procunit_value_info default_value_info[] = { { 0x01, "Switch", USB_MIXER_BOOLEAN }, { 0 } }; - static struct procunit_info default_info = { + static const struct procunit_info default_info = { 0, NULL, default_value_info }; const char *name = extension_unit ? "Extension Unit" : "Processing Unit"; - if (desc->bLength < 13) { - usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); - return -EINVAL; - } - num_ins = desc->bNrInPins; - if (desc->bLength < 13 + num_ins || - desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) { - usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); - return -EINVAL; - } - for (i = 0; i < num_ins; i++) { err = parse_audio_unit(state, desc->baSourceID[i]); if (err < 0) @@ -2446,7 +2430,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); if (!kctl) { - kfree(cval); + usb_mixer_elem_info_free(cval); return -ENOMEM; } kctl->private_free = snd_usb_mixer_elem_free; @@ -2584,7 +2568,7 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl) if (kctl->private_data) { struct usb_mixer_elem_info *cval = kctl->private_data; num_ins = cval->max; - kfree(cval); + usb_mixer_elem_info_free(cval); kctl->private_data = NULL; } if (kctl->private_value) { @@ -2610,13 +2594,6 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, const struct usbmix_name_map *map; char **namelist; - if (desc->bLength < 5 || !desc->bNrInPins || - desc->bLength < 5 + desc->bNrInPins) { - usb_audio_err(state->chip, - "invalid SELECTOR UNIT descriptor %d\n", unitid); - return -EINVAL; - } - for (i = 0; i < desc->bNrInPins; i++) { err = parse_audio_unit(state, desc->baSourceID[i]); if (err < 0) @@ -2656,10 +2633,10 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, break; } - namelist = kmalloc_array(desc->bNrInPins, sizeof(char *), GFP_KERNEL); + namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL); if (!namelist) { - kfree(cval); - return -ENOMEM; + err = -ENOMEM; + goto error_cval; } #define MAX_ITEM_NAME_LEN 64 for (i = 0; i < desc->bNrInPins; i++) { @@ -2667,11 +2644,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, len = 0; namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); if (!namelist[i]) { - while (i--) - kfree(namelist[i]); - kfree(namelist); - kfree(cval); - return -ENOMEM; + err = -ENOMEM; + goto error_name; } len = check_mapped_selector_name(state, unitid, i, namelist[i], MAX_ITEM_NAME_LEN); @@ -2685,11 +2659,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); if (! kctl) { usb_audio_err(state->chip, "cannot malloc kcontrol\n"); - for (i = 0; i < desc->bNrInPins; i++) - kfree(namelist[i]); - kfree(namelist); - kfree(cval); - return -ENOMEM; + err = -ENOMEM; + goto error_name; } kctl->private_value = (unsigned long)namelist; kctl->private_free = usb_mixer_selector_elem_free; @@ -2735,6 +2706,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n", cval->head.id, kctl->id.name, desc->bNrInPins); return snd_usb_mixer_add_control(&cval->head, kctl); + + error_name: + for (i = 0; i < desc->bNrInPins; i++) + kfree(namelist[i]); + kfree(namelist); + error_cval: + usb_mixer_elem_info_free(cval); + return err; } /* @@ -2755,62 +2734,49 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) return -EINVAL; } - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - switch (p1[2]) { - case UAC_INPUT_TERMINAL: - return parse_audio_input_terminal(state, unitid, p1); - case UAC_MIXER_UNIT: - return parse_audio_mixer_unit(state, unitid, p1); - case UAC2_CLOCK_SOURCE: - return parse_clock_source_unit(state, unitid, p1); - case UAC_SELECTOR_UNIT: - case UAC2_CLOCK_SELECTOR: - return parse_audio_selector_unit(state, unitid, p1); - case UAC_FEATURE_UNIT: - return parse_audio_feature_unit(state, unitid, p1); - case UAC1_PROCESSING_UNIT: - /* UAC2_EFFECT_UNIT has the same value */ - if (protocol == UAC_VERSION_1) - return parse_audio_processing_unit(state, unitid, p1); - else - return 0; /* FIXME - effect units not implemented yet */ - case UAC1_EXTENSION_UNIT: - /* UAC2_PROCESSING_UNIT_V2 has the same value */ - if (protocol == UAC_VERSION_1) - return parse_audio_extension_unit(state, unitid, p1); - else /* UAC_VERSION_2 */ - return parse_audio_processing_unit(state, unitid, p1); - case UAC2_EXTENSION_UNIT_V2: - return parse_audio_extension_unit(state, unitid, p1); - default: - usb_audio_err(state->chip, - "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); - return -EINVAL; - } - } else { /* UAC_VERSION_3 */ - switch (p1[2]) { - case UAC_INPUT_TERMINAL: - return parse_audio_input_terminal(state, unitid, p1); - case UAC3_MIXER_UNIT: - return parse_audio_mixer_unit(state, unitid, p1); - case UAC3_CLOCK_SOURCE: - return parse_clock_source_unit(state, unitid, p1); - case UAC3_SELECTOR_UNIT: - case UAC3_CLOCK_SELECTOR: - return parse_audio_selector_unit(state, unitid, p1); - case UAC3_FEATURE_UNIT: - return parse_audio_feature_unit(state, unitid, p1); - case UAC3_EFFECT_UNIT: - return 0; /* FIXME - effect units not implemented yet */ - case UAC3_PROCESSING_UNIT: - return parse_audio_processing_unit(state, unitid, p1); - case UAC3_EXTENSION_UNIT: - return parse_audio_extension_unit(state, unitid, p1); - default: - usb_audio_err(state->chip, - "unit %u: unexpected type 0x%02x\n", unitid, p1[2]); - return -EINVAL; - } + if (!snd_usb_validate_audio_desc(p1, protocol)) { + usb_audio_dbg(state->chip, "invalid unit %d\n", unitid); + return 0; /* skip invalid unit */ + } + + switch (PTYPE(protocol, p1[2])) { + case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): + case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): + case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): + return parse_audio_input_terminal(state, unitid, p1); + case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): + case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): + return parse_audio_mixer_unit(state, unitid, p1); + case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): + case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): + return parse_clock_source_unit(state, unitid, p1); + case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): + case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): + case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): + return parse_audio_selector_unit(state, unitid, p1); + case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): + case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): + return parse_audio_feature_unit(state, unitid, p1); + case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): + case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): + return parse_audio_processing_unit(state, unitid, p1); + case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): + case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): + case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): + return parse_audio_extension_unit(state, unitid, p1); + case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): + case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): + return 0; /* FIXME - effect units not implemented yet */ + default: + usb_audio_err(state->chip, + "unit %u: unexpected type 0x%02x\n", + unitid, p1[2]); + return -EINVAL; } } @@ -2845,7 +2811,7 @@ struct uac3_badd_profile { int st_chmask; /* side tone mixing channel mask */ }; -static struct uac3_badd_profile uac3_badd_profiles[] = { +static const struct uac3_badd_profile uac3_badd_profiles[] = { { /* * BAIF, BAOF or combination of both @@ -2906,7 +2872,7 @@ static struct uac3_badd_profile uac3_badd_profiles[] = { }; static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer, - struct uac3_badd_profile *f, + const struct uac3_badd_profile *f, int c_chmask, int p_chmask) { /* @@ -2950,7 +2916,7 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, struct usb_device *dev = mixer->chip->dev; struct usb_interface_assoc_descriptor *assoc; int badd_profile = mixer->chip->badd_profile; - struct uac3_badd_profile *f; + const struct uac3_badd_profile *f; const struct usbmix_ctl_map *map; int p_chmask = 0, c_chmask = 0, st_chmask = 0; int i; @@ -2972,6 +2938,9 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, continue; iface = usb_ifnum_to_if(dev, intf); + if (!iface) + continue; + num = iface->num_altsetting; if (num < 2) @@ -3125,11 +3094,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, mixer->hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) { + if (!snd_usb_validate_audio_desc(p, mixer->protocol)) + continue; /* skip invalid descriptor */ + if (mixer->protocol == UAC_VERSION_1) { struct uac1_output_terminal_descriptor *desc = p; - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ /* mark terminal ID as visited */ set_bit(desc->bTerminalID, state.unitbitmap); state.oterm.id = desc->bTerminalID; @@ -3141,8 +3111,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) } else if (mixer->protocol == UAC_VERSION_2) { struct uac2_output_terminal_descriptor *desc = p; - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ /* mark terminal ID as visited */ set_bit(desc->bTerminalID, state.unitbitmap); state.oterm.id = desc->bTerminalID; @@ -3168,8 +3136,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) } else { /* UAC_VERSION_3 */ struct uac3_output_terminal_descriptor *desc = p; - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ /* mark terminal ID as visited */ set_bit(desc->bTerminalID, state.unitbitmap); state.oterm.id = desc->bTerminalID; @@ -3216,7 +3182,7 @@ static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, struct usb_mixer_elem_list *list) { struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); - static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", + static const char * const val_types[] = {"BOOLEAN", "INV_BOOLEAN", "S8", "U8", "S16", "U16"}; snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " "channels=%i, type=\"%s\"\n", cval->head.id, @@ -3449,7 +3415,7 @@ static int create_keep_iface_ctl(struct usb_mixer_interface *mixer) int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error) { - static struct snd_device_ops dev_ops = { + static const struct snd_device_ops dev_ops = { .dev_free = snd_usb_mixer_dev_free }; struct usb_mixer_interface *mixer; @@ -3530,6 +3496,8 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) usb_kill_urb(mixer->urb); if (mixer->rc_urb) usb_kill_urb(mixer->rc_urb); + if (mixer->private_free) + mixer->private_free(mixer); mixer->disconnected = true; } @@ -3557,6 +3525,8 @@ static int snd_usb_mixer_activate(struct usb_mixer_interface *mixer) int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) { snd_usb_mixer_inactivate(mixer); + if (mixer->private_suspend) + mixer->private_suspend(mixer); return 0; } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 394cd9107507..65d6d08c96f5 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -28,6 +28,10 @@ struct usb_mixer_interface { struct media_mixer_ctl *media_mixer_ctl; bool disconnected; + + void *private_data; + void (*private_free)(struct usb_mixer_interface *mixer); + void (*private_suspend)(struct usb_mixer_interface *mixer); }; #define MAX_CHANNELS 16 /* max logical channels */ @@ -112,6 +116,6 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, extern void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl); -extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; +extern const struct snd_kcontrol_new *snd_usb_feature_unit_ctl; #endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 73baf398c84a..5ebca8013840 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -14,7 +14,7 @@ struct usbmix_name_map { int id; const char *name; int control; - struct usbmix_dB_map *dB; + const struct usbmix_dB_map *dB; }; struct usbmix_selector_map { @@ -52,7 +52,7 @@ Mic-IN[9] --+->FU[10]----------------------------+ | ++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13] */ -static struct usbmix_name_map extigy_map[] = { +static const struct usbmix_name_map extigy_map[] = { /* 1: IT pcm */ { 2, "PCM Playback" }, /* FU */ /* 3: IT pcm */ @@ -93,12 +93,12 @@ static struct usbmix_name_map extigy_map[] = { * e.g. no Master and fake PCM volume * Pavel Mihaylov <bin@bash.info> */ -static struct usbmix_dB_map mp3plus_dB_1 = {.min = -4781, .max = 0}; +static const struct usbmix_dB_map mp3plus_dB_1 = {.min = -4781, .max = 0}; /* just guess */ -static struct usbmix_dB_map mp3plus_dB_2 = {.min = -1781, .max = 618}; +static const struct usbmix_dB_map mp3plus_dB_2 = {.min = -1781, .max = 618}; /* just guess */ -static struct usbmix_name_map mp3plus_map[] = { +static const struct usbmix_name_map mp3plus_map[] = { /* 1: IT pcm */ /* 2: IT mic */ /* 3: IT line */ @@ -139,7 +139,7 @@ Lin_IN[7]-+--->FU[8]---+ +->EU[23]->FU[28]------------->Spk_OUT[19] | ^ +->FU[13]--------------------------------------+ */ -static struct usbmix_name_map audigy2nx_map[] = { +static const struct usbmix_name_map audigy2nx_map[] = { /* 1: IT pcm playback */ /* 4: IT digital in */ { 6, "Digital In Playback" }, /* FU */ @@ -167,12 +167,12 @@ static struct usbmix_name_map audigy2nx_map[] = { { 0 } /* terminator */ }; -static struct usbmix_name_map mbox1_map[] = { +static const struct usbmix_name_map mbox1_map[] = { { 1, "Clock" }, { 0 } /* terminator */ }; -static struct usbmix_selector_map c400_selectors[] = { +static const struct usbmix_selector_map c400_selectors[] = { { .id = 0x80, .count = 2, @@ -181,7 +181,7 @@ static struct usbmix_selector_map c400_selectors[] = { { 0 } /* terminator */ }; -static struct usbmix_selector_map audigy2nx_selectors[] = { +static const struct usbmix_selector_map audigy2nx_selectors[] = { { .id = 14, /* Capture Source */ .count = 3, @@ -201,21 +201,21 @@ static struct usbmix_selector_map audigy2nx_selectors[] = { }; /* Creative SoundBlaster Live! 24-bit External */ -static struct usbmix_name_map live24ext_map[] = { +static const struct usbmix_name_map live24ext_map[] = { /* 2: PCM Playback Volume */ { 5, "Mic Capture" }, /* FU, default PCM Capture Volume */ { 0 } /* terminator */ }; /* LineX FM Transmitter entry - needed to bypass controls bug */ -static struct usbmix_name_map linex_map[] = { +static const struct usbmix_name_map linex_map[] = { /* 1: IT pcm */ /* 2: OT Speaker */ { 3, "Master" }, /* FU: master volume - left / right / mute */ { 0 } /* terminator */ }; -static struct usbmix_name_map maya44_map[] = { +static const struct usbmix_name_map maya44_map[] = { /* 1: IT line */ { 2, "Line Playback" }, /* FU */ /* 3: IT line */ @@ -238,7 +238,7 @@ static struct usbmix_name_map maya44_map[] = { * so this map removes all unwanted sliders from alsamixer */ -static struct usbmix_name_map justlink_map[] = { +static const struct usbmix_name_map justlink_map[] = { /* 1: IT pcm playback */ /* 2: Not present */ { 3, NULL}, /* IT mic (No mic input on device) */ @@ -255,7 +255,7 @@ static struct usbmix_name_map justlink_map[] = { }; /* TerraTec Aureon 5.1 MkII USB */ -static struct usbmix_name_map aureon_51_2_map[] = { +static const struct usbmix_name_map aureon_51_2_map[] = { /* 1: IT USB */ /* 2: IT Mic */ /* 3: IT Line */ @@ -274,7 +274,7 @@ static struct usbmix_name_map aureon_51_2_map[] = { {} /* terminator */ }; -static struct usbmix_name_map scratch_live_map[] = { +static const struct usbmix_name_map scratch_live_map[] = { /* 1: IT Line 1 (USB streaming) */ /* 2: OT Line 1 (Speaker) */ /* 3: IT Line 1 (Line connector) */ @@ -290,7 +290,7 @@ static struct usbmix_name_map scratch_live_map[] = { { 0 } /* terminator */ }; -static struct usbmix_name_map ebox44_map[] = { +static const struct usbmix_name_map ebox44_map[] = { { 4, NULL }, /* FU */ { 6, NULL }, /* MU */ { 7, NULL }, /* FU */ @@ -305,7 +305,7 @@ static struct usbmix_name_map ebox44_map[] = { * FIXME: or mp3plus_map should use "Capture Source" too, * so this maps can be merget */ -static struct usbmix_name_map hercules_usb51_map[] = { +static const struct usbmix_name_map hercules_usb51_map[] = { { 8, "Capture Source" }, /* SU, default "PCM Capture Source" */ { 9, "Master Playback" }, /* FU, default "Speaker Playback" */ { 10, "Mic Boost", 7 }, /* FU, default "Auto Gain Input" */ @@ -316,7 +316,7 @@ static struct usbmix_name_map hercules_usb51_map[] = { }; /* Plantronics Gamecom 780 has a broken volume control, better to disable it */ -static struct usbmix_name_map gamecom780_map[] = { +static const struct usbmix_name_map gamecom780_map[] = { { 9, NULL }, /* FU, speaker out */ {} }; @@ -330,8 +330,8 @@ static const struct usbmix_name_map scms_usb3318_map[] = { }; /* Bose companion 5, the dB conversion factor is 16 instead of 256 */ -static struct usbmix_dB_map bose_companion5_dB = {-5006, -6}; -static struct usbmix_name_map bose_companion5_map[] = { +static const struct usbmix_dB_map bose_companion5_dB = {-5006, -6}; +static const struct usbmix_name_map bose_companion5_map[] = { { 3, NULL, .dB = &bose_companion5_dB }, { 0 } /* terminator */ }; @@ -350,10 +350,20 @@ static const struct usbmix_name_map dell_alc4020_map[] = { }; /* + * Corsair Virtuoso calls everything "Headset" without this, leading to + * applications moving the sidetone control instead of the main one. + */ +static const struct usbmix_name_map corsair_virtuoso_map[] = { + { 3, "Mic Capture" }, + { 6, "Sidetone Playback" }, + { 0 } +}; + +/* * Control map entries */ -static struct usbmix_ctl_map usbmix_ctl_maps[] = { +static const struct usbmix_ctl_map usbmix_ctl_maps[] = { { .id = USB_ID(0x041e, 0x3000), .map = extigy_map, @@ -468,6 +478,16 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x05a7, 0x1020), .map = bose_companion5_map, }, + { + /* Corsair Virtuoso (wired mode) */ + .id = USB_ID(0x1b1c, 0x0a41), + .map = corsair_virtuoso_map, + }, + { + /* Corsair Virtuoso (wireless mode) */ + .id = USB_ID(0x1b1c, 0x0a42), + .map = corsair_virtuoso_map, + }, { 0 } /* terminator */ }; @@ -475,37 +495,37 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { * Control map entries for UAC3 BADD profiles */ -static struct usbmix_name_map uac3_badd_generic_io_map[] = { +static const struct usbmix_name_map uac3_badd_generic_io_map[] = { { UAC3_BADD_FU_ID2, "Generic Out Playback" }, { UAC3_BADD_FU_ID5, "Generic In Capture" }, { 0 } /* terminator */ }; -static struct usbmix_name_map uac3_badd_headphone_map[] = { +static const struct usbmix_name_map uac3_badd_headphone_map[] = { { UAC3_BADD_FU_ID2, "Headphone Playback" }, { 0 } /* terminator */ }; -static struct usbmix_name_map uac3_badd_speaker_map[] = { +static const struct usbmix_name_map uac3_badd_speaker_map[] = { { UAC3_BADD_FU_ID2, "Speaker Playback" }, { 0 } /* terminator */ }; -static struct usbmix_name_map uac3_badd_microphone_map[] = { +static const struct usbmix_name_map uac3_badd_microphone_map[] = { { UAC3_BADD_FU_ID5, "Mic Capture" }, { 0 } /* terminator */ }; /* Covers also 'headset adapter' profile */ -static struct usbmix_name_map uac3_badd_headset_map[] = { +static const struct usbmix_name_map uac3_badd_headset_map[] = { { UAC3_BADD_FU_ID2, "Headset Playback" }, { UAC3_BADD_FU_ID5, "Headset Capture" }, { UAC3_BADD_FU_ID7, "Sidetone Mixing" }, { 0 } /* terminator */ }; -static struct usbmix_name_map uac3_badd_speakerphone_map[] = { +static const struct usbmix_name_map uac3_badd_speakerphone_map[] = { { UAC3_BADD_FU_ID2, "Speaker Playback" }, { UAC3_BADD_FU_ID5, "Mic Capture" }, { 0 } /* terminator */ }; -static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = { +static const struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = { { .id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, .map = uac3_badd_generic_io_map, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 199fa157a411..c237e24f08d9 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -32,6 +32,7 @@ #include "mixer.h" #include "mixer_quirks.h" #include "mixer_scarlett.h" +#include "mixer_scarlett_gen2.h" #include "mixer_us16x08.h" #include "helper.h" @@ -118,7 +119,7 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, * Create a set of standard UAC controls from a table */ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, - struct std_mono_table *t) + const struct std_mono_table *t) { int err; @@ -506,7 +507,7 @@ static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list) list->kctl->private_value); } -static struct snd_kcontrol_new snd_emu0204_control = { +static const struct snd_kcontrol_new snd_emu0204_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Front Jack Channels", .info = snd_emu0204_ch_switch_info, @@ -574,7 +575,7 @@ static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list) list->kctl->private_value); } -static struct snd_kcontrol_new snd_xonar_u1_output_switch = { +static const struct snd_kcontrol_new snd_xonar_u1_output_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Playback Switch", .info = snd_ctl_boolean_mono_info, @@ -701,7 +702,7 @@ static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list) return snd_mbox1_switch_update(list->mixer, list->kctl->private_value); } -static struct snd_kcontrol_new snd_mbox1_switch = { +static const struct snd_kcontrol_new snd_mbox1_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Clock Source", .index = 0, @@ -786,7 +787,7 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, return err < 0 ? err : 1; } -static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { +static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { { .name = "Direct Thru Channel A", .private_value = _MAKE_NI_CONTROL(0x01, 0x03), @@ -805,7 +806,7 @@ static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { }, }; -static struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = { +static const struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = { { .name = "Direct Thru Channel A", .private_value = _MAKE_NI_CONTROL(0x01, 0x03), @@ -1155,17 +1156,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, { struct usb_mixer_interface *mixer; struct usb_mixer_elem_info *cval; - int unitid = 12; /* SamleRate ExtensionUnit ID */ + int unitid = 12; /* SampleRate ExtensionUnit ID */ list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); - if (cval) { + if (mixer->id_elems[unitid]) { + cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, cval->control << 8, samplerate_id); snd_usb_mixer_notify_id(mixer, unitid); + break; } - break; } } @@ -1385,7 +1386,7 @@ static int snd_c400_create_mixer(struct usb_mixer_interface *mixer) * are valid they presents mono controls as L and R channels of * stereo. So we provide a good mixer here. */ -static struct std_mono_table ebox44_table[] = { +static const struct std_mono_table ebox44_table[] = { { .unitid = 4, .control = 1, @@ -1661,7 +1662,7 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, return err < 0 ? err : 1; } -static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { +static const struct snd_kcontrol_new snd_microii_mixer_spdif[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), @@ -1690,7 +1691,7 @@ static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { static int snd_microii_controls_create(struct usb_mixer_interface *mixer) { int err, i; - static usb_mixer_elem_resume_func_t resume_funcs[] = { + static const usb_mixer_elem_resume_func_t resume_funcs[] = { snd_microii_spdif_default_update, NULL, snd_microii_spdif_switch_update @@ -1768,7 +1769,7 @@ static int snd_soundblaster_e1_switch_info(struct snd_kcontrol *kcontrol, return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } -static struct snd_kcontrol_new snd_soundblaster_e1_input_switch = { +static const struct snd_kcontrol_new snd_soundblaster_e1_input_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", .info = snd_soundblaster_e1_switch_info, @@ -2092,7 +2093,7 @@ static int snd_rme_sync_source_info(struct snd_kcontrol *kcontrol, ARRAY_SIZE(sync_sources), sync_sources); } -static struct snd_kcontrol_new snd_rme_controls[] = { +static const struct snd_kcontrol_new snd_rme_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "AES Rate", @@ -2258,6 +2259,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_scarlett_controls_create(mixer); break; + case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */ + case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */ + case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */ + err = snd_scarlett_gen2_controls_create(mixer); + break; + case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */ err = snd_soundblaster_e1_switch_create(mixer); break; diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 83715fd8dfd6..49fcd2505443 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -142,6 +142,7 @@ enum { SCARLETT_OUTPUTS, SCARLETT_SWITCH_IMPEDANCE, SCARLETT_SWITCH_PAD, + SCARLETT_SWITCH_GAIN, }; enum { @@ -192,6 +193,15 @@ static const struct scarlett_mixer_elem_enum_info opt_pad = { } }; +static const struct scarlett_mixer_elem_enum_info opt_gain = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "Lo", "Hi" + } +}; + static const struct scarlett_mixer_elem_enum_info opt_impedance = { .start = 0, .len = 2, @@ -623,7 +633,7 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /********************** device-specific config *************************/ /* untested... */ -static struct scarlett_device_info s6i6_info = { +static const struct scarlett_device_info s6i6_info = { .matrix_in = 18, .matrix_out = 8, .input_len = 6, @@ -652,8 +662,8 @@ static struct scarlett_device_info s6i6_info = { { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, - { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, - { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_GAIN, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_GAIN, .name = NULL}, }, .matrix_mux_init = { @@ -665,7 +675,7 @@ static struct scarlett_device_info s6i6_info = { }; /* untested... */ -static struct scarlett_device_info s8i6_info = { +static const struct scarlett_device_info s8i6_info = { .matrix_in = 18, .matrix_out = 6, .input_len = 8, @@ -704,7 +714,7 @@ static struct scarlett_device_info s8i6_info = { } }; -static struct scarlett_device_info s18i6_info = { +static const struct scarlett_device_info s18i6_info = { .matrix_in = 18, .matrix_out = 6, .input_len = 18, @@ -741,7 +751,7 @@ static struct scarlett_device_info s18i6_info = { } }; -static struct scarlett_device_info s18i8_info = { +static const struct scarlett_device_info s18i8_info = { .matrix_in = 18, .matrix_out = 8, .input_len = 18, @@ -783,7 +793,7 @@ static struct scarlett_device_info s18i8_info = { } }; -static struct scarlett_device_info s18i20_info = { +static const struct scarlett_device_info s18i20_info = { .matrix_in = 18, .matrix_out = 8, .input_len = 18, @@ -833,7 +843,7 @@ static struct scarlett_device_info s18i20_info = { static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, - struct scarlett_device_info *info) + const struct scarlett_device_info *info) { int i, err; char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; @@ -883,6 +893,15 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, if (err < 0) return err; break; + case SCARLETT_SWITCH_GAIN: + sprintf(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, + &opt_gain, &elem); + if (err < 0) + return err; + break; } } @@ -896,7 +915,7 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) { int err, i, o; char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - struct scarlett_device_info *info; + const struct scarlett_device_info *info; struct usb_mixer_elem_info *elem; static char sample_rate_buffer[4] = { '\x80', '\xbb', '\x00', '\x00' }; diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c new file mode 100644 index 000000000000..74c00c905d24 --- /dev/null +++ b/sound/usb/mixer_scarlett_gen2.c @@ -0,0 +1,2075 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Focusrite Scarlett 6i6/18i8/18i20 Gen 2 Driver for ALSA + * + * Copyright (c) 2018-2019 by Geoffrey D. Bennett <g at b4.vu> + * + * Based on the Scarlett (Gen 1) Driver for ALSA: + * + * Copyright (c) 2013 by Tobias Hoffmann + * Copyright (c) 2013 by Robin Gareus <robin at gareus.org> + * Copyright (c) 2002 by Takashi Iwai <tiwai at suse.de> + * Copyright (c) 2014 by Chris J Arges <chris.j.arges at canonical.com> + * + * Many codes borrowed from audio.c by + * Alan Cox (alan at lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer at ife.ee.ethz.ch) + * + * Code cleanup: + * David Henningsson <david.henningsson at canonical.com> + */ + +/* Mixer Interface for the Focusrite Scarlett 6i6/18i8/18i20 Gen 2 audio + * interface. Based on the Gen 1 driver and rewritten. + */ + +/* The protocol was reverse engineered by looking at the communication + * between Focusrite Control 2.3.4 and the Focusrite(R) Scarlett 18i20 + * (firmware 1083) using usbmon in July-August 2018. + * + * Scarlett 18i8 support added in April 2019. + * + * Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann + * for providing usbmon output and testing). + * + * This ALSA mixer gives access to: + * - input, output, mixer-matrix muxes + * - 18x10 mixer-matrix gain stages + * - gain/volume controls + * - level meters + * - line/inst level and pad controls + * + * <ditaa> + * /--------------\ 18chn 20chn /--------------\ + * | Hardware in +--+------\ /-------------+--+ ALSA PCM out | + * \--------------/ | | | | \--------------/ + * | | | /-----\ | + * | | | | | | + * | v v v | | + * | +---------------+ | | + * | \ Matrix Mux / | | + * | +-----+-----+ | | + * | | | | + * | |18chn | | + * | | | | + * | | 10chn| | + * | v | | + * | +------------+ | | + * | | Mixer | | | + * | | Matrix | | | + * | | | | | + * | | 18x10 Gain | | | + * | | stages | | | + * | +-----+------+ | | + * | | | | + * |18chn |10chn | |20chn + * | | | | + * | +----------/ | + * | | | + * v v v + * =========================== + * +---------------+ +--—------------+ + * \ Output Mux / \ Capture Mux / + * +---+---+---+ +-----+-----+ + * | | | + * 10chn| | |18chn + * | | | + * /--------------\ | | | /--------------\ + * | S/PDIF, ADAT |<--/ |10chn \-->| ALSA PCM in | + * | Hardware out | | \--------------/ + * \--------------/ | + * v + * +-------------+ Software gain per channel. + * | Master Gain |<-- 18i20 only: Switch per channel + * +------+------+ to select HW or SW gain control. + * | + * |10chn + * /--------------\ | + * | Analogue |<------/ + * | Hardware out | + * \--------------/ + * </ditaa> + * + */ + +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/moduleparam.h> + +#include <sound/control.h> +#include <sound/tlv.h> + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" + +#include "mixer_scarlett_gen2.h" + +/* device_setup value to enable */ +#define SCARLETT2_ENABLE 0x01 + +/* some gui mixers can't handle negative ctl values */ +#define SCARLETT2_VOLUME_BIAS 127 + +/* mixer range from -80dB to +6dB in 0.5dB steps */ +#define SCARLETT2_MIXER_MIN_DB -80 +#define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2) +#define SCARLETT2_MIXER_MAX_DB 6 +#define SCARLETT2_MIXER_MAX_VALUE \ + ((SCARLETT2_MIXER_MAX_DB - SCARLETT2_MIXER_MIN_DB) * 2) + +/* map from (dB + 80) * 2 to mixer value + * for dB in 0 .. 172: int(8192 * pow(10, ((dB - 160) / 2 / 20))) + */ +static const u16 scarlett2_mixer_values[173] = { + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 23, 24, 25, 27, 29, 30, 32, 34, 36, 38, 41, 43, 46, 48, 51, + 54, 57, 61, 65, 68, 73, 77, 81, 86, 91, 97, 103, 109, 115, + 122, 129, 137, 145, 154, 163, 173, 183, 194, 205, 217, 230, + 244, 259, 274, 290, 307, 326, 345, 365, 387, 410, 434, 460, + 487, 516, 547, 579, 614, 650, 689, 730, 773, 819, 867, 919, + 973, 1031, 1092, 1157, 1225, 1298, 1375, 1456, 1543, 1634, + 1731, 1833, 1942, 2057, 2179, 2308, 2445, 2590, 2744, 2906, + 3078, 3261, 3454, 3659, 3876, 4105, 4349, 4606, 4879, 5168, + 5475, 5799, 6143, 6507, 6892, 7301, 7733, 8192, 8677, 9191, + 9736, 10313, 10924, 11571, 12257, 12983, 13752, 14567, 15430, + 16345 +}; + +/* Maximum number of analogue outputs */ +#define SCARLETT2_ANALOGUE_MAX 10 + +/* Maximum number of level and pad switches */ +#define SCARLETT2_LEVEL_SWITCH_MAX 2 +#define SCARLETT2_PAD_SWITCH_MAX 4 + +/* Maximum number of inputs to the mixer */ +#define SCARLETT2_INPUT_MIX_MAX 18 + +/* Maximum number of outputs from the mixer */ +#define SCARLETT2_OUTPUT_MIX_MAX 10 + +/* Maximum size of the data in the USB mux assignment message: + * 18 inputs, 20 outputs, 18 matrix inputs, 8 spare + */ +#define SCARLETT2_MUX_MAX 64 + +/* Number of meters: + * 18 inputs, 20 outputs, 18 matrix inputs + */ +#define SCARLETT2_NUM_METERS 56 + +/* Hardware port types: + * - None (no input to mux) + * - Analogue I/O + * - S/PDIF I/O + * - ADAT I/O + * - Mixer I/O + * - PCM I/O + */ +enum { + SCARLETT2_PORT_TYPE_NONE = 0, + SCARLETT2_PORT_TYPE_ANALOGUE = 1, + SCARLETT2_PORT_TYPE_SPDIF = 2, + SCARLETT2_PORT_TYPE_ADAT = 3, + SCARLETT2_PORT_TYPE_MIX = 4, + SCARLETT2_PORT_TYPE_PCM = 5, + SCARLETT2_PORT_TYPE_COUNT = 6, +}; + +/* Count of total I/O and number available at each sample rate */ +enum { + SCARLETT2_PORT_IN = 0, + SCARLETT2_PORT_OUT = 1, + SCARLETT2_PORT_OUT_44 = 2, + SCARLETT2_PORT_OUT_88 = 3, + SCARLETT2_PORT_OUT_176 = 4, + SCARLETT2_PORT_DIRECTIONS = 5, +}; + +/* Hardware buttons on the 18i20 */ +#define SCARLETT2_BUTTON_MAX 2 + +static const char *const scarlett2_button_names[SCARLETT2_BUTTON_MAX] = { + "Mute", "Dim" +}; + +/* Description of each hardware port type: + * - id: hardware ID for this port type + * - num: number of sources/destinations of this port type + * - src_descr: printf format string for mux input selections + * - src_num_offset: added to channel number for the fprintf + * - dst_descr: printf format string for mixer controls + */ +struct scarlett2_ports { + u16 id; + int num[SCARLETT2_PORT_DIRECTIONS]; + const char * const src_descr; + int src_num_offset; + const char * const dst_descr; +}; + +struct scarlett2_device_info { + u8 line_out_hw_vol; /* line out hw volume is sw controlled */ + u8 button_count; /* number of buttons */ + u8 level_input_count; /* inputs with level selectable */ + u8 pad_input_count; /* inputs with pad selectable */ + const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX]; + struct scarlett2_ports ports[SCARLETT2_PORT_TYPE_COUNT]; +}; + +struct scarlett2_mixer_data { + struct usb_mixer_interface *mixer; + struct mutex usb_mutex; /* prevent sending concurrent USB requests */ + struct mutex data_mutex; /* lock access to this data */ + struct delayed_work work; + const struct scarlett2_device_info *info; + int num_mux_srcs; + u16 scarlett2_seq; + u8 vol_updated; + u8 master_vol; + u8 vol[SCARLETT2_ANALOGUE_MAX]; + u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; + u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; + u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; + u8 buttons[SCARLETT2_BUTTON_MAX]; + struct snd_kcontrol *master_vol_ctl; + struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; + struct snd_kcontrol *button_ctls[SCARLETT2_BUTTON_MAX]; + u8 mux[SCARLETT2_MUX_MAX]; + u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; +}; + +/*** Model-specific data ***/ + +static const struct scarlett2_device_info s6i6_gen2_info = { + /* The first two analogue inputs can be switched between line + * and instrument levels. + */ + .level_input_count = 2, + + /* The first two analogue inputs have an optional pad. */ + .pad_input_count = 2, + + .line_out_descrs = { + "Monitor L", + "Monitor R", + "Headphones L", + "Headphones R", + }, + + .ports = { + [SCARLETT2_PORT_TYPE_NONE] = { + .id = 0x000, + .num = { 1, 0, 8, 8, 8 }, + .src_descr = "Off", + .src_num_offset = 0, + }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { + .id = 0x080, + .num = { 4, 4, 4, 4, 4 }, + .src_descr = "Analogue %d", + .src_num_offset = 1, + .dst_descr = "Analogue Output %02d Playback" + }, + [SCARLETT2_PORT_TYPE_SPDIF] = { + .id = 0x180, + .num = { 2, 2, 2, 2, 2 }, + .src_descr = "S/PDIF %d", + .src_num_offset = 1, + .dst_descr = "S/PDIF Output %d Playback" + }, + [SCARLETT2_PORT_TYPE_MIX] = { + .id = 0x300, + .num = { 10, 18, 18, 18, 18 }, + .src_descr = "Mix %c", + .src_num_offset = 65, + .dst_descr = "Mixer Input %02d Capture" + }, + [SCARLETT2_PORT_TYPE_PCM] = { + .id = 0x600, + .num = { 6, 6, 6, 6, 6 }, + .src_descr = "PCM %d", + .src_num_offset = 1, + .dst_descr = "PCM %02d Capture" + }, + }, +}; + +static const struct scarlett2_device_info s18i8_gen2_info = { + /* The first two analogue inputs can be switched between line + * and instrument levels. + */ + .level_input_count = 2, + + /* The first four analogue inputs have an optional pad. */ + .pad_input_count = 4, + + .line_out_descrs = { + "Monitor L", + "Monitor R", + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", + }, + + .ports = { + [SCARLETT2_PORT_TYPE_NONE] = { + .id = 0x000, + .num = { 1, 0, 8, 8, 4 }, + .src_descr = "Off", + .src_num_offset = 0, + }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { + .id = 0x080, + .num = { 8, 6, 6, 6, 6 }, + .src_descr = "Analogue %d", + .src_num_offset = 1, + .dst_descr = "Analogue Output %02d Playback" + }, + [SCARLETT2_PORT_TYPE_SPDIF] = { + .id = 0x180, + /* S/PDIF outputs aren't available at 192KHz + * but are included in the USB mux I/O + * assignment message anyway + */ + .num = { 2, 2, 2, 2, 2 }, + .src_descr = "S/PDIF %d", + .src_num_offset = 1, + .dst_descr = "S/PDIF Output %d Playback" + }, + [SCARLETT2_PORT_TYPE_ADAT] = { + .id = 0x200, + .num = { 8, 0, 0, 0, 0 }, + .src_descr = "ADAT %d", + .src_num_offset = 1, + }, + [SCARLETT2_PORT_TYPE_MIX] = { + .id = 0x300, + .num = { 10, 18, 18, 18, 18 }, + .src_descr = "Mix %c", + .src_num_offset = 65, + .dst_descr = "Mixer Input %02d Capture" + }, + [SCARLETT2_PORT_TYPE_PCM] = { + .id = 0x600, + .num = { 20, 18, 18, 14, 10 }, + .src_descr = "PCM %d", + .src_num_offset = 1, + .dst_descr = "PCM %02d Capture" + }, + }, +}; + +static const struct scarlett2_device_info s18i20_gen2_info = { + /* The analogue line outputs on the 18i20 can be switched + * between software and hardware volume control + */ + .line_out_hw_vol = 1, + + /* Mute and dim buttons */ + .button_count = 2, + + .line_out_descrs = { + "Monitor L", + "Monitor R", + NULL, + NULL, + NULL, + NULL, + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", + }, + + .ports = { + [SCARLETT2_PORT_TYPE_NONE] = { + .id = 0x000, + .num = { 1, 0, 8, 8, 6 }, + .src_descr = "Off", + .src_num_offset = 0, + }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { + .id = 0x080, + .num = { 8, 10, 10, 10, 10 }, + .src_descr = "Analogue %d", + .src_num_offset = 1, + .dst_descr = "Analogue Output %02d Playback" + }, + [SCARLETT2_PORT_TYPE_SPDIF] = { + /* S/PDIF outputs aren't available at 192KHz + * but are included in the USB mux I/O + * assignment message anyway + */ + .id = 0x180, + .num = { 2, 2, 2, 2, 2 }, + .src_descr = "S/PDIF %d", + .src_num_offset = 1, + .dst_descr = "S/PDIF Output %d Playback" + }, + [SCARLETT2_PORT_TYPE_ADAT] = { + .id = 0x200, + .num = { 8, 8, 8, 4, 0 }, + .src_descr = "ADAT %d", + .src_num_offset = 1, + .dst_descr = "ADAT Output %d Playback" + }, + [SCARLETT2_PORT_TYPE_MIX] = { + .id = 0x300, + .num = { 10, 18, 18, 18, 18 }, + .src_descr = "Mix %c", + .src_num_offset = 65, + .dst_descr = "Mixer Input %02d Capture" + }, + [SCARLETT2_PORT_TYPE_PCM] = { + .id = 0x600, + .num = { 20, 18, 18, 14, 10 }, + .src_descr = "PCM %d", + .src_num_offset = 1, + .dst_descr = "PCM %02d Capture" + }, + }, +}; + +/* get the starting port index number for a given port type/direction */ +static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports, + int direction, int port_type) +{ + int i, num = 0; + + for (i = 0; i < port_type; i++) + num += ports[i].num[direction]; + + return num; +} + +/*** USB Interactions ***/ + +/* Vendor-Specific Interface, Endpoint, MaxPacketSize, Interval */ +#define SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE 5 +#define SCARLETT2_USB_INTERRUPT_ENDPOINT 4 +#define SCARLETT2_USB_INTERRUPT_MAX_DATA 64 +#define SCARLETT2_USB_INTERRUPT_INTERVAL 3 + +/* Interrupt flags for volume and mute/dim button changes */ +#define SCARLETT2_USB_INTERRUPT_VOL_CHANGE 0x400000 +#define SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE 0x200000 + +/* Commands for sending/receiving requests/responses */ +#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ 2 +#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP 3 + +#define SCARLETT2_USB_INIT_SEQ 0x00000000 +#define SCARLETT2_USB_GET_METER_LEVELS 0x00001001 +#define SCARLETT2_USB_SET_MIX 0x00002002 +#define SCARLETT2_USB_SET_MUX 0x00003002 +#define SCARLETT2_USB_GET_DATA 0x00800000 +#define SCARLETT2_USB_SET_DATA 0x00800001 +#define SCARLETT2_USB_DATA_CMD 0x00800002 +#define SCARLETT2_USB_CONFIG_SAVE 6 + +#define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31 +#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1 + +/* volume status is read together (matches scarlett2_config_items[]) */ +struct scarlett2_usb_volume_status { + /* mute & dim buttons */ + u8 buttons[SCARLETT2_BUTTON_MAX]; + + u8 pad1; + + /* software volume setting */ + s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; + + /* actual volume of output inc. dim (-18dB) */ + s16 hw_vol[SCARLETT2_ANALOGUE_MAX]; + + u8 pad2[SCARLETT2_ANALOGUE_MAX]; + + /* sw (0) or hw (1) controlled */ + u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; + + u8 pad3[6]; + + /* front panel volume knob */ + s16 master_vol; +} __packed; + +/* Configuration parameters that can be read and written */ +enum { + SCARLETT2_CONFIG_BUTTONS = 0, + SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1, + SCARLETT2_CONFIG_SW_HW_SWITCH = 2, + SCARLETT2_CONFIG_LEVEL_SWITCH = 3, + SCARLETT2_CONFIG_PAD_SWITCH = 4, + SCARLETT2_CONFIG_COUNT = 5 +}; + +/* Location, size, and activation command number for the configuration + * parameters + */ +struct scarlett2_config { + u8 offset; + u8 size; + u8 activate; +}; + +static const struct scarlett2_config + scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = { + /* Mute/Dim Buttons */ + { + .offset = 0x31, + .size = 1, + .activate = 2 + }, + + /* Line Out Volume */ + { + .offset = 0x34, + .size = 2, + .activate = 1 + }, + + /* SW/HW Volume Switch */ + { + .offset = 0x66, + .size = 1, + .activate = 3 + }, + + /* Level Switch */ + { + .offset = 0x7c, + .size = 1, + .activate = 7 + }, + + /* Pad Switch */ + { + .offset = 0x84, + .size = 1, + .activate = 8 + } +}; + +/* proprietary request/response format */ +struct scarlett2_usb_packet { + __le32 cmd; + __le16 size; + __le16 seq; + __le32 error; + __le32 pad; + u8 data[]; +}; + +#define SCARLETT2_USB_PACKET_LEN (sizeof(struct scarlett2_usb_packet)) + +static void scarlett2_fill_request_header(struct scarlett2_mixer_data *private, + struct scarlett2_usb_packet *req, + u32 cmd, u16 req_size) +{ + /* sequence must go up by 1 for each request */ + u16 seq = private->scarlett2_seq++; + + req->cmd = cpu_to_le32(cmd); + req->size = cpu_to_le16(req_size); + req->seq = cpu_to_le16(seq); + req->error = 0; + req->pad = 0; +} + +/* Send a proprietary format request to the Scarlett interface */ +static int scarlett2_usb( + struct usb_mixer_interface *mixer, u32 cmd, + void *req_data, u16 req_size, void *resp_data, u16 resp_size) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size; + u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size; + struct scarlett2_usb_packet *req = NULL, *resp = NULL; + int err = 0; + + req = kmalloc(req_buf_size, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + goto error; + } + + resp = kmalloc(resp_buf_size, GFP_KERNEL); + if (!resp) { + err = -ENOMEM; + goto error; + } + + mutex_lock(&private->usb_mutex); + + /* build request message and send it */ + + scarlett2_fill_request_header(private, req, cmd, req_size); + + if (req_size) + memcpy(req->data, req_data, req_size); + + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0, + SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE, + req, + req_buf_size); + + if (err != req_buf_size) { + usb_audio_err( + mixer->chip, + "Scarlett Gen 2 USB request result cmd %x was %d\n", + cmd, err); + err = -EINVAL; + goto unlock; + } + + /* send a second message to get the response */ + + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, + SCARLETT2_USB_VENDOR_SPECIFIC_INTERFACE, + resp, + resp_buf_size); + + /* validate the response */ + + if (err != resp_buf_size) { + usb_audio_err( + mixer->chip, + "Scarlett Gen 2 USB response result cmd %x was %d\n", + cmd, err); + err = -EINVAL; + goto unlock; + } + + if (resp->cmd != req->cmd || + resp->seq != req->seq || + resp_size != le16_to_cpu(resp->size) || + resp->error || + resp->pad) { + usb_audio_err( + mixer->chip, + "Scarlett Gen 2 USB invalid response; " + "cmd tx/rx %d/%d seq %d/%d size %d/%d " + "error %d pad %d\n", + le32_to_cpu(req->cmd), le32_to_cpu(resp->cmd), + le16_to_cpu(req->seq), le16_to_cpu(resp->seq), + resp_size, le16_to_cpu(resp->size), + le32_to_cpu(resp->error), + le32_to_cpu(resp->pad)); + err = -EINVAL; + goto unlock; + } + + if (resp_size > 0) + memcpy(resp_data, resp->data, resp_size); + +unlock: + mutex_unlock(&private->usb_mutex); +error: + kfree(req); + kfree(resp); + return err; +} + +/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ +static void scarlett2_config_save(struct usb_mixer_interface *mixer) +{ + __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE); + + scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, + &req, sizeof(u32), + NULL, 0); +} + +/* Delayed work to save config */ +static void scarlett2_config_save_work(struct work_struct *work) +{ + struct scarlett2_mixer_data *private = + container_of(work, struct scarlett2_mixer_data, work.work); + + scarlett2_config_save(private->mixer); +} + +/* Send a USB message to set a configuration parameter (volume level, + * sw/hw volume switch, line/inst level switch, or pad switch) + */ +static int scarlett2_usb_set_config( + struct usb_mixer_interface *mixer, + int config_item_num, int index, int value) +{ + const struct scarlett2_config config_item = + scarlett2_config_items[config_item_num]; + struct { + __le32 offset; + __le32 bytes; + __le32 value; + } __packed req; + __le32 req2; + int err; + struct scarlett2_mixer_data *private = mixer->private_data; + + /* Cancel any pending NVRAM save */ + cancel_delayed_work_sync(&private->work); + + /* Send the configuration parameter data */ + req.offset = cpu_to_le32(config_item.offset + index * config_item.size); + req.bytes = cpu_to_le32(config_item.size); + req.value = cpu_to_le32(value); + err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA, + &req, sizeof(u32) * 2 + config_item.size, + NULL, 0); + if (err < 0) + return err; + + /* Activate the change */ + req2 = cpu_to_le32(config_item.activate); + err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, + &req2, sizeof(req2), NULL, 0); + if (err < 0) + return err; + + /* Schedule the change to be written to NVRAM */ + schedule_delayed_work(&private->work, msecs_to_jiffies(2000)); + + return 0; +} + +/* Send a USB message to get data; result placed in *buf */ +static int scarlett2_usb_get( + struct usb_mixer_interface *mixer, + int offset, void *buf, int size) +{ + struct { + __le32 offset; + __le32 size; + } __packed req; + + req.offset = cpu_to_le32(offset); + req.size = cpu_to_le32(size); + return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA, + &req, sizeof(req), buf, size); +} + +/* Send a USB message to get configuration parameters; result placed in *buf */ +static int scarlett2_usb_get_config( + struct usb_mixer_interface *mixer, + int config_item_num, int count, void *buf) +{ + const struct scarlett2_config config_item = + scarlett2_config_items[config_item_num]; + int size = config_item.size * count; + + return scarlett2_usb_get(mixer, config_item.offset, buf, size); +} + +/* Send a USB message to get volume status; result placed in *buf */ +static int scarlett2_usb_get_volume_status( + struct usb_mixer_interface *mixer, + struct scarlett2_usb_volume_status *buf) +{ + return scarlett2_usb_get(mixer, SCARLETT2_USB_VOLUME_STATUS_OFFSET, + buf, sizeof(*buf)); +} + +/* Send a USB message to set the volumes for all inputs of one mix + * (values obtained from private->mix[]) + */ +static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer, + int mix_num) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + struct { + __le16 mix_num; + __le16 data[SCARLETT2_INPUT_MIX_MAX]; + } __packed req; + + int i, j; + int num_mixer_in = + info->ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; + + req.mix_num = cpu_to_le16(mix_num); + + for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++) + req.data[i] = cpu_to_le16( + scarlett2_mixer_values[private->mix[j]] + ); + + return scarlett2_usb(mixer, SCARLETT2_USB_SET_MIX, + &req, (num_mixer_in + 1) * sizeof(u16), + NULL, 0); +} + +/* Convert a port number index (per info->ports) to a hardware ID */ +static u32 scarlett2_mux_src_num_to_id(const struct scarlett2_ports *ports, + int num) +{ + int port_type; + + for (port_type = 0; + port_type < SCARLETT2_PORT_TYPE_COUNT; + port_type++) { + if (num < ports[port_type].num[SCARLETT2_PORT_IN]) + return ports[port_type].id | num; + num -= ports[port_type].num[SCARLETT2_PORT_IN]; + } + + /* Oops */ + return 0; +} + +/* Send USB messages to set mux inputs */ +static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + const struct scarlett2_ports *ports = info->ports; + int rate, port_dir_rate; + + static const int assignment_order[SCARLETT2_PORT_TYPE_COUNT] = { + SCARLETT2_PORT_TYPE_PCM, + SCARLETT2_PORT_TYPE_ANALOGUE, + SCARLETT2_PORT_TYPE_SPDIF, + SCARLETT2_PORT_TYPE_ADAT, + SCARLETT2_PORT_TYPE_MIX, + SCARLETT2_PORT_TYPE_NONE, + }; + + struct { + __le16 pad; + __le16 num; + __le32 data[SCARLETT2_MUX_MAX]; + } __packed req; + + req.pad = 0; + + /* mux settings for each rate */ + for (rate = 0, port_dir_rate = SCARLETT2_PORT_OUT_44; + port_dir_rate <= SCARLETT2_PORT_OUT_176; + rate++, port_dir_rate++) { + int order_num, i, err; + + req.num = cpu_to_le16(rate); + + for (order_num = 0, i = 0; + order_num < SCARLETT2_PORT_TYPE_COUNT; + order_num++) { + int port_type = assignment_order[order_num]; + int j = scarlett2_get_port_start_num(ports, + SCARLETT2_PORT_OUT, + port_type); + int port_id = ports[port_type].id; + int channel; + + for (channel = 0; + channel < ports[port_type].num[port_dir_rate]; + channel++, i++, j++) + /* lower 12 bits for the destination and + * next 12 bits for the source + */ + req.data[i] = !port_id + ? 0 + : cpu_to_le32( + port_id | + channel | + scarlett2_mux_src_num_to_id( + ports, private->mux[j] + ) << 12 + ); + + /* skip private->mux[j] entries not output */ + j += ports[port_type].num[SCARLETT2_PORT_OUT] - + ports[port_type].num[port_dir_rate]; + } + + err = scarlett2_usb(mixer, SCARLETT2_USB_SET_MUX, + &req, (i + 1) * sizeof(u32), + NULL, 0); + if (err < 0) + return err; + } + + return 0; +} + +/* Send USB message to get meter levels */ +static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, + u16 *levels) +{ + struct { + __le16 pad; + __le16 num_meters; + __le32 magic; + } __packed req; + u32 resp[SCARLETT2_NUM_METERS]; + int i, err; + + req.pad = 0; + req.num_meters = cpu_to_le16(SCARLETT2_NUM_METERS); + req.magic = cpu_to_le32(SCARLETT2_USB_METER_LEVELS_GET_MAGIC); + err = scarlett2_usb(mixer, SCARLETT2_USB_GET_METER_LEVELS, + &req, sizeof(req), resp, sizeof(resp)); + if (err < 0) + return err; + + /* copy, convert to u16 */ + for (i = 0; i < SCARLETT2_NUM_METERS; i++) + levels[i] = resp[i]; + + return 0; +} + +/*** Control Functions ***/ + +/* helper function to create a new control */ +static int scarlett2_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; + + elem->head.mixer = mixer; + elem->control = index; + elem->head.id = index; + elem->channels = channels; + + kctl = snd_ctl_new1(ncontrol, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + + strlcpy(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; +} + +/*** Analogue Line Out Volume Controls ***/ + +/* Update hardware volume controls after receiving notification that + * they have changed + */ +static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_ports *ports = private->info->ports; + struct scarlett2_usb_volume_status volume_status; + int num_line_out = + ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + int err, i; + + private->vol_updated = 0; + + err = scarlett2_usb_get_volume_status(mixer, &volume_status); + if (err < 0) + return err; + + private->master_vol = clamp( + volume_status.master_vol + SCARLETT2_VOLUME_BIAS, + 0, SCARLETT2_VOLUME_BIAS); + + for (i = 0; i < num_line_out; i++) { + if (private->vol_sw_hw_switch[i]) + private->vol[i] = private->master_vol; + } + + for (i = 0; i < private->info->button_count; i++) + private->buttons[i] = !!volume_status.buttons[i]; + + return 0; +} + +static int scarlett2_volume_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 = SCARLETT2_VOLUME_BIAS; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett2_master_volume_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 scarlett2_mixer_data *private = mixer->private_data; + + if (private->vol_updated) { + mutex_lock(&private->data_mutex); + scarlett2_update_volumes(mixer); + mutex_unlock(&private->data_mutex); + } + + ucontrol->value.integer.value[0] = private->master_vol; + return 0; +} + +static int scarlett2_volume_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 scarlett2_mixer_data *private = mixer->private_data; + int index = elem->control; + + if (private->vol_updated) { + mutex_lock(&private->data_mutex); + scarlett2_update_volumes(mixer); + mutex_unlock(&private->data_mutex); + } + + ucontrol->value.integer.value[0] = private->vol[index]; + return 0; +} + +static int scarlett2_volume_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_mixer_data *private = mixer->private_data; + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->vol[index]; + val = ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + 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; +} + +static const DECLARE_TLV_DB_MINMAX( + db_scale_scarlett2_gain, -SCARLETT2_VOLUME_BIAS * 100, 0 +); + +static const struct snd_kcontrol_new scarlett2_master_volume_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_volume_ctl_info, + .get = scarlett2_master_volume_ctl_get, + .private_value = 0, /* max value */ + .tlv = { .p = db_scale_scarlett2_gain } +}; + +static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_volume_ctl_info, + .get = scarlett2_volume_ctl_get, + .put = scarlett2_volume_ctl_put, + .private_value = 0, /* max value */ + .tlv = { .p = db_scale_scarlett2_gain } +}; + +/*** HW/SW Volume Switch Controls ***/ + +static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[2] = { + "SW", "HW" + }; + + return snd_ctl_enum_info(uinfo, 1, 2, values); +} + +static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_mixer_data *private = elem->head.mixer->private_data; + + ucontrol->value.enumerated.item[0] = + private->vol_sw_hw_switch[elem->control]; + return 0; +} + +static int scarlett2_sw_hw_enum_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_mixer_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->vol_sw_hw_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->vol_sw_hw_switch[index] = val; + + /* Change access mode to RO (hardware controlled volume) + * or RW (software controlled volume) + */ + if (val) + private->vol_ctls[index]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_WRITE; + else + private->vol_ctls[index]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_WRITE; + + /* Reset volume to master volume */ + private->vol[index] = private->master_vol; + + /* Set SW volume to current HW volume */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, + index, private->master_vol - SCARLETT2_VOLUME_BIAS); + if (err < 0) + goto unlock; + + /* Notify of RO/RW change */ + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, + &private->vol_ctls[index]->id); + + /* Send SW/HW switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_sw_hw_enum_ctl_info, + .get = scarlett2_sw_hw_enum_ctl_get, + .put = scarlett2_sw_hw_enum_ctl_put, +}; + +/*** Line Level/Instrument Level Switch Controls ***/ + +static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[2] = { + "Line", "Inst" + }; + + return snd_ctl_enum_info(uinfo, 1, 2, values); +} + +static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_mixer_data *private = elem->head.mixer->private_data; + + ucontrol->value.enumerated.item[0] = + private->level_switch[elem->control]; + return 0; +} + +static int scarlett2_level_enum_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_mixer_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->level_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->level_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_level_enum_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_level_enum_ctl_info, + .get = scarlett2_level_enum_ctl_get, + .put = scarlett2_level_enum_ctl_put, +}; + +/*** Pad Switch Controls ***/ + +static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_mixer_data *private = elem->head.mixer->private_data; + + ucontrol->value.enumerated.item[0] = + private->pad_switch[elem->control]; + return 0; +} + +static int scarlett2_pad_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_mixer_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->pad_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->pad_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PAD_SWITCH, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_pad_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_pad_ctl_get, + .put = scarlett2_pad_ctl_put, +}; + +/*** Mute/Dim Controls ***/ + +static int scarlett2_button_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 scarlett2_mixer_data *private = mixer->private_data; + + if (private->vol_updated) { + mutex_lock(&private->data_mutex); + scarlett2_update_volumes(mixer); + mutex_unlock(&private->data_mutex); + } + + ucontrol->value.enumerated.item[0] = private->buttons[elem->control]; + return 0; +} + +static int scarlett2_button_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_mixer_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->buttons[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->buttons[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_BUTTONS, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_button_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_button_ctl_get, + .put = scarlett2_button_ctl_put +}; + +/*** Create the analogue output controls ***/ + +static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + const struct scarlett2_ports *ports = info->ports; + int num_line_out = + ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + int err, i; + char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + /* Add R/O HW volume control */ + if (info->line_out_hw_vol) { + snprintf(s, sizeof(s), "Master HW Playback Volume"); + err = scarlett2_add_new_ctl(mixer, + &scarlett2_master_volume_ctl, + 0, 1, s, &private->master_vol_ctl); + if (err < 0) + return err; + } + + /* Add volume controls */ + for (i = 0; i < num_line_out; i++) { + + /* Fader */ + if (info->line_out_descrs[i]) + snprintf(s, sizeof(s), + "Line %02d (%s) Playback Volume", + i + 1, info->line_out_descrs[i]); + else + snprintf(s, sizeof(s), + "Line %02d Playback Volume", + i + 1); + err = scarlett2_add_new_ctl(mixer, + &scarlett2_line_out_volume_ctl, + i, 1, s, &private->vol_ctls[i]); + if (err < 0) + return err; + + /* Make the fader read-only if the SW/HW switch is set to HW */ + if (private->vol_sw_hw_switch[i]) + private->vol_ctls[i]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_WRITE; + + /* SW/HW Switch */ + if (info->line_out_hw_vol) { + snprintf(s, sizeof(s), + "Line Out %02d Volume Control Playback Enum", + i + 1); + err = scarlett2_add_new_ctl(mixer, + &scarlett2_sw_hw_enum_ctl, + i, 1, s, NULL); + if (err < 0) + return err; + } + } + + /* Add HW button controls */ + for (i = 0; i < private->info->button_count; i++) { + err = scarlett2_add_new_ctl(mixer, &scarlett2_button_ctl, + i, 1, scarlett2_button_names[i], + &private->button_ctls[i]); + if (err < 0) + return err; + } + + return 0; +} + +/*** Create the analogue input controls ***/ + +static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int err, i; + char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + /* Add input level (line/inst) controls */ + for (i = 0; i < info->level_input_count; i++) { + snprintf(s, sizeof(s), "Line In %d Level Capture Enum", i + 1); + err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl, + i, 1, s, NULL); + if (err < 0) + return err; + } + + /* Add input pad controls */ + for (i = 0; i < info->pad_input_count; i++) { + snprintf(s, sizeof(s), "Line In %d Pad Capture Switch", i + 1); + err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl, + i, 1, s, NULL); + if (err < 0) + return err; + } + + return 0; +} + +/*** Mixer Volume Controls ***/ + +static int scarlett2_mixer_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 = SCARLETT2_MIXER_MAX_VALUE; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_mixer_data *private = elem->head.mixer->private_data; + + ucontrol->value.integer.value[0] = private->mix[elem->control]; + return 0; +} + +static int scarlett2_mixer_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_mixer_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + const struct scarlett2_ports *ports = info->ports; + int oval, val, num_mixer_in, mix_num, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->mix[elem->control]; + val = ucontrol->value.integer.value[0]; + num_mixer_in = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; + mix_num = elem->control / num_mixer_in; + + if (oval == val) + goto unlock; + + private->mix[elem->control] = val; + err = scarlett2_usb_set_mix(mixer, mix_num); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const DECLARE_TLV_DB_MINMAX( + db_scale_scarlett2_mixer, + SCARLETT2_MIXER_MIN_DB * 100, + SCARLETT2_MIXER_MAX_DB * 100 +); + +static const struct snd_kcontrol_new scarlett2_mixer_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_mixer_ctl_info, + .get = scarlett2_mixer_ctl_get, + .put = scarlett2_mixer_ctl_put, + .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */ + .tlv = { .p = db_scale_scarlett2_mixer } +}; + +static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_ports *ports = private->info->ports; + int err, i, j; + int index; + char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + int num_inputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; + int num_outputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_IN]; + + for (i = 0, index = 0; i < num_outputs; i++) { + for (j = 0; j < num_inputs; j++, index++) { + snprintf(s, sizeof(s), + "Mix %c Input %02d Playback Volume", + 'A' + i, j + 1); + err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl, + index, 1, s, NULL); + if (err < 0) + return err; + } + } + + return 0; +} + +/*** Mux Source Selection Controls ***/ + +static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_mixer_data *private = elem->head.mixer->private_data; + const struct scarlett2_ports *ports = private->info->ports; + unsigned int item = uinfo->value.enumerated.item; + int items = private->num_mux_srcs; + int port_type; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = elem->channels; + uinfo->value.enumerated.items = items; + + if (item >= items) + item = uinfo->value.enumerated.item = items - 1; + + for (port_type = 0; + port_type < SCARLETT2_PORT_TYPE_COUNT; + port_type++) { + if (item < ports[port_type].num[SCARLETT2_PORT_IN]) { + sprintf(uinfo->value.enumerated.name, + ports[port_type].src_descr, + item + ports[port_type].src_num_offset); + return 0; + } + item -= ports[port_type].num[SCARLETT2_PORT_IN]; + } + + return -EINVAL; +} + +static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_mixer_data *private = elem->head.mixer->private_data; + + ucontrol->value.enumerated.item[0] = private->mux[elem->control]; + return 0; +} + +static int scarlett2_mux_src_enum_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_mixer_data *private = mixer->private_data; + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->mux[index]; + val = clamp(ucontrol->value.integer.value[0], + 0L, private->num_mux_srcs - 1L); + + if (oval == val) + goto unlock; + + private->mux[index] = val; + err = scarlett2_usb_set_mux(mixer); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_mux_src_enum_ctl_info, + .get = scarlett2_mux_src_enum_ctl_get, + .put = scarlett2_mux_src_enum_ctl_put, +}; + +static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_ports *ports = private->info->ports; + int port_type, channel, i; + + for (i = 0, port_type = 0; + port_type < SCARLETT2_PORT_TYPE_COUNT; + port_type++) { + for (channel = 0; + channel < ports[port_type].num[SCARLETT2_PORT_OUT]; + channel++, i++) { + int err; + char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *const descr = ports[port_type].dst_descr; + + snprintf(s, sizeof(s) - 5, descr, channel + 1); + strcat(s, " Enum"); + + err = scarlett2_add_new_ctl(mixer, + &scarlett2_mux_src_enum_ctl, + i, 1, s, NULL); + if (err < 0) + return err; + } + } + + return 0; +} + +/*** Meter Controls ***/ + +static int scarlett2_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 scarlett2_meter_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + u16 meter_levels[SCARLETT2_NUM_METERS]; + int i, err; + + err = scarlett2_usb_get_meter_levels(elem->head.mixer, meter_levels); + if (err < 0) + return err; + + for (i = 0; i < elem->channels; i++) + ucontrol->value.integer.value[i] = meter_levels[i]; + + return 0; +} + +static const struct snd_kcontrol_new scarlett2_meter_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .name = "", + .info = scarlett2_meter_ctl_info, + .get = scarlett2_meter_ctl_get +}; + +static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) +{ + return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, + 0, SCARLETT2_NUM_METERS, + "Level Meter", NULL); +} + +/*** Cleanup/Suspend Callbacks ***/ + +static void scarlett2_private_free(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + + cancel_delayed_work_sync(&private->work); + kfree(private); + mixer->private_data = NULL; +} + +static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + + if (cancel_delayed_work_sync(&private->work)) + scarlett2_config_save(private->mixer); +} + +/*** Initialisation ***/ + +static int scarlett2_count_mux_srcs(const struct scarlett2_ports *ports) +{ + int port_type, count = 0; + + for (port_type = 0; + port_type < SCARLETT2_PORT_TYPE_COUNT; + port_type++) + count += ports[port_type].num[SCARLETT2_PORT_IN]; + + return count; +} + +/* Default routing connects PCM outputs and inputs to Analogue, + * S/PDIF, then ADAT + */ +static void scarlett2_init_routing(u8 *mux, + const struct scarlett2_ports *ports) +{ + int i, input_num, input_count, port_type; + int output_num, output_count, port_type_connect_num; + + static const int connect_order[] = { + SCARLETT2_PORT_TYPE_ANALOGUE, + SCARLETT2_PORT_TYPE_SPDIF, + SCARLETT2_PORT_TYPE_ADAT, + -1 + }; + + /* Assign PCM inputs (routing outputs) */ + output_num = scarlett2_get_port_start_num(ports, + SCARLETT2_PORT_OUT, + SCARLETT2_PORT_TYPE_PCM); + output_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_OUT]; + + for (port_type = connect_order[port_type_connect_num = 0]; + port_type >= 0; + port_type = connect_order[++port_type_connect_num]) { + input_num = scarlett2_get_port_start_num( + ports, SCARLETT2_PORT_IN, port_type); + input_count = ports[port_type].num[SCARLETT2_PORT_IN]; + for (i = 0; + i < input_count && output_count; + i++, output_count--) + mux[output_num++] = input_num++; + } + + /* Assign PCM outputs (routing inputs) */ + input_num = scarlett2_get_port_start_num(ports, + SCARLETT2_PORT_IN, + SCARLETT2_PORT_TYPE_PCM); + input_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_IN]; + + for (port_type = connect_order[port_type_connect_num = 0]; + port_type >= 0; + port_type = connect_order[++port_type_connect_num]) { + output_num = scarlett2_get_port_start_num( + ports, SCARLETT2_PORT_OUT, port_type); + output_count = ports[port_type].num[SCARLETT2_PORT_OUT]; + for (i = 0; + i < output_count && input_count; + i++, input_count--) + mux[output_num++] = input_num++; + } +} + +/* Initialise private data, routing, sequence number */ +static int scarlett2_init_private(struct usb_mixer_interface *mixer, + const struct scarlett2_device_info *info) +{ + struct scarlett2_mixer_data *private = + kzalloc(sizeof(struct scarlett2_mixer_data), GFP_KERNEL); + + if (!private) + return -ENOMEM; + + mutex_init(&private->usb_mutex); + mutex_init(&private->data_mutex); + INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); + private->info = info; + private->num_mux_srcs = scarlett2_count_mux_srcs(info->ports); + private->scarlett2_seq = 0; + private->mixer = mixer; + mixer->private_data = private; + mixer->private_free = scarlett2_private_free; + mixer->private_suspend = scarlett2_private_suspend; + + /* Setup default routing */ + scarlett2_init_routing(private->mux, info->ports); + + /* Initialise the sequence number used for the proprietary commands */ + return scarlett2_usb(mixer, SCARLETT2_USB_INIT_SEQ, NULL, 0, NULL, 0); +} + +/* Read line-in config and line-out volume settings on start */ +static int scarlett2_read_configs(struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + const struct scarlett2_ports *ports = info->ports; + int num_line_out = + ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + u8 level_switches[SCARLETT2_LEVEL_SWITCH_MAX]; + u8 pad_switches[SCARLETT2_PAD_SWITCH_MAX]; + struct scarlett2_usb_volume_status volume_status; + int err, i; + + if (info->level_input_count) { + err = scarlett2_usb_get_config( + mixer, + SCARLETT2_CONFIG_LEVEL_SWITCH, + info->level_input_count, + level_switches); + if (err < 0) + return err; + for (i = 0; i < info->level_input_count; i++) + private->level_switch[i] = level_switches[i]; + } + + if (info->pad_input_count) { + err = scarlett2_usb_get_config( + mixer, + SCARLETT2_CONFIG_PAD_SWITCH, + info->pad_input_count, + pad_switches); + if (err < 0) + return err; + for (i = 0; i < info->pad_input_count; i++) + private->pad_switch[i] = pad_switches[i]; + } + + err = scarlett2_usb_get_volume_status(mixer, &volume_status); + if (err < 0) + return err; + + private->master_vol = clamp( + volume_status.master_vol + SCARLETT2_VOLUME_BIAS, + 0, SCARLETT2_VOLUME_BIAS); + + for (i = 0; i < num_line_out; i++) { + int volume; + + private->vol_sw_hw_switch[i] = + info->line_out_hw_vol + && volume_status.sw_hw_switch[i]; + + volume = private->vol_sw_hw_switch[i] + ? volume_status.master_vol + : volume_status.sw_vol[i]; + volume = clamp(volume + SCARLETT2_VOLUME_BIAS, + 0, SCARLETT2_VOLUME_BIAS); + private->vol[i] = volume; + } + + for (i = 0; i < info->button_count; i++) + private->buttons[i] = !!volume_status.buttons[i]; + + return 0; +} + +/* Notify on volume change */ +static void scarlett2_mixer_interrupt_vol_change( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + const struct scarlett2_ports *ports = private->info->ports; + int num_line_out = + ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + int i; + + private->vol_updated = 1; + + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->master_vol_ctl->id); + + for (i = 0; i < num_line_out; i++) { + if (!private->vol_sw_hw_switch[i]) + continue; + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->vol_ctls[i]->id); + } +} + +/* Notify on button change */ +static void scarlett2_mixer_interrupt_button_change( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_mixer_data *private = mixer->private_data; + int i; + + private->vol_updated = 1; + + for (i = 0; i < private->info->button_count; i++) + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->button_ctls[i]->id); +} + +/* Interrupt callback */ +static void scarlett2_mixer_interrupt(struct urb *urb) +{ + struct usb_mixer_interface *mixer = urb->context; + int len = urb->actual_length; + int ustatus = urb->status; + u32 data; + + if (ustatus != 0) + goto requeue; + + if (len == 8) { + data = le32_to_cpu(*(u32 *)urb->transfer_buffer); + if (data & SCARLETT2_USB_INTERRUPT_VOL_CHANGE) + scarlett2_mixer_interrupt_vol_change(mixer); + if (data & SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE) + scarlett2_mixer_interrupt_button_change(mixer); + } else { + usb_audio_err(mixer->chip, + "scarlett mixer interrupt length %d\n", len); + } + +requeue: + if (ustatus != -ENOENT && + ustatus != -ECONNRESET && + ustatus != -ESHUTDOWN) { + urb->dev = mixer->chip->dev; + usb_submit_urb(urb, GFP_ATOMIC); + } +} + +static int scarlett2_mixer_status_create(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + unsigned int pipe = usb_rcvintpipe(dev, + SCARLETT2_USB_INTERRUPT_ENDPOINT); + void *transfer_buffer; + + if (mixer->urb) { + usb_audio_err(mixer->chip, + "%s: mixer urb already in use!\n", __func__); + return 0; + } + + if (snd_usb_pipe_sanity_check(dev, pipe)) + return -EINVAL; + + mixer->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mixer->urb) + return -ENOMEM; + + transfer_buffer = kmalloc(SCARLETT2_USB_INTERRUPT_MAX_DATA, GFP_KERNEL); + if (!transfer_buffer) + return -ENOMEM; + + usb_fill_int_urb(mixer->urb, dev, pipe, + transfer_buffer, SCARLETT2_USB_INTERRUPT_MAX_DATA, + scarlett2_mixer_interrupt, mixer, + SCARLETT2_USB_INTERRUPT_INTERVAL); + + return usb_submit_urb(mixer->urb, GFP_KERNEL); +} + +/* Entry point */ +int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) +{ + const struct scarlett2_device_info *info; + int err; + + /* only use UAC_VERSION_2 */ + if (!mixer->protocol) + return 0; + + switch (mixer->chip->usb_id) { + case USB_ID(0x1235, 0x8203): + info = &s6i6_gen2_info; + break; + case USB_ID(0x1235, 0x8204): + info = &s18i8_gen2_info; + break; + case USB_ID(0x1235, 0x8201): + info = &s18i20_gen2_info; + break; + default: /* device not (yet) supported */ + return -EINVAL; + } + + if (!(mixer->chip->setup & SCARLETT2_ENABLE)) { + usb_audio_err(mixer->chip, + "Focusrite Scarlett Gen 2 Mixer Driver disabled; " + "use options snd_usb_audio device_setup=1 " + "to enable and report any issues to g@b4.vu"); + return 0; + } + + /* Initialise private data, routing, sequence number */ + err = scarlett2_init_private(mixer, info); + if (err < 0) + return err; + + /* Read volume levels and controls from the interface */ + err = scarlett2_read_configs(mixer); + if (err < 0) + return err; + + /* Create the analogue output controls */ + err = scarlett2_add_line_out_ctls(mixer); + if (err < 0) + return err; + + /* Create the analogue input controls */ + err = scarlett2_add_line_in_ctls(mixer); + if (err < 0) + return err; + + /* Create the input, output, and mixer mux input selections */ + err = scarlett2_add_mux_enums(mixer); + if (err < 0) + return err; + + /* Create the matrix mixer controls */ + err = scarlett2_add_mixer_ctls(mixer); + if (err < 0) + return err; + + /* Create the level meter controls */ + err = scarlett2_add_meter_ctl(mixer); + if (err < 0) + return err; + + /* Set up the interrupt polling if there are hardware buttons */ + if (info->button_count) { + err = scarlett2_mixer_status_create(mixer); + if (err < 0) + return err; + } + + return 0; +} diff --git a/sound/usb/mixer_scarlett_gen2.h b/sound/usb/mixer_scarlett_gen2.h new file mode 100644 index 000000000000..52e1dad77afd --- /dev/null +++ b/sound/usb/mixer_scarlett_gen2.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __USB_MIXER_SCARLETT_GEN2_H +#define __USB_MIXER_SCARLETT_GEN2_H + +int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer); + +#endif /* __USB_MIXER_SCARLETT_GEN2_H */ diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index f0e8e1539450..986145fd2ce0 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -760,7 +760,7 @@ static int snd_us16x08_meter_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new snd_us16x08_ch_boolean_ctl = { +static const struct snd_kcontrol_new snd_us16x08_ch_boolean_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -770,7 +770,7 @@ static struct snd_kcontrol_new snd_us16x08_ch_boolean_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) }; -static struct snd_kcontrol_new snd_us16x08_ch_int_ctl = { +static const struct snd_kcontrol_new snd_us16x08_ch_int_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -780,7 +780,7 @@ static struct snd_kcontrol_new snd_us16x08_ch_int_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 133) }; -static struct snd_kcontrol_new snd_us16x08_pan_int_ctl = { +static const struct snd_kcontrol_new snd_us16x08_pan_int_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -790,7 +790,7 @@ static struct snd_kcontrol_new snd_us16x08_pan_int_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 255) }; -static struct snd_kcontrol_new snd_us16x08_master_ctl = { +static const struct snd_kcontrol_new snd_us16x08_master_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 1, @@ -800,7 +800,7 @@ static struct snd_kcontrol_new snd_us16x08_master_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 133) }; -static struct snd_kcontrol_new snd_us16x08_route_ctl = { +static const struct snd_kcontrol_new snd_us16x08_route_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 8, @@ -810,7 +810,7 @@ static struct snd_kcontrol_new snd_us16x08_route_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 9) }; -static struct snd_kcontrol_new snd_us16x08_bus_ctl = { +static const struct snd_kcontrol_new snd_us16x08_bus_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 1, @@ -820,7 +820,7 @@ static struct snd_kcontrol_new snd_us16x08_bus_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) }; -static struct snd_kcontrol_new snd_us16x08_compswitch_ctl = { +static const struct snd_kcontrol_new snd_us16x08_compswitch_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -830,7 +830,7 @@ static struct snd_kcontrol_new snd_us16x08_compswitch_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) }; -static struct snd_kcontrol_new snd_us16x08_comp_threshold_ctl = { +static const struct snd_kcontrol_new snd_us16x08_comp_threshold_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -841,7 +841,7 @@ static struct snd_kcontrol_new snd_us16x08_comp_threshold_ctl = { 0, 0x20) }; -static struct snd_kcontrol_new snd_us16x08_comp_ratio_ctl = { +static const struct snd_kcontrol_new snd_us16x08_comp_ratio_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -852,7 +852,7 @@ static struct snd_kcontrol_new snd_us16x08_comp_ratio_ctl = { sizeof(ratio_map) - 1), /*max*/ }; -static struct snd_kcontrol_new snd_us16x08_comp_gain_ctl = { +static const struct snd_kcontrol_new snd_us16x08_comp_gain_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -862,7 +862,7 @@ static struct snd_kcontrol_new snd_us16x08_comp_gain_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x14) }; -static struct snd_kcontrol_new snd_us16x08_comp_attack_ctl = { +static const struct snd_kcontrol_new snd_us16x08_comp_attack_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -873,7 +873,7 @@ static struct snd_kcontrol_new snd_us16x08_comp_attack_ctl = { SND_US16X08_KCSET(SND_US16X08_COMP_ATTACK_BIAS, 1, 0, 0xc6), }; -static struct snd_kcontrol_new snd_us16x08_comp_release_ctl = { +static const struct snd_kcontrol_new snd_us16x08_comp_release_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -884,7 +884,7 @@ static struct snd_kcontrol_new snd_us16x08_comp_release_ctl = { SND_US16X08_KCSET(SND_US16X08_COMP_RELEASE_BIAS, 1, 0, 0x63), }; -static struct snd_kcontrol_new snd_us16x08_eq_gain_ctl = { +static const struct snd_kcontrol_new snd_us16x08_eq_gain_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -894,7 +894,7 @@ static struct snd_kcontrol_new snd_us16x08_eq_gain_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 24), }; -static struct snd_kcontrol_new snd_us16x08_eq_low_freq_ctl = { +static const struct snd_kcontrol_new snd_us16x08_eq_low_freq_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -904,7 +904,7 @@ static struct snd_kcontrol_new snd_us16x08_eq_low_freq_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x1F), }; -static struct snd_kcontrol_new snd_us16x08_eq_mid_freq_ctl = { +static const struct snd_kcontrol_new snd_us16x08_eq_mid_freq_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -914,7 +914,7 @@ static struct snd_kcontrol_new snd_us16x08_eq_mid_freq_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x3F) }; -static struct snd_kcontrol_new snd_us16x08_eq_mid_width_ctl = { +static const struct snd_kcontrol_new snd_us16x08_eq_mid_width_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -924,7 +924,7 @@ static struct snd_kcontrol_new snd_us16x08_eq_mid_width_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x06) }; -static struct snd_kcontrol_new snd_us16x08_eq_high_freq_ctl = { +static const struct snd_kcontrol_new snd_us16x08_eq_high_freq_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -935,7 +935,7 @@ static struct snd_kcontrol_new snd_us16x08_eq_high_freq_ctl = { SND_US16X08_KCSET(SND_US16X08_EQ_HIGHFREQ_BIAS, 1, 0, 0x1F) }; -static struct snd_kcontrol_new snd_us16x08_eq_switch_ctl = { +static const struct snd_kcontrol_new snd_us16x08_eq_switch_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 16, @@ -945,7 +945,7 @@ static struct snd_kcontrol_new snd_us16x08_eq_switch_ctl = { .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) }; -static struct snd_kcontrol_new snd_us16x08_meter_ctl = { +static const struct snd_kcontrol_new snd_us16x08_meter_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .count = 1, diff --git a/sound/usb/mixer_us16x08.h b/sound/usb/mixer_us16x08.h index 56ff16c0698f..7b8583dd3b07 100644 --- a/sound/usb/mixer_us16x08.h +++ b/sound/usb/mixer_us16x08.h @@ -108,7 +108,7 @@ struct snd_us16x08_meter_store { }; struct snd_us16x08_control_params { - struct snd_kcontrol_new *kcontrol_new; + const struct snd_kcontrol_new *kcontrol_new; int control_id; int type; int num_channels; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 75b96929f76c..bd258f1ec2dd 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -260,18 +260,31 @@ static int start_endpoints(struct snd_usb_substream *subs) return 0; } -static void stop_endpoints(struct snd_usb_substream *subs, bool wait) +static void sync_pending_stops(struct snd_usb_substream *subs) +{ + snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint); + snd_usb_endpoint_sync_pending_stop(subs->data_endpoint); +} + +static void stop_endpoints(struct snd_usb_substream *subs) { if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) snd_usb_endpoint_stop(subs->sync_endpoint); if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) snd_usb_endpoint_stop(subs->data_endpoint); +} + +/* PCM sync_stop callback */ +static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; - if (wait) { - snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint); - snd_usb_endpoint_sync_pending_stop(subs->data_endpoint); + if (!snd_usb_lock_shutdown(subs->stream->chip)) { + sync_pending_stops(subs); + snd_usb_unlock_shutdown(subs->stream->chip); } + return 0; } static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, @@ -339,6 +352,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x81; ifnum = 2; goto add_sync_ep_from_ifnum; + case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */ case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ ep = 0x81; ifnum = 1; @@ -347,6 +361,13 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x84; ifnum = 0; goto add_sync_ep_from_ifnum; + case USB_ID(0x07fd, 0x0008): /* MOTU M Series */ + ep = 0x81; + ifnum = 2; + goto add_sync_ep_from_ifnum; + case USB_ID(0x0582, 0x01d8): /* BOSS Katana */ + /* BOSS Katana amplifiers do not need quirks */ + return 0; } if (attr == USB_ENDPOINT_SYNC_ASYNC && @@ -366,7 +387,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, add_sync_ep_from_ifnum: iface = usb_ifnum_to_if(dev, ifnum); - if (!iface || iface->num_altsetting == 0) + if (!iface || iface->num_altsetting < 2) return -EINVAL; alts = &iface->altsetting[1]; @@ -456,6 +477,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, } ep = get_endpoint(alts, 1)->bEndpointAddress; if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 0)->bSynchAddress != 0 && ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { dev_err(&dev->dev, @@ -501,15 +523,15 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (WARN_ON(!iface)) return -EINVAL; alts = usb_altnum_to_altsetting(iface, fmt->altsetting); - altsd = get_iface_desc(alts); - if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) + if (WARN_ON(!alts)) return -EINVAL; + altsd = get_iface_desc(alts); - if (fmt == subs->cur_audiofmt) + if (fmt == subs->cur_audiofmt && !subs->need_setup_fmt) return 0; /* close the old interface */ - if (subs->interface >= 0 && subs->interface != fmt->iface) { + if (subs->interface >= 0 && (subs->interface != fmt->iface || subs->need_setup_fmt)) { if (!subs->stream->chip->keep_iface) { err = usb_set_interface(subs->dev, subs->interface, 0); if (err < 0) { @@ -523,6 +545,9 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) subs->altset_idx = 0; } + if (subs->need_setup_fmt) + subs->need_setup_fmt = false; + /* set interface */ if (iface->cur_altsetting != alts) { err = snd_usb_select_mode_quirk(subs, fmt); @@ -692,7 +717,8 @@ static int configure_endpoint(struct snd_usb_substream *subs) int ret; /* format changed */ - stop_endpoints(subs, true); + stop_endpoints(subs); + sync_pending_stops(subs); ret = snd_usb_endpoint_set_params(subs->data_endpoint, subs->pcm_format, subs->channels, @@ -780,15 +806,6 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - if (snd_usb_use_vmalloc) - ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - else - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - goto stop_pipeline; - subs->pcm_format = params_format(hw_params); subs->period_bytes = params_period_bytes(hw_params); subs->period_frames = params_period_size(hw_params); @@ -846,16 +863,14 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_rate = 0; subs->period_bytes = 0; if (!snd_usb_lock_shutdown(subs->stream->chip)) { - stop_endpoints(subs, true); + stop_endpoints(subs); + sync_pending_stops(subs); snd_usb_endpoint_deactivate(subs->sync_endpoint); snd_usb_endpoint_deactivate(subs->data_endpoint); snd_usb_unlock_shutdown(subs->stream->chip); } - if (snd_usb_use_vmalloc) - return snd_pcm_lib_free_vmalloc_buffer(substream); - else - return snd_pcm_lib_free_pages(substream); + return 0; } /* @@ -884,9 +899,6 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) goto unlock; } - snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint); - snd_usb_endpoint_sync_pending_stop(subs->data_endpoint); - ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0); if (ret < 0) goto unlock; @@ -1344,7 +1356,6 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream) struct snd_usb_substream *subs = &as->substream[direction]; int ret; - stop_endpoints(subs, true); snd_media_stop_pipeline(subs); if (!as->chip->keep_iface && @@ -1721,7 +1732,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea subs->running = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs, false); + stop_endpoints(subs); subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -1730,6 +1741,13 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea subs->data_endpoint->retire_data_urb = retire_playback_urb; subs->running = 0; return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (subs->stream->chip->setup_fmt_after_resume_quirk) { + stop_endpoints(subs); + subs->need_setup_fmt = true; + return 0; + } + break; } return -EINVAL; @@ -1751,7 +1769,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream subs->running = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs, false); + stop_endpoints(subs); subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -1762,6 +1780,13 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream subs->data_endpoint->retire_data_urb = retire_capture_urb; subs->running = 1; return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (subs->stream->chip->setup_fmt_after_resume_quirk) { + stop_endpoints(subs); + subs->need_setup_fmt = true; + return 0; + } + break; } return -EINVAL; @@ -1770,61 +1795,31 @@ 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, - .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usb_hw_params, .hw_free = snd_usb_hw_free, .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_substream_playback_trigger, + .sync_stop = snd_usb_pcm_sync_stop, .pointer = snd_usb_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops snd_usb_capture_ops = { .open = snd_usb_pcm_open, .close = snd_usb_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_usb_hw_params, - .hw_free = snd_usb_hw_free, - .prepare = snd_usb_pcm_prepare, - .trigger = snd_usb_substream_capture_trigger, - .pointer = snd_usb_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops snd_usb_playback_dev_ops = { - .open = snd_usb_pcm_open, - .close = snd_usb_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_usb_hw_params, - .hw_free = snd_usb_hw_free, - .prepare = snd_usb_pcm_prepare, - .trigger = snd_usb_substream_playback_trigger, - .pointer = snd_usb_pcm_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - -static const struct snd_pcm_ops snd_usb_capture_dev_ops = { - .open = snd_usb_pcm_open, - .close = snd_usb_pcm_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usb_hw_params, .hw_free = snd_usb_hw_free, .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_substream_capture_trigger, + .sync_stop = snd_usb_pcm_sync_stop, .pointer = snd_usb_pcm_pointer, - .page = snd_pcm_sgbuf_ops_page, }; void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream) { const struct snd_pcm_ops *ops; - if (snd_usb_use_vmalloc) - ops = stream == SNDRV_PCM_STREAM_PLAYBACK ? + ops = stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops; - else - ops = stream == SNDRV_PCM_STREAM_PLAYBACK ? - &snd_usb_playback_dev_ops : &snd_usb_capture_dev_ops; snd_pcm_set_ops(pcm, stream, ops); } @@ -1834,7 +1829,10 @@ void snd_usb_preallocate_buffer(struct snd_usb_substream *subs) struct snd_pcm_substream *s = pcm->streams[subs->direction].substream; struct device *dev = subs->dev->bus->controller; - if (!snd_usb_use_vmalloc) - snd_pcm_lib_preallocate_pages(s, SNDRV_DMA_TYPE_DEV_SG, - dev, 64*1024, 512*1024); + if (snd_usb_use_vmalloc) + snd_pcm_set_managed_buffer(s, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); + else + snd_pcm_set_managed_buffer(s, SNDRV_DMA_TYPE_DEV_SG, + dev, 64*1024, 512*1024); } diff --git a/sound/usb/power.c b/sound/usb/power.c index bd303a1ba1b7..606a2cb23eab 100644 --- a/sound/usb/power.c +++ b/sound/usb/power.c @@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface, struct uac3_power_domain_descriptor *pd_desc = p; int i; + if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3)) + continue; for (i = 0; i < pd_desc->bNrEntities; i++) { if (pd_desc->baEntityID[i] == id) { pd->pd_id = pd_desc->bPowerDomainID; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 49e3f176aaf5..ffbf4bd9208c 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -60,7 +60,7 @@ void snd_usb_audio_create_proc(struct snd_usb_audio *chip) static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) { struct audioformat *fp; - static char *sync_types[4] = { + static const char * const sync_types[4] = { "NONE", "ASYNC", "ADAPTIVE", "SYNC" }; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index e918ce346027..d187aa6d50db 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3466,7 +3466,8 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), .vendor_name = "Dell", .product_name = "WD19 Dock", .profile_name = "Dell-WD15-Dock", - .ifnum = QUIRK_NO_INTERFACE + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_SETUP_FMT_AFTER_RESUME } }, /* MOTU Microbook II */ @@ -3534,5 +3535,62 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } } }, +{ + /* + * PIONEER DJ DDJ-SX3 + * PCM is 12 channels out, 10 channels in @ 44.1 fixed + * interface 0, vendor class alt setting 1 for endpoints 5 and 0x86 + * 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[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels = 12, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x05, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } + }, + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels = 10, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x86, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } + }, + { + .ifnum = -1 + } + } + } +}, #undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 78858918cbc1..7f558f4b4520 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip, NULL, USB_MS_MIDI_OUT_JACK); if (!injd && !outjd) return -ENODEV; + if ((injd && !snd_usb_validate_midi_desc(injd)) || + (outjd && !snd_usb_validate_midi_desc(outjd))) + return -ENODEV; if (injd && (injd->bLength < 5 || (injd->bJackType != USB_MS_EMBEDDED && injd->bJackType != USB_MS_EXTERNAL))) @@ -505,6 +508,16 @@ static int create_standard_mixer_quirk(struct snd_usb_audio *chip, return snd_usb_create_mixer(chip, quirk->ifnum, 0); } + +static int setup_fmt_after_resume_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + chip->setup_fmt_after_resume_quirk = 1; + return 1; /* Continue with creating streams and mixer */ +} + /* * audio-interface quirks * @@ -543,6 +556,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk, [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk, + [QUIRK_SETUP_FMT_AFTER_RESUME] = setup_fmt_after_resume_quirk, }; if (quirk->type < QUIRK_TYPE_COUNT) { @@ -1099,6 +1113,31 @@ free_buf: return err; } +static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev) +{ + int ret; + + if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, 0, NULL, 0, 1000); + + if (ret < 0) + return ret; + + msleep(2000); + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x20, 0, NULL, 0, 1000); + + if (ret < 0) + return ret; + + return 0; +} + /* * Setup quirks */ @@ -1283,6 +1322,19 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, return 0; } +int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + struct usb_interface *intf, + const struct snd_usb_audio_quirk *quirk, + unsigned int id) +{ + switch (id) { + case USB_ID(0x07fd, 0x0008): /* MOTU M Series */ + return snd_usb_motu_m_series_boot_quirk(dev); + } + + return 0; +} + /* * check if the device uses big-endian samples */ @@ -1379,21 +1431,23 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) { /* devices which do not support reading the sample rate. */ switch (chip->usb_id) { - case USB_ID(0x041E, 0x4080): /* Creative Live Cam VF0610 */ - case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ + case USB_ID(0x041e, 0x4080): /* Creative Live Cam VF0610 */ + case USB_ID(0x04d8, 0xfeea): /* Benchmark DAC1 Pre */ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */ - case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */ - case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ + case USB_ID(0x05a3, 0x9420): /* ELP HD USB Camera */ + case USB_ID(0x05a7, 0x1020): /* Bose Companion 5 */ + case USB_ID(0x074d, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */ case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */ - case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ + case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */ + case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */ return true; } /* devices of these vendors don't support reading rate, either */ switch (USB_ID_VENDOR(chip->usb_id)) { - case 0x045E: /* MS Lifecam */ - case 0x047F: /* Plantronics */ + case 0x045e: /* MS Lifecam */ + case 0x047f: /* Plantronics */ case 0x1de7: /* Phoenix Audio */ return true; } @@ -1563,7 +1617,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct usb_interface *iface; /* Playback Designs */ - if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) { + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba && + USB_ID_PRODUCT(chip->usb_id) < 0x0110) { switch (fp->altsetting) { case 1: fp->dsd_dop = true; @@ -1580,9 +1635,6 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, /* XMOS based USB DACs */ switch (chip->usb_id) { case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */ - case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */ - case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */ - case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */ case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */ case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */ if (fp->altsetting == 2) @@ -1596,7 +1648,6 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */ case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */ case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */ - case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */ 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 */ @@ -1651,10 +1702,16 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, * from XMOS/Thesycon */ switch (USB_ID_VENDOR(chip->usb_id)) { - case 0x20b1: /* XMOS based devices */ case 0x152a: /* Thesycon devices */ + case 0x20b1: /* XMOS based devices */ + case 0x22d9: /* Oppo */ + case 0x23ba: /* Playback Designs */ case 0x25ce: /* Mytek devices */ + case 0x278b: /* Rotel? */ + case 0x292b: /* Gustard/Ess based devices */ case 0x2ab6: /* T+A devices */ + case 0x3842: /* EVGA */ + case 0xc502: /* HiBy devices */ if (fp->dsd_raw) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index a80e0ddd0736..df0355843a4c 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -20,6 +20,11 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, const struct snd_usb_audio_quirk *quirk, unsigned int usb_id); +int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + struct usb_interface *intf, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); + void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt); diff --git a/sound/usb/stream.c b/sound/usb/stream.c index e852c7fd6109..afd5aa574611 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -28,6 +28,14 @@ #include "power.h" #include "media.h" +static void audioformat_free(struct audioformat *fp) +{ + list_del(&fp->list); /* unlink for avoiding double-free */ + kfree(fp->rate_table); + kfree(fp->chmap); + kfree(fp); +} + /* * free a substream */ @@ -37,11 +45,8 @@ static void free_substream(struct snd_usb_substream *subs) if (!subs->num_formats) return; /* not initialized */ - list_for_each_entry_safe(fp, n, &subs->fmt_list, list) { - kfree(fp->rate_table); - kfree(fp->chmap); - kfree(fp); - } + list_for_each_entry_safe(fp, n, &subs->fmt_list, list) + audioformat_free(fp); kfree(subs->rate_list.list); kfree(subs->str_pd); snd_media_stream_delete(subs); @@ -234,7 +239,7 @@ static int add_chmap(struct snd_pcm *pcm, int stream, static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, int protocol) { - static unsigned int uac1_maps[] = { + static const unsigned int uac1_maps[] = { SNDRV_CHMAP_FL, /* left front */ SNDRV_CHMAP_FR, /* right front */ SNDRV_CHMAP_FC, /* center front */ @@ -249,7 +254,7 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, SNDRV_CHMAP_TC, /* top */ 0 /* terminator */ }; - static unsigned int uac2_maps[] = { + static const unsigned int uac2_maps[] = { SNDRV_CHMAP_FL, /* front left */ SNDRV_CHMAP_FR, /* front right */ SNDRV_CHMAP_FC, /* front center */ @@ -627,16 +632,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, */ static void * snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, - int terminal_id, bool uac23) + int terminal_id, int protocol) { struct uac2_input_terminal_descriptor *term = NULL; - size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) : - sizeof(struct uac_input_terminal_descriptor); while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, term, UAC_INPUT_TERMINAL))) { - if (term->bLength < minlen) + if (!snd_usb_validate_audio_desc(term, protocol)) continue; if (term->bTerminalID == terminal_id) return term; @@ -647,7 +650,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, static void * snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, - int terminal_id) + int terminal_id, int protocol) { /* OK to use with both UAC2 and UAC3 */ struct uac2_output_terminal_descriptor *term = NULL; @@ -655,8 +658,9 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, while ((term = snd_usb_find_csint_desc(ctrl_iface->extra, ctrl_iface->extralen, term, UAC_OUTPUT_TERMINAL))) { - if (term->bLength >= sizeof(*term) && - term->bTerminalID == terminal_id) + if (!snd_usb_validate_audio_desc(term, protocol)) + continue; + if (term->bTerminalID == terminal_id) return term; } @@ -731,7 +735,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, as->bTerminalLink, - false); + protocol); if (iterm) { num_channels = iterm->bNrChannels; chconfig = le16_to_cpu(iterm->wChannelConfig); @@ -767,7 +771,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, */ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, as->bTerminalLink, - true); + protocol); if (input_term) { clock = input_term->bCSourceID; if (!chconfig && (num_channels == input_term->bNrChannels)) @@ -776,7 +780,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, } output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); + as->bTerminalLink, + protocol); if (output_term) { clock = output_term->bCSourceID; goto found_clock; @@ -832,8 +837,7 @@ found_clock: /* ok, let's parse further... */ if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) { - kfree(fp->rate_table); - kfree(fp); + audioformat_free(fp); return NULL; } @@ -1002,14 +1006,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, */ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, as->bTerminalLink, - true); + UAC_VERSION_3); if (input_term) { clock = input_term->bCSourceID; goto found_clock; } output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); + as->bTerminalLink, + UAC_VERSION_3); if (output_term) { clock = output_term->bCSourceID; goto found_clock; @@ -1043,9 +1048,7 @@ found_clock: pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { - kfree(fp->chmap); - kfree(fp->rate_table); - kfree(fp); + audioformat_free(fp); return NULL; } pd->pd_id = (stream == SNDRV_PCM_STREAM_PLAYBACK) ? @@ -1064,9 +1067,7 @@ found_clock: /* ok, let's parse further... */ if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { kfree(pd); - kfree(fp->chmap); - kfree(fp->rate_table); - kfree(fp); + audioformat_free(fp); return NULL; } } @@ -1077,7 +1078,9 @@ found_clock: return fp; } -int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) +static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, + int iface_no, + bool *has_non_pcm, bool non_pcm) { struct usb_device *dev; struct usb_interface *iface; @@ -1178,6 +1181,16 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) else if (IS_ERR(fp)) return PTR_ERR(fp); + if (fp->fmt_type != UAC_FORMAT_TYPE_I) + *has_non_pcm = true; + if ((fp->fmt_type == UAC_FORMAT_TYPE_I) == non_pcm) { + audioformat_free(fp); + kfree(pd); + fp = NULL; + pd = NULL; + continue; + } + dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); if (protocol == UAC_VERSION_3) err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd); @@ -1185,11 +1198,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { - list_del(&fp->list); /* unlink for avoiding double-free */ + audioformat_free(fp); kfree(pd); - kfree(fp->rate_table); - kfree(fp->chmap); - kfree(fp); return err; } /* try to set the interface... */ @@ -1200,3 +1210,23 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) return 0; } +int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) +{ + int err; + bool has_non_pcm = false; + + /* parse PCM formats */ + err = __snd_usb_parse_audio_interface(chip, iface_no, &has_non_pcm, false); + if (err < 0) + return err; + + if (has_non_pcm) { + /* parse non-PCM formats */ + err = __snd_usb_parse_audio_interface(chip, iface_no, &has_non_pcm, true); + if (err < 0) + return err; + } + + return 0; +} + diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index feb30f9c1716..6fe3ab582ec6 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -33,7 +33,7 @@ struct snd_usb_audio { wait_queue_head_t shutdown_wait; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */ - + unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */ int num_interfaces; int num_suspended_intf; int sample_rate_read_error; @@ -98,6 +98,7 @@ enum quirk_type { QUIRK_AUDIO_EDIROL_UAXX, QUIRK_AUDIO_ALIGN_TRANSFER, QUIRK_AUDIO_STANDARD_MIXER, + QUIRK_SETUP_FMT_AFTER_RESUME, QUIRK_TYPE_COUNT }; @@ -120,5 +121,6 @@ int snd_usb_lock_shutdown(struct snd_usb_audio *chip); void snd_usb_unlock_shutdown(struct snd_usb_audio *chip); extern bool snd_usb_use_vmalloc; +extern bool snd_usb_skip_validation; #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index e82c5236482d..f86f7a61fb36 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -40,13 +40,13 @@ static int snd_us122l_card_used[SNDRV_CARDS]; static int us122l_create_usbmidi(struct snd_card *card) { - static struct snd_usb_midi_endpoint_info quirk_data = { + static const struct snd_usb_midi_endpoint_info quirk_data = { .out_ep = 4, .in_ep = 3, .out_cables = 0x001, .in_cables = 0x001 }; - static struct snd_usb_audio_quirk quirk = { + static const struct snd_usb_audio_quirk quirk = { .vendor_name = "US122L", .product_name = NAME_ALLCAPS, .ifnum = 1, @@ -62,13 +62,13 @@ static int us122l_create_usbmidi(struct snd_card *card) static int us144_create_usbmidi(struct snd_card *card) { - static struct snd_usb_midi_endpoint_info quirk_data = { + static const struct snd_usb_midi_endpoint_info quirk_data = { .out_ep = 4, .in_ep = 3, .out_cables = 0x001, .in_cables = 0x001 }; - static struct snd_usb_audio_quirk quirk = { + static const struct snd_usb_audio_quirk quirk = { .vendor_name = "US144", .product_name = NAME_ALLCAPS, .ifnum = 0, diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index d1caa8ed9e68..22412cd69e98 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -94,7 +94,7 @@ static __poll_t snd_us428ctls_poll(struct snd_hwdep *hw, struct file *file, poll static int snd_usX2Y_hwdep_dsp_status(struct snd_hwdep *hw, struct snd_hwdep_dsp_status *info) { - static char *type_ids[USX2Y_TYPE_NUMS] = { + static const char * const type_ids[USX2Y_TYPE_NUMS] = { [USX2Y_TYPE_122] = "us122", [USX2Y_TYPE_224] = "us224", [USX2Y_TYPE_428] = "us428", @@ -119,33 +119,33 @@ static int snd_usX2Y_hwdep_dsp_status(struct snd_hwdep *hw, info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code if (us428->chip_status & USX2Y_STAT_CHIP_INIT) info->chip_ready = 1; - info->version = USX2Y_DRIVER_VERSION; + info->version = USX2Y_DRIVER_VERSION; return 0; } static int usX2Y_create_usbmidi(struct snd_card *card) { - static struct snd_usb_midi_endpoint_info quirk_data_1 = { + static const struct snd_usb_midi_endpoint_info quirk_data_1 = { .out_ep = 0x06, .in_ep = 0x06, .out_cables = 0x001, .in_cables = 0x001 }; - static struct snd_usb_audio_quirk quirk_1 = { + static const struct snd_usb_audio_quirk quirk_1 = { .vendor_name = "TASCAM", .product_name = NAME_ALLCAPS, .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = &quirk_data_1 }; - static struct snd_usb_midi_endpoint_info quirk_data_2 = { + static const struct snd_usb_midi_endpoint_info quirk_data_2 = { .out_ep = 0x06, .in_ep = 0x06, .out_cables = 0x003, .in_cables = 0x003 }; - static struct snd_usb_audio_quirk quirk_2 = { + static const struct snd_usb_audio_quirk quirk_2 = { .vendor_name = "TASCAM", .product_name = "US428", .ifnum = 0, @@ -154,7 +154,7 @@ static int usX2Y_create_usbmidi(struct snd_card *card) }; struct usb_device *dev = usX2Y(card)->dev; struct usb_interface *iface = usb_ifnum_to_if(dev, 0); - struct snd_usb_audio_quirk *quirk = + const struct snd_usb_audio_quirk *quirk = le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ? &quirk_2 : &quirk_1; diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 89fa287678fc..772f6f3ccbb1 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -549,7 +549,7 @@ static int snd_usX2Y_pcm_trigger(struct snd_pcm_substream *substream, int cmd) * if sg buffer is supported on the later version of alsa, we'll follow * that. */ -static struct s_c2 +static const struct s_c2 { char c1, c2; } @@ -589,7 +589,7 @@ static struct s_c2 { 0x18, 0x7C}, { 0x18, 0x7E} }; -static struct s_c2 SetRate48000[] = +static const struct s_c2 SetRate48000[] = { { 0x14, 0x09}, // this line sets 48000, well actually a little less { 0x18, 0x40}, // only tascam / frontier design knows the further lines ....... @@ -642,7 +642,7 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate) int err = 0, i; struct snd_usX2Y_urbSeq *us = NULL; int *usbdata = NULL; - struct s_c2 *ra = rate == 48000 ? SetRate48000 : SetRate44100; + const struct s_c2 *ra = rate == 48000 ? SetRate48000 : SetRate44100; if (usX2Y->rate != rate) { us = kzalloc(sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS, GFP_KERNEL); @@ -764,14 +764,6 @@ static int snd_usX2Y_pcm_hw_params(struct snd_pcm_substream *substream, } } - err = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (err < 0) { - snd_printk(KERN_ERR "snd_pcm_lib_malloc_pages(%p, %i) returned %i\n", - substream, params_buffer_bytes(hw_params), err); - goto error; - } - error: mutex_unlock(&usX2Y(card)->pcm_mutex); return err; @@ -806,7 +798,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream) } } mutex_unlock(&subs->usX2Y->pcm_mutex); - return snd_pcm_lib_free_pages(substream); + return 0; } /* * prepare callback @@ -846,7 +838,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream) return err; } -static struct snd_pcm_hardware snd_usX2Y_2c = +static const struct snd_pcm_hardware snd_usX2Y_2c = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -901,7 +893,6 @@ static const struct snd_pcm_ops snd_usX2Y_pcm_ops = { .open = snd_usX2Y_pcm_open, .close = snd_usX2Y_pcm_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usX2Y_pcm_hw_params, .hw_free = snd_usX2Y_pcm_hw_free, .prepare = snd_usX2Y_pcm_prepare, @@ -968,16 +959,16 @@ static int usX2Y_audio_stream_new(struct snd_card *card, int playback_endpoint, sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usX2Y(card)->pcm_devs); if (playback_endpoint) { - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - 64*1024, 128*1024); + snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, + 64*1024, 128*1024); } - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - 64*1024, 128*1024); + snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, + 64*1024, 128*1024); usX2Y(card)->pcm_devs++; return 0; diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index ac8960b6b299..8253669c6a7d 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -376,7 +376,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream) } } mutex_unlock(&subs->usX2Y->pcm_mutex); - return snd_pcm_lib_free_pages(substream); + return 0; } static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs) @@ -528,7 +528,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream) return err; } -static struct snd_pcm_hardware snd_usX2Y_4c = +static const struct snd_pcm_hardware snd_usX2Y_4c = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -581,7 +581,6 @@ static const struct snd_pcm_ops snd_usX2Y_usbpcm_ops = { .open = snd_usX2Y_usbpcm_open, .close = snd_usX2Y_usbpcm_close, - .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usX2Y_pcm_hw_params, .hw_free = snd_usX2Y_usbpcm_hw_free, .prepare = snd_usX2Y_usbpcm_prepare, @@ -726,14 +725,14 @@ int usX2Y_hwdep_pcm_new(struct snd_card *card) pcm->info_flags = 0; sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio"); - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - 64*1024, 128*1024); - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - 64*1024, 128*1024); + snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, + 64*1024, 128*1024); + snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, + 64*1024, 128*1024); return 0; } diff --git a/sound/usb/validate.c b/sound/usb/validate.c new file mode 100644 index 000000000000..6fe206f6e911 --- /dev/null +++ b/sound/usb/validate.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Validation of USB-audio class descriptors +// + +#include <linux/init.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/midi.h> +#include "usbaudio.h" +#include "helper.h" + +struct usb_desc_validator { + unsigned char protocol; + unsigned char type; + bool (*func)(const void *p, const struct usb_desc_validator *v); + size_t size; +}; + +#define UAC_VERSION_ALL (unsigned char)(-1) + +/* UAC1 only */ +static bool validate_uac1_header(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac1_ac_header_descriptor *d = p; + + return d->bLength >= sizeof(*d) && + d->bLength >= sizeof(*d) + d->bInCollection; +} + +/* for mixer unit; covering all UACs */ +static bool validate_mixer_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac_mixer_unit_descriptor *d = p; + size_t len; + + if (d->bLength < sizeof(*d) || !d->bNrInPins) + return false; + len = sizeof(*d) + d->bNrInPins; + /* We can't determine the bitmap size only from this unit descriptor, + * so just check with the remaining length. + * The actual bitmap is checked at mixer unit parser. + */ + switch (v->protocol) { + case UAC_VERSION_1: + default: + len += 2 + 1; /* wChannelConfig, iChannelNames */ + /* bmControls[n*m] */ + len += 1; /* iMixer */ + break; + case UAC_VERSION_2: + len += 4 + 1; /* bmChannelConfig, iChannelNames */ + /* bmMixerControls[n*m] */ + len += 1 + 1; /* bmControls, iMixer */ + break; + case UAC_VERSION_3: + len += 2; /* wClusterDescrID */ + /* bmMixerControls[n*m] */ + break; + } + return d->bLength >= len; +} + +/* both for processing and extension units; covering all UACs */ +static bool validate_processing_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac_processing_unit_descriptor *d = p; + const unsigned char *hdr = p; + size_t len, m; + + if (d->bLength < sizeof(*d)) + return false; + len = sizeof(*d) + d->bNrInPins; + if (d->bLength < len) + return false; + switch (v->protocol) { + case UAC_VERSION_1: + default: + /* bNrChannels, wChannelConfig, iChannelNames */ + len += 1 + 2 + 1; + if (d->bLength < len + 1) /* bControlSize */ + return false; + m = hdr[len]; + len += 1 + m + 1; /* bControlSize, bmControls, iProcessing */ + break; + case UAC_VERSION_2: + /* bNrChannels, bmChannelConfig, iChannelNames */ + len += 1 + 4 + 1; + if (v->type == UAC2_PROCESSING_UNIT_V2) + len += 2; /* bmControls -- 2 bytes for PU */ + else + len += 1; /* bmControls -- 1 byte for EU */ + len += 1; /* iProcessing */ + break; + case UAC_VERSION_3: + /* wProcessingDescrStr, bmControls */ + len += 2 + 4; + break; + } + if (d->bLength < len) + return false; + + switch (v->protocol) { + case UAC_VERSION_1: + default: + if (v->type == UAC1_EXTENSION_UNIT) + return true; /* OK */ + switch (le16_to_cpu(d->wProcessType)) { + case UAC_PROCESS_UP_DOWNMIX: + case UAC_PROCESS_DOLBY_PROLOGIC: + if (d->bLength < len + 1) /* bNrModes */ + return false; + m = hdr[len]; + len += 1 + m * 2; /* bNrModes, waModes(n) */ + break; + default: + break; + } + break; + case UAC_VERSION_2: + if (v->type == UAC2_EXTENSION_UNIT_V2) + return true; /* OK */ + switch (le16_to_cpu(d->wProcessType)) { + case UAC2_PROCESS_UP_DOWNMIX: + case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */ + if (d->bLength < len + 1) /* bNrModes */ + return false; + m = hdr[len]; + len += 1 + m * 4; /* bNrModes, daModes(n) */ + break; + default: + break; + } + break; + case UAC_VERSION_3: + if (v->type == UAC3_EXTENSION_UNIT) { + len += 2; /* wClusterDescrID */ + break; + } + switch (le16_to_cpu(d->wProcessType)) { + case UAC3_PROCESS_UP_DOWNMIX: + if (d->bLength < len + 1) /* bNrModes */ + return false; + m = hdr[len]; + len += 1 + m * 2; /* bNrModes, waClusterDescrID(n) */ + break; + case UAC3_PROCESS_MULTI_FUNCTION: + len += 2 + 4; /* wClusterDescrID, bmAlgorighms */ + break; + default: + break; + } + break; + } + if (d->bLength < len) + return false; + + return true; +} + +/* both for selector and clock selector units; covering all UACs */ +static bool validate_selector_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac_selector_unit_descriptor *d = p; + size_t len; + + if (d->bLength < sizeof(*d)) + return false; + len = sizeof(*d) + d->bNrInPins; + switch (v->protocol) { + case UAC_VERSION_1: + default: + len += 1; /* iSelector */ + break; + case UAC_VERSION_2: + len += 1 + 1; /* bmControls, iSelector */ + break; + case UAC_VERSION_3: + len += 4 + 2; /* bmControls, wSelectorDescrStr */ + break; + } + return d->bLength >= len; +} + +static bool validate_uac1_feature_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac_feature_unit_descriptor *d = p; + + if (d->bLength < sizeof(*d) || !d->bControlSize) + return false; + /* at least bmaControls(0) for master channel + iFeature */ + return d->bLength >= sizeof(*d) + d->bControlSize + 1; +} + +static bool validate_uac2_feature_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac2_feature_unit_descriptor *d = p; + + if (d->bLength < sizeof(*d)) + return false; + /* at least bmaControls(0) for master channel + iFeature */ + return d->bLength >= sizeof(*d) + 4 + 1; +} + +static bool validate_uac3_feature_unit(const void *p, + const struct usb_desc_validator *v) +{ + const struct uac3_feature_unit_descriptor *d = p; + + if (d->bLength < sizeof(*d)) + return false; + /* at least bmaControls(0) for master channel + wFeatureDescrStr */ + return d->bLength >= sizeof(*d) + 4 + 2; +} + +static bool validate_midi_out_jack(const void *p, + const struct usb_desc_validator *v) +{ + const struct usb_midi_out_jack_descriptor *d = p; + + return d->bLength >= sizeof(*d) && + d->bLength >= sizeof(*d) + d->bNrInputPins * 2; +} + +#define FIXED(p, t, s) { .protocol = (p), .type = (t), .size = sizeof(s) } +#define FUNC(p, t, f) { .protocol = (p), .type = (t), .func = (f) } + +static const struct usb_desc_validator audio_validators[] = { + /* UAC1 */ + FUNC(UAC_VERSION_1, UAC_HEADER, validate_uac1_header), + FIXED(UAC_VERSION_1, UAC_INPUT_TERMINAL, + struct uac_input_terminal_descriptor), + FIXED(UAC_VERSION_1, UAC_OUTPUT_TERMINAL, + struct uac1_output_terminal_descriptor), + FUNC(UAC_VERSION_1, UAC_MIXER_UNIT, validate_mixer_unit), + FUNC(UAC_VERSION_1, UAC_SELECTOR_UNIT, validate_selector_unit), + FUNC(UAC_VERSION_1, UAC_FEATURE_UNIT, validate_uac1_feature_unit), + FUNC(UAC_VERSION_1, UAC1_PROCESSING_UNIT, validate_processing_unit), + FUNC(UAC_VERSION_1, UAC1_EXTENSION_UNIT, validate_processing_unit), + + /* UAC2 */ + FIXED(UAC_VERSION_2, UAC_HEADER, struct uac2_ac_header_descriptor), + FIXED(UAC_VERSION_2, UAC_INPUT_TERMINAL, + struct uac2_input_terminal_descriptor), + FIXED(UAC_VERSION_2, UAC_OUTPUT_TERMINAL, + struct uac2_output_terminal_descriptor), + 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 */ + 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, + struct uac_clock_source_descriptor), + FUNC(UAC_VERSION_2, UAC2_CLOCK_SELECTOR, validate_selector_unit), + FIXED(UAC_VERSION_2, UAC2_CLOCK_MULTIPLIER, + struct uac_clock_multiplier_descriptor), + /* UAC_VERSION_2, UAC2_SAMPLE_RATE_CONVERTER: not implemented yet */ + + /* UAC3 */ + FIXED(UAC_VERSION_2, UAC_HEADER, struct uac3_ac_header_descriptor), + FIXED(UAC_VERSION_3, UAC_INPUT_TERMINAL, + struct uac3_input_terminal_descriptor), + FIXED(UAC_VERSION_3, UAC_OUTPUT_TERMINAL, + struct uac3_output_terminal_descriptor), + /* 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_PROCESSING_UNIT, validate_processing_unit), + FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit), + FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE, + struct uac3_clock_source_descriptor), + FUNC(UAC_VERSION_3, UAC3_CLOCK_SELECTOR, validate_selector_unit), + FIXED(UAC_VERSION_3, UAC3_CLOCK_MULTIPLIER, + struct uac3_clock_multiplier_descriptor), + /* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */ + /* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */ + { } /* terminator */ +}; + +static const struct usb_desc_validator midi_validators[] = { + FIXED(UAC_VERSION_ALL, USB_MS_HEADER, + struct usb_ms_header_descriptor), + FIXED(UAC_VERSION_ALL, USB_MS_MIDI_IN_JACK, + struct usb_midi_in_jack_descriptor), + FUNC(UAC_VERSION_ALL, USB_MS_MIDI_OUT_JACK, + validate_midi_out_jack), + { } /* terminator */ +}; + + +/* Validate the given unit descriptor, return true if it's OK */ +static bool validate_desc(unsigned char *hdr, int protocol, + const struct usb_desc_validator *v) +{ + if (hdr[1] != USB_DT_CS_INTERFACE) + return true; /* don't care */ + + for (; v->type; v++) { + if (v->type == hdr[2] && + (v->protocol == UAC_VERSION_ALL || + v->protocol == protocol)) { + if (v->func) + return v->func(hdr, v); + /* check for the fixed size */ + return hdr[0] >= v->size; + } + } + + return true; /* not matching, skip validation */ +} + +bool snd_usb_validate_audio_desc(void *p, int protocol) +{ + unsigned char *c = p; + bool valid; + + valid = validate_desc(p, protocol, audio_validators); + if (!valid && snd_usb_skip_validation) { + print_hex_dump(KERN_ERR, "USB-audio: buggy audio desc: ", + DUMP_PREFIX_NONE, 16, 1, c, c[0], true); + valid = true; + } + return valid; +} + +bool snd_usb_validate_midi_desc(void *p) +{ + unsigned char *c = p; + bool valid; + + valid = validate_desc(p, UAC_VERSION_1, midi_validators); + if (!valid && snd_usb_skip_validation) { + print_hex_dump(KERN_ERR, "USB-audio: buggy midi desc: ", + DUMP_PREFIX_NONE, 16, 1, c, c[0], true); + valid = true; + } + return valid; +} |