// SPDX-License-Identifier: GPL-2.0-or-later /* * Scarlett 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>
*/
/* * Rewritten and extended to support more models, e.g. Scarlett 18i8. * * Auto-detection via UAC2 is not feasible to properly discover the vast * majority of features. It's related to both Linux/ALSA's UAC2 as well as * Focusrite's implementation of it. Eventually quirks may be sufficient but * right now it's a major headache to work around these things. * * NB. Neither the OSX nor the win driver provided by Focusrite performs * discovery, they seem to operate the same as this driver.
*/
/* Mixer Interface for the Focusrite Scarlett 18i6 audio interface. * * The protocol was reverse engineered by looking at communication between * Scarlett MixControl (v 1.2.128.0) and the Focusrite(R) Scarlett 18i6 * (firmware v305) using wireshark and usbmon in January 2013. * Extended in July 2013. * * this mixer gives complete access to all features of the device: * - change Impedance of inputs (Line-in, Mic / Instrument, Hi-Z) * - select clock source * - dynamic input to mixer-matrix assignment * - 18 x 6 mixer-matrix gain stages * - bus routing & volume control * - automatic re-initialization on connect if device was power-cycled * * USB URB commands overview (bRequest = 0x01 = UAC2_CS_CUR) * wIndex * 0x01 Analog Input line/instrument impedance switch, wValue=0x0901 + * channel, data=Line/Inst (2bytes) * pad (-10dB) switch, wValue=0x0b01 + channel, data=Off/On (2bytes) * ?? wValue=0x0803/04, ?? (2bytes) * 0x0a Master Volume, wValue=0x0200+bus[0:all + only 1..4?] data(2bytes) * Bus Mute/Unmute wValue=0x0100+bus[0:all + only 1..4?], data(2bytes) * 0x28 Clock source, wValue=0x0100, data={1:int,2:spdif,3:adat} (1byte) * 0x29 Set Sample-rate, wValue=0x0100, data=sample-rate(4bytes) * 0x32 Mixer mux, wValue=0x0600 + mixer-channel, data=input-to-connect(2bytes) * 0x33 Output mux, wValue=bus, data=input-to-connect(2bytes) * 0x34 Capture mux, wValue=0...18, data=input-to-connect(2bytes) * 0x3c Matrix Mixer gains, wValue=mixer-node data=gain(2bytes) * ?? [sometimes](4bytes, e.g 0x000003be 0x000003bf ...03ff) * * USB reads: (i.e. actually issued by original software) * 0x01 wValue=0x0901+channel (1byte!!), wValue=0x0b01+channed (1byte!!) * 0x29 wValue=0x0100 sample-rate(4bytes) * wValue=0x0200 ?? 1byte (only once) * 0x2a wValue=0x0100 ?? 4bytes, sample-rate2 ?? * * USB reads with bRequest = 0x03 = UAC2_CS_MEM * 0x3c wValue=0x0002 1byte: sync status (locked=1) * wValue=0x0000 18*2byte: peak meter (inputs) * wValue=0x0001 8(?)*2byte: peak meter (mix) * wValue=0x0003 6*2byte: peak meter (pcm/daw) * * USB write with bRequest = 0x03 * 0x3c Save settings to hardware: wValue=0x005a, data=0xa5 * * * <ditaa> * /--------------\ 18chn 6chn /--------------\ * | Hardware in +--+-------\ /------+--+ ALSA PCM out | * \--------------/ | | | | \--------------/ * | | | | * | v v | * | +---------------+ | * | \ Matrix Mux / | * | +-----+-----+ | * | | | * | | 18chn | * | v | * | +-----------+ | * | | Mixer | | * | | Matrix | | * | | | | * | | 18x6 Gain | | * | | stages | | * | +-----+-----+ | * | | | * | | | * | 18chn | 6chn | 6chn * v v v * ========================= * +---------------+ +--—------------+ * \ Output Mux / \ Capture Mux / * +-----+-----+ +-----+-----+ * | | * | 6chn | * v | * +-------------+ | * | Master Gain | | * +------+------+ | * | | * | 6chn | 18chn * | (3 stereo pairs) | * /--------------\ | | /--------------\ * | Hardware out |<--/ \-->| ALSA PCM in | * \--------------/ \--------------/ * </ditaa> *
*/
staticint scarlett_ctl_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
{ struct usb_mixer_elem_info *elem = kctl->private_data; int i, changed = 0; int err, oval, val;
for (i = 0; i < elem->channels; i++) {
err = snd_usb_get_cur_mix_value(elem, i, i, &oval); if (err < 0) return err;
val = ucontrol->value.integer.value[i] -
SND_SCARLETT_LEVEL_BIAS;
val = val * 256; if (oval != val) {
err = snd_usb_set_cur_mix_value(elem, i, i, val); if (err < 0) return err;
changed = 1;
}
}
return changed;
}
staticvoid scarlett_generate_name(int i, char *dst, size_t size, int offsets[])
{ if (i > offsets[SCARLETT_OFFSET_MIX])
scnprintf(dst, size, "Mix %c", 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); elseif (i > offsets[SCARLETT_OFFSET_ADAT])
scnprintf(dst, size, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); elseif (i > offsets[SCARLETT_OFFSET_SPDIF])
scnprintf(dst, size, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); elseif (i > offsets[SCARLETT_OFFSET_ANALOG])
scnprintf(dst, size, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); elseif (i > offsets[SCARLETT_OFFSET_PCM])
scnprintf(dst, size, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); else
scnprintf(dst, size, "Off");
}
if (uinfo->value.enumerated.item >= items)
uinfo->value.enumerated.item = items - 1;
/* generate name dynamically based on item number and offset info */
scarlett_generate_name(uinfo->value.enumerated.item,
uinfo->value.enumerated.name, sizeof(uinfo->value.enumerated.name),
opt->offsets);
/* * Create and initialize a mixer for the Focusrite(R) Scarlett
*/ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer)
{ int err, i, o; char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; conststruct scarlett_device_info *info; struct usb_mixer_elem_info *elem; staticchar sample_rate_buffer[4] = { '\x80', '\xbb', '\x00', '\x00' };
/* only use UAC_VERSION_2 */ if (!mixer->protocol) return 0;
switch (mixer->chip->usb_id) { case USB_ID(0x1235, 0x8012):
info = &s6i6_info; break; case USB_ID(0x1235, 0x8002):
info = &s8i6_info; break; case USB_ID(0x1235, 0x8004):
info = &s18i6_info; break; case USB_ID(0x1235, 0x8014):
info = &s18i8_info; break; case USB_ID(0x1235, 0x800c):
info = &s18i20_info; break; default: /* device not (yet) supported */ return -EINVAL;
}
/* generic function to create controls */
err = scarlett_controls_create_generic(mixer, info); if (err < 0) return err;
/* setup matrix controls */ for (i = 0; i < info->matrix_in; i++) {
snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route",
i+1);
err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
scarlett_ctl_enum_resume, 0x32,
0x06, i, USB_MIXER_S16, 1, mx,
&info->opt_matrix, &elem); if (err < 0) return err;
for (o = 0; o < info->matrix_out; o++) {
scnprintf(mx, sizeof(mx), "Matrix %02d Mix %c Playback Volume", i+1,
o+'A');
err = add_new_ctl(mixer, &usb_scarlett_ctl,
scarlett_ctl_resume, 0x3c, 0x00,
(i << 3) + (o & 0x07), USB_MIXER_S16,
1, mx, NULL, &elem); if (err < 0) return err;
}
}
for (i = 0; i < info->input_len; i++) {
snprintf(mx, sizeof(mx), "Input Source %02d Capture Route",
i+1);
err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum,
scarlett_ctl_enum_resume, 0x34,
0x00, i, USB_MIXER_S16, 1, mx,
&info->opt_master, &elem); if (err < 0) return err;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.