|
@@ -1328,6 +1328,51 @@ static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* get the connectors status and report it as boolean type */
|
|
|
+static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
|
|
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
|
|
|
+ int idx = 0, validx, ret, val;
|
|
|
+
|
|
|
+ validx = cval->control << 8 | 0;
|
|
|
+
|
|
|
+ ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
|
|
|
+ if (ret)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
|
|
|
+ if (cval->head.mixer->protocol == UAC_VERSION_2) {
|
|
|
+ struct uac2_connectors_ctl_blk uac2_conn;
|
|
|
+
|
|
|
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR,
|
|
|
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
|
|
+ validx, idx, &uac2_conn, sizeof(uac2_conn));
|
|
|
+ val = !!uac2_conn.bNrChannels;
|
|
|
+ } else { /* UAC_VERSION_3 */
|
|
|
+ struct uac3_insertion_ctl_blk uac3_conn;
|
|
|
+
|
|
|
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR,
|
|
|
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
|
|
+ validx, idx, &uac3_conn, sizeof(uac3_conn));
|
|
|
+ val = !!uac3_conn.bmConInserted;
|
|
|
+ }
|
|
|
+
|
|
|
+ snd_usb_unlock_shutdown(chip);
|
|
|
+
|
|
|
+ if (ret < 0) {
|
|
|
+error:
|
|
|
+ usb_audio_err(chip,
|
|
|
+ "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
|
|
|
+ UAC_GET_CUR, validx, idx, cval->val_type);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ucontrol->value.integer.value[0] = val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct snd_kcontrol_new usb_feature_unit_ctl = {
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
.name = "", /* will be filled later manually */
|
|
@@ -1358,6 +1403,15 @@ static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = {
|
|
|
.put = NULL,
|
|
|
};
|
|
|
|
|
|
+static const struct snd_kcontrol_new usb_connector_ctl_ro = {
|
|
|
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
|
|
|
+ .name = "", /* will be filled later manually */
|
|
|
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
|
+ .info = snd_ctl_boolean_mono_info,
|
|
|
+ .get = mixer_ctl_connector_get,
|
|
|
+ .put = NULL,
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* This symbol is exported in order to allow the mixer quirks to
|
|
|
* hook up to the standard feature unit control mechanism
|
|
@@ -1626,17 +1680,25 @@ static void build_connector_control(struct mixer_build *state,
|
|
|
return;
|
|
|
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id);
|
|
|
/*
|
|
|
- * The first byte from reading the UAC2_TE_CONNECTOR control returns the
|
|
|
- * number of channels connected. This boolean ctl will simply report
|
|
|
- * if any channels are connected or not.
|
|
|
- * (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block)
|
|
|
+ * UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the
|
|
|
+ * number of channels connected.
|
|
|
+ *
|
|
|
+ * UAC3: The first byte specifies size of bitmap for the inserted controls. The
|
|
|
+ * following byte(s) specifies which connectors are inserted.
|
|
|
+ *
|
|
|
+ * This boolean ctl will simply report if any channels are connected
|
|
|
+ * or not.
|
|
|
*/
|
|
|
- cval->control = UAC2_TE_CONNECTOR;
|
|
|
+ if (state->mixer->protocol == UAC_VERSION_2)
|
|
|
+ cval->control = UAC2_TE_CONNECTOR;
|
|
|
+ else /* UAC_VERSION_3 */
|
|
|
+ cval->control = UAC3_TE_INSERTION;
|
|
|
+
|
|
|
cval->val_type = USB_MIXER_BOOLEAN;
|
|
|
cval->channels = 1; /* report true if any channel is connected */
|
|
|
cval->min = 0;
|
|
|
cval->max = 1;
|
|
|
- kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
|
|
|
+ kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
|
|
|
if (!kctl) {
|
|
|
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
|
|
|
kfree(cval);
|
|
@@ -1954,16 +2016,28 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
|
|
|
void *raw_desc)
|
|
|
{
|
|
|
struct usb_audio_term iterm;
|
|
|
- struct uac2_input_terminal_descriptor *d = raw_desc;
|
|
|
+ unsigned int control, bmctls, term_id;
|
|
|
|
|
|
- check_input_term(state, d->bTerminalID, &iterm);
|
|
|
if (state->mixer->protocol == UAC_VERSION_2) {
|
|
|
- /* Check for jack detection. */
|
|
|
- if (uac_v2v3_control_is_readable(le16_to_cpu(d->bmControls),
|
|
|
- UAC2_TE_CONNECTOR)) {
|
|
|
- build_connector_control(state, &iterm, true);
|
|
|
- }
|
|
|
+ struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
|
|
|
+ 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;
|
|
|
+ control = UAC3_TE_INSERTION;
|
|
|
+ term_id = d_v3->bTerminalID;
|
|
|
+ bmctls = le32_to_cpu(d_v3->bmControls);
|
|
|
+ } else {
|
|
|
+ return 0; /* UAC1. No Insertion control */
|
|
|
}
|
|
|
+
|
|
|
+ check_input_term(state, term_id, &iterm);
|
|
|
+
|
|
|
+ /* Check for jack detection. */
|
|
|
+ if (uac_v2v3_control_is_readable(bmctls, control))
|
|
|
+ build_connector_control(state, &iterm, true);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2554,7 +2628,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
|
|
} else { /* UAC_VERSION_3 */
|
|
|
switch (p1[2]) {
|
|
|
case UAC_INPUT_TERMINAL:
|
|
|
- return 0; /* NOP */
|
|
|
+ return parse_audio_input_terminal(state, unitid, p1);
|
|
|
case UAC3_MIXER_UNIT:
|
|
|
return parse_audio_mixer_unit(state, unitid, p1);
|
|
|
case UAC3_CLOCK_SOURCE:
|
|
@@ -2932,6 +3006,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
|
|
err = parse_audio_unit(&state, desc->bCSourceID);
|
|
|
if (err < 0 && err != -EINVAL)
|
|
|
return err;
|
|
|
+
|
|
|
+ if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
|
|
|
+ UAC3_TE_INSERTION)) {
|
|
|
+ build_connector_control(&state, &state.oterm,
|
|
|
+ false);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|