[alsa-devel] [PATCH 4/4 v4] ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20
Chris J Arges
chris.j.arges at canonical.com
Tue Nov 4 20:56:15 CET 2014
On Tue, Nov 04, 2014 at 03:00:46PM +0100, Takashi Iwai wrote:
> At Tue, 04 Nov 2014 14:16:13 +0100,
> Tobias Hoffmann wrote:
> >
> > > +#define TXT_OFF "Off"
> > > +#define TXT_PCM(num) "PCM " #num
> > > +#define TXT_ANALOG(num) "Analog " #num
> > > +#define TXT_SPDIF(num) "SPDIF " #num
> > > +#define TXT_ADAT(num) "ADAT " #num
> > > +#define TXT_MIX(letter) "MIX " #letter
> >
> > My reasoning for using:
> > static const char txtOff[] = "Off",
> > txtPcm1[] = "PCM 1", txtPcm2[] = "PCM 2",
> > txtPcm3[] = "PCM 3", txtPcm4[] = "PCM 4", [...]
> > instead of repeating the strings for each device (or using macros that
> > expand to the same strings) was that the final .o / .ko would contain
> > the string data only once.
>
> The compiler should be clever enough.
>
> > But maybe (as Clemens suggested) the strings *for .opt_master and
> > .opt_matrix* should just be created dynamically in
> > scarlett_ctl_enum_info, instead of using snd_ctl_enum_info.
> > I want to point out that scarlett_device_info already contains the
> > necessary information to do this:
> >
> > .matrix_out = 6,
> > ...
> > .pcm_start = 0, // pcm(1) .. pcm(analog_start-pcm_start)=pcm(12)
> > .analog_start = 12, // analog(1) .. analog(4)
> > .spdif_start = 16, // spdif(1) .. spdif(2)
> > .adat_start = 18, // adat(1) .. adat(0) [i.e. no adat]
> > .mix_start = 18, // mix('A') .. mix('F')=mix('A' + matrix_out)
> >
> >
> >
> > and Off(-1). Only elem->private_data has to be of type
> > scarlett_mixer_elem_enum_info for the usual enums (impedance, pad, ...),
> > but of type scarlett_device_info for the master and mixer enums...
>
> Yeah, that would be also cleaner.
>
>
> > Some more comments inline:
> >
> > On 04/11/14 11:18, Takashi Iwai wrote:
> > > The new patch looks almost good. A remaining concern is:
> > >> +static const struct scarlett_mixer_elem_enum_info opt_save = {
> > >> + .start = 0,
> > >> + .len = 2,
> > >> + .names = (const char * const[]){
> > >> + "---", "Save"
> > > This isn't quite intuitive. And I think this is an abuse of ctl
> > > enum (see below).
> > It's a hack to expose the "Save to hardware" functionality without
> > requiring a special mixer application. Robin's original patch already
> > contained this (ab)use.
>
> Heh, the history can't be an excuse to continue the abuse :)
>
> > > Also...
> > >
> > >> +static int scarlett_ctl_enum_get(struct snd_kcontrol *kctl,
> > >> + struct snd_ctl_elem_value *ucontrol)
> > >> +{
> > >> + struct usb_mixer_elem_info *elem = kctl->private_data;
> > >> + struct scarlett_mixer_elem_enum_info *opt = elem->private_data;
> > >> + int err, val;
> > >> +
> > >> + err = snd_usb_get_cur_mix_value(elem, 0, 0,&val);
> > >> + if (err< 0)
> > >> + return err;
> > >> +
> > >> + if ((opt->start == -1)&& (val> opt->len)) /*>= 0x20 */
> > >> + val = 0;
> > >> + else
> > >> + val = clamp(val - opt->start, 0, opt->len-1);
> > > Is the if condition above really correct? It's not obvious to me what
> > > this really checks.
> >
> > (opt->start == -1) is used in enums that control the routing, to
> > represent the "Off"-value.
> > But the device uses number_of_master_enum_values+1 (even for the matrix
> > enum) as "Off".
> >
> > That would make "Off" the last enum value in Mixer applications, which
> > is undesired (and, for the matrix enums, this still results in a gap
> > between last_matrix_enum_index and Off=last_mixer_enum_index).
> >
> > scarlett_ctl_enum_put did contain the inverse logic (before cleanup):
> >
> > > #if 0 // TODO?
> > > if (val == -1) {
> > > val = elem->enum->len + 1; /* only true for master,
> > > not for mixer [also master must be used] */
> > > // ... or? > 0x20, 18i8: 0x22
> > > } else
> > > #endif
> > > val = val + elem->opt->start;
> >
> > It is commented out, because the device also recognized val = -1 + -1;
> > as "Off", without the need to somehow expose the device-specific
> > enum->len of .opt_master to all the .opt_matrix controls.
> > It's a bit dirty, but it works...
>
> Oh, no this is too tricky. If you want to keep this coding, you have
> to put this whole comment in the code. Otherwise no reader
> understands the code correctly.
>
> Please, don't play with too much tricks. This is no hot path. The
> code size and speed don't matter so much. The readability has to be
> cared at most, instead.
>
>
> > > Now back to "save to hw" ctl:
> > >
> > >> +static int scarlett_ctl_save_get(struct snd_kcontrol *kctl,
> > >> + struct snd_ctl_elem_value *ucontrol)
> > >> +{
> > >> + ucontrol->value.enumerated.item[0] = 0;
> > >> + return 0;
> > >> +}
> > > So, here is the problem. You want to use this ctl to trigger
> > > something. This is no correct behavior for an enum ctl.
> > > If the put callback succeeds, the value should be stored.
> > > (Of course, then it won't work as you expected.)
> > >
> > > What actually this control does? Why it can't be done always
> > > (transparently)?
> >
> > What it does is it store the current settings in non-volatile memory for
> > standalone operation (i.e. USB not connected). It's the device
> > configuration after power-on.
> >
> > Loading this driver then reinitalizes the device to basically zero
> > values (userspace "acontrol restore" will finally set the device to what
> > the user expects).
> > The zeroing is unfortunately necessary, because the device does not
> > support reading back certain mixer values after power-on (only garbage
> > is returned). After initialization, only cval->cache_val is ever
> > returned by snd_usb_get_cur_mix_value.
> >
> >
> > >> +static int scarlett_ctl_save_put(struct snd_kcontrol *kctl,
> > >> + struct snd_ctl_elem_value *ucontrol)
> > >> +{
> > >> + struct usb_mixer_elem_info *elem = kctl->private_data;
> > >> + struct snd_usb_audio *chip = elem->mixer->chip;
> > >> + char buf[] = { 0x00, 0xa5 };
> > >> + int err;
> > >> +
> > >> + if (ucontrol->value.enumerated.item[0]> 0) {
> > >> + err = snd_usb_ctl_msg(chip->dev,
> > >> + usb_sndctrlpipe(chip->dev, 0), UAC2_CS_MEM,
> > >> + USB_RECIP_INTERFACE | USB_TYPE_CLASS |
> > >> + USB_DIR_OUT, 0x005a, snd_usb_ctrl_intf(chip) |
> > >> + (0x3c<< 8), buf, 2);
> > >> + if (err< 0)
> > >> + return err;
> > >> +
> > >> + usb_audio_info(elem->mixer->chip,
> > >> + "scarlett: saved settings to hardware.\n");
> > > Using *_info() here is rather annoying, it may spew too many random
> > > messages.
> > OTOH, saving the settings to hardware should be a rare operation (the
> > non-volatile memory might also be designed to withstand only a limited
> > amount of read-write cycles).
>
> If so, implementing it as a mixer ctl is a wrong design. I'd suggest
> rather to drop whole this stuff at first. If really inevitably
> needed, you can implement it in a different way.
>
>
> thanks,
>
> Takashi
Takashi,
I'll drop it for the initial patchset. Clemens mentioned implementing it as a
mixer control that allows no access but a TLV_COMMAND. Perhaps that could be a
start and then writing support in any userspace applications to expose that
'button' control correctly.
--chris
More information about the Alsa-devel
mailing list