static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc,
int protocol)
{
- return (protocol == UAC_VERSION_1) ?
- &desc->baSourceID[desc->bNrInPins + 4] :
- &desc->baSourceID[desc->bNrInPins + 6];
+ switch (protocol) {
+ case UAC_VERSION_1:
+ return &desc->baSourceID[desc->bNrInPins + 4];
+ case UAC_VERSION_2:
+ return &desc->baSourceID[desc->bNrInPins + 6];
+ case UAC_VERSION_3:
+ return &desc->baSourceID[desc->bNrInPins + 2];
+ default:
+ return NULL;
+ }
+}
+
+static inline __u16 uac3_mixer_unit_wClusterDescrID(struct uac_mixer_unit_descriptor *desc)
+{
+ return (desc->baSourceID[desc->bNrInPins + 1] << 8) |
+ desc->baSourceID[desc->bNrInPins];
}
static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
return 0;
}
+/*
+ * Get logical cluster information for UAC3 devices.
+ */
+static int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id)
+{
+ struct uac3_cluster_header_descriptor c_header;
+ int err;
+
+ err = snd_usb_ctl_msg(state->chip->dev,
+ usb_rcvctrlpipe(state->chip->dev, 0),
+ UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ cluster_id,
+ snd_usb_ctrl_intf(state->chip),
+ &c_header, sizeof(c_header));
+ if (err < 0)
+ goto error;
+ if (err != sizeof(c_header)) {
+ err = -EIO;
+ goto error;
+ }
+
+ return c_header.bNrChannels;
+
+error:
+ usb_audio_err(state->chip, "cannot request logical cluster ID: %d (err: %d)\n", cluster_id, err);
+ return err;
+}
+
+/*
+ * Get number of channels for a Mixer Unit.
+ */
+static int uac_mixer_unit_get_channels(struct mixer_build *state,
+ struct uac_mixer_unit_descriptor *desc)
+{
+ int mu_channels;
+
+ if (desc->bLength < 11)
+ return -EINVAL;
+ if (!desc->bNrInPins)
+ return -EINVAL;
+
+ switch (state->mixer->protocol) {
+ case UAC_VERSION_1:
+ case UAC_VERSION_2:
+ default:
+ mu_channels = uac_mixer_unit_bNrChannels(desc);
+ break;
+ case UAC_VERSION_3:
+ mu_channels = get_cluster_channels_v3(state,
+ uac3_mixer_unit_wClusterDescrID(desc));
+ break;
+ }
+
+ if (!mu_channels)
+ return -EINVAL;
+
+ return mu_channels;
+}
+
/*
* parse the source unit recursively until it reaches to a terminal
* or a branched unit.
term->name = le16_to_cpu(d->wClockSourceStr);
return 0;
}
+ case UAC3_MIXER_UNIT: {
+ struct uac_mixer_unit_descriptor *d = p1;
+
+ err = uac_mixer_unit_get_channels(state, d);
+ if (err < 0)
+ return err;
+
+ term->channels = err;
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+
+ return 0;
+ }
default:
return -ENODEV;
}
*/
static void build_mixer_unit_ctl(struct mixer_build *state,
struct uac_mixer_unit_descriptor *desc,
- int in_pin, int in_ch, int unitid,
- struct usb_audio_term *iterm)
+ int in_pin, int in_ch, int num_outs,
+ int unitid, struct usb_audio_term *iterm)
{
struct usb_mixer_elem_info *cval;
- unsigned int num_outs = uac_mixer_unit_bNrChannels(desc);
unsigned int i, len;
struct snd_kcontrol *kctl;
const struct usbmix_name_map *map;
int input_pins, num_ins, num_outs;
int pin, ich, err;
- if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
- !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
+ err = uac_mixer_unit_get_channels(state, desc);
+ if (err < 0) {
usb_audio_err(state->chip,
"invalid MIXER UNIT descriptor %d\n",
unitid);
- return -EINVAL;
+ return err;
}
+ num_outs = err;
+ input_pins = desc->bNrInPins;
+
num_ins = 0;
ich = 0;
for (pin = 0; pin < input_pins; pin++) {
}
}
if (ich_has_controls)
- build_mixer_unit_ctl(state, desc, pin, ich,
+ build_mixer_unit_ctl(state, desc, pin, ich, num_outs,
unitid, &iterm);
}
}