On Wed, 25 Dec 2019 03:34:24 +0100, mickflemm@gmail.com wrote:
This patch adds support for Presonus Studio 1810c, a usb interface that's UAC2 compliant with a few quirks and a few extra hw-specific controls. I've tested all 3 altsettings and the added switch controls and they work as expected.
More infos on the card: https://www.presonus.com/products/Studio-1810c
Note that this work is based on packet inspection with usbmon. I just wanted to get this card to work for using it on our open-source radio station: https://github.com/UoC-Radio
Signed-off-by: Nick Kossifidis mickflemm@gmail.com
Thanks for the patch.
In addition to the issues kbuild bot suggested, below are my quick review comments:
diff --git a/sound/usb/format.c b/sound/usb/format.c index d79db7130..edf3f2a55 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -262,6 +262,23 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, }
for (rate = min; rate <= max; rate += res) {
/*
* Presonus Studio 1810c anounces invalid
* sampling rates for its streams.
*/
if (chip->usb_id == USB_ID(0x0194f, 0x010c) &&
((rate > 48000 && fp->altsetting == 1) ||
((rate < 88200 || rate > 96000)
&& fp->altsetting == 2) ||
((rate < 176400 || rate > 192000)
&& fp->altsetting == 3))) {
if (res)
continue;
else
break;
}
It's hard to imagine what result this would lead to, because the conditions are so complex. Maybe better to prepare a fixed table instead? Or, can we simply take a fixed quirk?
diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c new file mode 100644 index 000000000..ff86e4aab --- /dev/null +++ b/sound/usb/mixer_s1810c.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Presonus Studio 1810c driver for ALSA
- Copyright (C) 2019 Nick Kossifidis mickflemm@gmail.com
- Based on reverse engineering of the communication protocol
- between the windows driver / Univeral Control (UC) program
- and the device, through usbmon.
- For now this bypasses the mixer, with all channels split,
- so that the software can mix with greater flexibility.
- It also adds controls for the 4 buttons on the front of
- the device.
- */
Usually place a blank line here.
+#include <linux/usb.h> +#include <linux/usb/audio-v2.h> +#include <sound/control.h> +#include <linux/slab.h>
The common practice is to group linux/*.h first, followed by sound/*.h. And <sound/core.h> is almost always needed before other sound/*.h.
+struct s1810c_ctl_packet {
- uint32_t a;
- uint32_t b;
- uint32_t fixed1;
- uint32_t fixed2;
- uint32_t c;
- uint32_t d;
- uint32_t e;
Use u32, u16 or such types instead of uint*_t. It's the standard type for kernel codes. Also applied in other places in the patch, too.
+static int +snd_sc1810c_get_status_field(struct usb_device *dev,
uint32_t *field, int field_idx, uint16_t *seqnum)
+{
- struct s1810c_state_packet pkt_out = { 0 };
- struct s1810c_state_packet pkt_in = { 0 };
- int ret = 0;
- pkt_out.fields[SC1810C_STATE_F1_IDX] = SC1810C_SET_STATE_F1;
- pkt_out.fields[SC1810C_STATE_F2_IDX] = SC1810C_SET_STATE_F2;
- ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
SC1810C_SET_STATE_REQ,
SC1810C_SET_STATE_REQTYPE,
(*seqnum), 0, &pkt_out, sizeof(pkt_out));
Avoid unnecessary parentheses around *seqnum. Ditto for other possible places.
+static int +snd_s1810c_switch_set(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_elem)
+{
- struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- struct usb_mixer_interface *mixer = list->mixer;
- uint32_t curval = 0;
- uint32_t newval = (uint32_t) ctl_elem->value.integer.value[0];
- int ret = 0;
- ret = snd_s1810c_get_switch_state(mixer, kctl, &curval);
- if (ret < 0)
return 0;
- if (curval == newval)
return 0;
- kctl->private_value &= ~(0x1 << 16);
- kctl->private_value |= (unsigned int)(newval & 0x1) << 16;
- ret = snd_s1810c_set_switch_state(mixer, kctl);
Hm, this can be racy. The control get/put isn't protected, so you might get the inconsistency here when multiple kctls are accessed concurrently.
+static int +snd_s1810c_line_sw_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
+{
- static const char *const texts[2] = { "Preamp on (Mic/Inst)",
"Preamp off (Line in)"
- };
Better to put "Preamp on..." item in the next line.
+static struct snd_kcontrol_new snd_s1810c_line_sw = {
This (and other following definitions) can be const?
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Line 1/2 source type",
The control name should consist of words starting with a capital letter, so it should be "Line 1/2 Source Type". Also, usually a control name needs a standard suffix. Though, this has a special return enum type, so it can be OK as is.
However...
- .info = snd_s1810c_line_sw_info,
- .get = snd_s1810c_switch_get,
- .put = snd_s1810c_switch_set,
... this shows that the combination is invalid. The enum type doesn't get/put the values in integer fields. It's incompatible.
- .private_value = (SC1810C_STATE_LINE_SW | SC1810C_CTL_LINE_SW << 8)
+};
+static struct snd_kcontrol_new snd_s1810c_mute_sw = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Mute Main Out",
... and this one, for example, should deserve for "Switch" suffix, as it's a standard boolean switch (which use integer fields unlike enum).
+/* Entry point, called from mixer_quirks.c */ +int snd_sc1810_init_mixer(struct usb_mixer_interface *mixer) +{
- struct s1810_mixer_state *private = NULL;
- struct snd_usb_audio *chip = mixer->chip;
- struct usb_device *dev = chip->dev;
- int ret = 0;
- /* Run this only once */
- if (!list_empty(&chip->mixer_list))
return 0;
- dev_info(&dev->dev,
"Presonus Studio 1810c, device_setup: %u\n", chip->setup);
- if (chip->setup == 1)
dev_info(&dev->dev, "(8out/18in @ 48KHz)\n");
- else if (chip->setup == 2)
dev_info(&dev->dev, "(6out/8in @ 192KHz)\n");
- else
dev_info(&dev->dev, "(8out/14in @ 96KHz)\n");
- ret = snd_s1810c_init_mixer_maps(chip);
- if (ret < 0)
return ret;
- private = vzalloc(sizeof(struct s1810_mixer_state));
I don't think vmalloc is needed here, as the object is so small. Use kzalloc() and kfree() instead (unless I overlook something).
- if (!private) {
dev_err(&dev->dev, "could not allocate mixer state\n");
The error message is usually superfluous, as kernel spews the warning in anyway at each allocation error.
thanks,
Takashi