// SPDX-License-Identifier: GPL-2.0-or-later /* * (Tentative) USB Audio Driver for ALSA * * Mixer control part * * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * * Many codes borrowed from audio.c by * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch)
*/
/* * TODOs, for both the mixer and the streaming interfaces: * * - support for UAC2 effect units * - support for graphical equalizers * - RANGE and MEM set commands (UAC2) * - RANGE and MEM interrupt dispatchers (UAC2) * - audio channel clustering (UAC2) * - audio sample rate converter units (UAC2) * - proper handling of clock multipliers (UAC2) * - dispatch clock change notifications (UAC2) * - stop PCM streams which use a clock that became invalid * - stop PCM streams which use a clock selector that has changed * - parse available sample rates again when clock sources changed
*/
/* * manual mapping of mixer names * if the mixer topology is too complicated and the parsed names are * ambiguous, add the entries in usbmixer_maps.c.
*/ #include"mixer_maps.c"
staticconststruct usbmix_name_map *
find_map(conststruct usbmix_name_map *p, int unitid, int control)
{ if (!p) return NULL;
for (; p->id; p++) { if (p->id == unitid &&
(!control || !p->control || control == p->control)) return p;
} return NULL;
}
/* get the mapped name if the unit matches */ staticint
check_mapped_name(conststruct usbmix_name_map *p, char *buf, int buflen)
{ int len;
if (!p || !p->name) return 0;
buflen--;
len = strscpy(buf, p->name, buflen); return len < 0 ? buflen : len;
}
/* ignore the error value if ignore_ctl_error flag is set */ #define filter_error(cval, err) \
((cval)->head.mixer->ignore_ctl_error ? 0 : (err))
/* check whether the control should be ignored */ staticinlineint
check_ignored_ctl(conststruct usbmix_name_map *p)
{ if (!p || p->name || p->dB) return 0; return 1;
}
/* get the mapped selector source name */ staticint check_mapped_selector_name(struct mixer_build *state, int unitid, int index, char *buf, int buflen)
{ conststruct usbmix_selector_map *p; int len;
if (!state->selector_map) return 0; for (p = state->selector_map; p->id; p++) { if (p->id == unitid && index < p->count) {
len = strscpy(buf, p->names[index], buflen); return len < 0 ? buflen : len;
}
} return 0;
}
/* * find an audio control unit with the given unit id
*/ staticvoid *find_audio_control_unit(struct mixer_build *state, unsignedchar unit)
{ /* we just parse the header */ struct uac_feature_unit_descriptor *hdr = NULL;
/* * copy a string with the given id
*/ staticint snd_usb_copy_string_desc(struct snd_usb_audio *chip, int index, char *buf, int maxlen)
{ int len = usb_string(chip->dev, index, buf, maxlen - 1);
if (len < 0) return 0;
buf[len] = 0; return len;
}
/* * convert from the byte/word on usb descriptor to the zero-based integer
*/ staticint convert_signed_value(struct usb_mixer_elem_info *cval, int val)
{ switch (cval->val_type) { case USB_MIXER_BOOLEAN: return !!val; case USB_MIXER_INV_BOOLEAN: return !val; case USB_MIXER_U8:
val &= 0xff; break; case USB_MIXER_S8:
val &= 0xff; if (val >= 0x80)
val -= 0x100; break; case USB_MIXER_U16:
val &= 0xffff; break; case USB_MIXER_S16:
val &= 0xffff; if (val >= 0x8000)
val -= 0x10000; break;
} return val;
}
/* * convert from the zero-based int to the byte/word for usb descriptor
*/ staticint convert_bytes_value(struct usb_mixer_elem_info *cval, int val)
{ switch (cval->val_type) { case USB_MIXER_BOOLEAN: return !!val; case USB_MIXER_INV_BOOLEAN: return !val; case USB_MIXER_S8: case USB_MIXER_U8: return val & 0xff; case USB_MIXER_S16: case USB_MIXER_U16: return val & 0xffff;
} return 0; /* not reached */
}
staticint get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
{ struct snd_usb_audio *chip = cval->head.mixer->chip; /* enough space for one range */ unsignedchar buf[sizeof(__u16) + 3 * sizeof(__u32)]; unsignedchar *val; int idx = 0, ret, val_size, size;
__u8 bRequest;
/* * TLV callback for mixer volume controls
*/ int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsignedint size, unsignedint __user *_tlv)
{ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
DECLARE_TLV_DB_MINMAX(scale, 0, 0);
if (size < sizeof(scale)) return -ENOMEM; if (cval->min_mute)
scale[0] = SNDRV_CTL_TLVT_DB_MINMAX_MUTE;
scale[2] = cval->dBmin;
scale[3] = cval->dBmax; if (copy_to_user(_tlv, scale, sizeof(scale))) return -EFAULT; return 0;
}
/* * parser routines begin here...
*/
staticint parse_audio_unit(struct mixer_build *state, int unitid);
/* * check if the input/output channel routing is enabled on the given bitmap. * used for mixer unit parser
*/ staticint check_matrix_bitmap(unsignedchar *bmap, int ich, int och, int num_outs)
{ int idx = ich * num_outs + och; return bmap[idx >> 3] & (0x80 >> (idx & 7));
}
/* * add an alsa control element * search and increment the index until an empty slot is found. * * if failed, give up and free the control instance.
*/
int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl, bool is_std_info)
{ struct usb_mixer_interface *mixer = list->mixer; int err;
/* * Get number of channels for a Mixer Unit.
*/ staticint uac_mixer_unit_get_channels(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc)
{ int mu_channels;
switch (state->mixer->protocol) { case UAC_VERSION_1: case UAC_VERSION_2: default: if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 1) return 0; /* no bmControls -> skip */
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;
}
return mu_channels;
}
/* * Parse Input Terminal Unit
*/ staticint __check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term);
staticint parse_term_uac2_iterm_unit(struct mixer_build *state, struct usb_audio_term *term, void *p1, int id)
{ struct uac2_input_terminal_descriptor *d = p1; int err;
/* call recursively to verify the referenced clock entity */
err = __check_input_term(state, d->bCSourceID, term); if (err < 0) return err;
/* save input term properties after recursion, * to ensure they are not overriden by the recursion calls
*/
term->id = id;
term->type = le16_to_cpu(d->wTerminalType);
term->channels = d->bNrChannels;
term->chconfig = le32_to_cpu(d->bmChannelConfig);
term->name = d->iTerminal; return 0;
}
staticint parse_term_uac3_iterm_unit(struct mixer_build *state, struct usb_audio_term *term, void *p1, int id)
{ struct uac3_input_terminal_descriptor *d = p1; int err;
/* call recursively to verify the referenced clock entity */
err = __check_input_term(state, d->bCSourceID, term); if (err < 0) return err;
/* save input term properties after recursion, * to ensure they are not overriden by the recursion calls
*/
term->id = id;
term->type = le16_to_cpu(d->wTerminalType);
staticint parse_term_selector_unit(struct mixer_build *state, struct usb_audio_term *term, void *p1, int id)
{ struct uac_selector_unit_descriptor *d = p1; int err;
/* call recursively to retrieve the channel info */
err = __check_input_term(state, d->baSourceID[0], term); if (err < 0) return err;
term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
term->id = id; if (state->mixer->protocol != UAC_VERSION_3)
term->name = uac_selector_unit_iSelector(d); return 0;
}
staticint parse_term_proc_unit(struct mixer_build *state, struct usb_audio_term *term, void *p1, int id, int vtype)
{ struct uac_processing_unit_descriptor *d = p1; int protocol = state->mixer->protocol; int err;
if (d->bNrInPins) { /* call recursively to retrieve the channel info */
err = __check_input_term(state, d->baSourceID[0], term); if (err < 0) return err;
}
/* * parse the source unit recursively until it reaches to a terminal * or a branched unit.
*/ staticint __check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
{ int protocol = state->mixer->protocol; void *p1; unsignedchar *hdr;
for (;;) { /* a loop in the terminal chain? */ if (test_and_set_bit(id, state->termbitmap)) return -EINVAL;
p1 = find_audio_control_unit(state, id); if (!p1) break; if (!snd_usb_validate_audio_desc(p1, protocol)) break; /* bad descriptor */
hdr = p1;
term->id = id;
switch (PTYPE(protocol, hdr[2])) { case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): { /* the header is the same for all versions */ struct uac_feature_unit_descriptor *d = p1;
id = d->bSourceID; break; /* continue to parse */
} case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): return parse_term_uac1_iterm_unit(state, term, p1, id); case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): return parse_term_uac2_iterm_unit(state, term, p1, id); case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): return parse_term_uac3_iterm_unit(state, term, p1, id); case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): return parse_term_mixer_unit(state, term, p1, id); case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): return parse_term_selector_unit(state, term, p1, id); case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): return parse_term_proc_unit(state, term, p1, id,
UAC3_PROCESSING_UNIT); case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): return parse_term_effect_unit(state, term, p1, id); case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): return parse_term_proc_unit(state, term, p1, id,
UAC3_EXTENSION_UNIT); case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): return parse_term_uac2_clock_source(state, term, p1, id); case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): return parse_term_uac3_clock_source(state, term, p1, id); default: return -ENODEV;
}
} return -ENODEV;
}
/* feature unit control information */ struct usb_feature_control_info { int control; constchar *name; int type; /* data type for uac1 */ int type_uac2; /* data type for uac2 if different from uac1, else -1 */
};
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ if (strcmp(kctl->id.name, "Effect Duration") == 0) {
usb_audio_info(chip, "set quirk for FTU Effect Duration\n");
cval->min = 0x0000;
cval->max = 0x7f00;
cval->res = 0x0100; break;
} if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
usb_audio_info(chip, "set quirks for FTU Effect Feedback/Volume\n");
cval->min = 0x00;
cval->max = 0x7f; break;
} break;
case USB_ID(0x0d8c, 0x0103): if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
usb_audio_info(chip, "set volume quirk for CM102-A+/102S+\n");
cval->min = -256;
} break;
case USB_ID(0x0471, 0x0101): case USB_ID(0x0471, 0x0104): case USB_ID(0x0471, 0x0105): case USB_ID(0x0672, 0x1041): /* quirk for UDA1321/N101. * note that detection between firmware 2.1.1.7 (N101) * and later 2.1.1.21 is not very clear from datasheets. * I hope that the min value is -15360 for newer firmware --jk
*/ if (!strcmp(kctl->id.name, "PCM Playback Volume") &&
cval->min == -15616) {
usb_audio_info(chip, "set volume quirk for UDA1321/N101 chip\n");
cval->max = -256;
} break;
case USB_ID(0x046d, 0x09a4): if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
usb_audio_info(chip, "set volume quirk for QuickCam E3500\n");
cval->min = 6080;
cval->max = 8768;
cval->res = 192;
} break;
case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */ if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
strstr(kctl->id.name, "Capture Volume") != NULL) {
cval->min >>= 8;
cval->max = 0;
cval->res = 1;
} break; case USB_ID(0x3302, 0x12db): /* MOONDROP Quark2 */ if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
usb_audio_info(chip, "set volume quirk for MOONDROP Quark2\n");
cval->min = -14208; /* Mute under it */
} break;
}
}
/* forcibly initialize the current mixer value; if GET_CUR fails, set to * the minimum as default
*/ staticvoid init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
{ int val, err;
err = snd_usb_get_cur_mix_value(cval, ch, idx, &val); if (!err) return; if (!cval->head.mixer->ignore_ctl_error)
usb_audio_warn(cval->head.mixer->chip, "%d:%d: failed to get current value for ch %d (%d)\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
ch, err);
snd_usb_set_cur_mix_value(cval, ch, idx, cval->min);
}
/* * retrieve the minimum and maximum values for the specified control
*/ staticint get_min_max_with_quirks(struct usb_mixer_elem_info *cval, int default_min, struct snd_kcontrol *kctl)
{ int i, idx;
/* Additional checks for the proper resolution * * Some devices report smaller resolutions than actually * reacting. They don't return errors but simply clip * to the lower aligned value.
*/ if (cval->min + cval->res < cval->max) { int last_valid_res = cval->res; int saved, test, check; if (get_cur_mix_raw(cval, minchn, &saved) < 0) goto no_res_check; for (;;) {
test = saved; if (test < cval->max)
test += cval->res; else
test -= cval->res; if (test < cval->min || test > cval->max ||
snd_usb_set_cur_mix_value(cval, minchn, 0, test) ||
get_cur_mix_raw(cval, minchn, &check)) {
cval->res = last_valid_res; break;
} if (test == check) break;
cval->res *= 2;
}
snd_usb_set_cur_mix_value(cval, minchn, 0, saved);
}
no_res_check:
cval->initialized = 1;
}
if (kctl)
volume_control_quirks(cval, kctl);
/* USB descriptions contain the dB scale in 1/256 dB unit * while ALSA TLV contains in 1/100 dB unit
*/
cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256;
cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256; if (cval->dBmin > cval->dBmax) { /* something is wrong; assume it's either from/to 0dB */ if (cval->dBmin < 0)
cval->dBmax = 0; elseif (cval->dBmin > 0)
cval->dBmin = 0; if (cval->dBmin > cval->dBmax) { /* totally crap, return an error */ return -EINVAL;
}
} else { /* if the max volume is too low, it's likely a bogus range; * here we use -96dB as the threshold
*/ if (cval->dBmax <= -9600) {
usb_audio_info(cval->head.mixer->chip, "%d:%d: bogus dB values (%d/%d), disabling dB reporting\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
cval->dBmin, cval->dBmax);
cval->dBmin = cval->dBmax = 0;
}
}
/* initialize all elements */ if (!cval->cmask) {
init_cur_mix_raw(cval, 0, 0);
} else {
idx = 0; for (i = 0; i < MAX_CHANNELS; i++) { if (cval->cmask & BIT(i)) {
init_cur_mix_raw(cval, i + 1, idx);
idx++;
}
}
}
/* get the max value advertised via control API */ staticint get_max_exposed(struct usb_mixer_elem_info *cval)
{ if (!cval->max_exposed) { if (cval->res)
cval->max_exposed =
DIV_ROUND_UP(cval->max - cval->min, cval->res); else
cval->max_exposed = cval->max - cval->min;
} return cval->max_exposed;
}
/* get a feature/mixer unit info */ staticint mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
/* get the current value from feature/mixer unit */ staticint mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); int c, cnt, val, err;
ucontrol->value.integer.value[0] = cval->min; if (cval->cmask) {
cnt = 0; for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & BIT(c))) continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); if (err < 0) return filter_error(cval, err);
val = get_relative_value(cval, val);
ucontrol->value.integer.value[cnt] = val;
cnt++;
} return 0;
} else { /* master channel */
err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); if (err < 0) return filter_error(cval, err);
val = get_relative_value(cval, val);
ucontrol->value.integer.value[0] = val;
} return 0;
}
/* put the current value to feature/mixer unit */ staticint mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); int max_val = get_max_exposed(cval); int c, cnt, val, oval, err; int changed = 0;
if (cval->cmask) {
cnt = 0; for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & BIT(c))) continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); if (err < 0) return filter_error(cval, err);
val = ucontrol->value.integer.value[cnt]; if (val < 0 || val > max_val) return -EINVAL;
val = get_abs_value(cval, val); if (oval != val) {
snd_usb_set_cur_mix_value(cval, c + 1, cnt, val);
changed = 1;
}
cnt++;
}
} else { /* master channel */
err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) return filter_error(cval, err);
val = ucontrol->value.integer.value[0]; if (val < 0 || val > max_val) return -EINVAL;
val = get_abs_value(cval, val); if (val != oval) {
snd_usb_set_cur_mix_value(cval, 0, 0, val);
changed = 1;
}
} return changed;
}
/* get the boolean value from the master channel of a UAC control */ staticint mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); int val, err;
if (ret < 0) { if (name && strstr(name, "Speaker")) { if (val)
*val = 1; return 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);
if (val)
*val = 0;
return filter_error(cval, ret);
}
return ret;
}
/* get the connectors status and report it as boolean type */ staticint mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol); int ret, val;
ret = get_connector_value(cval, kcontrol->id.name, &val);
staticconststruct snd_kcontrol_new usb_feature_unit_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later manually */
.info = mixer_ctl_feature_info,
.get = mixer_ctl_feature_get,
.put = mixer_ctl_feature_put,
};
/* the read-only variant */ staticconststruct snd_kcontrol_new usb_feature_unit_ctl_ro = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later manually */
.info = mixer_ctl_feature_info,
.get = mixer_ctl_feature_get,
.put = NULL,
};
/* * A control which shows the boolean value from reading a UAC control on * the master channel.
*/ staticconststruct snd_kcontrol_new usb_bool_master_control_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_master_bool_get,
.put = NULL,
};
/* * This symbol is exported in order to allow the mixer quirks to * hook up to the standard feature unit control mechanism
*/ conststruct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl;
/* * build a feature control
*/ static size_t append_ctl_name(struct snd_kcontrol *kctl, constchar *str)
{ return strlcat(kctl->id.name, str, sizeof(kctl->id.name));
}
/* * A lot of headsets/headphones have a "Speaker" mixer. Make sure we * rename it to "Headphone". We determine if something is a headphone * similar to how udev determines form factor.
*/ staticvoid check_no_speaker_on_headset(struct snd_kcontrol *kctl, struct snd_card *card)
{ staticconstchar * const names_to_check[] = { "Headset", "headset", "Headphone", "headphone", NULL}; constchar * const *s; bool found = false;
if (strcmp("Speaker", kctl->id.name)) return;
for (s = names_to_check; *s; s++) if (strstr(card->shortname, *s)) {
found = true; break;
}
if (!found) return;
snd_ctl_rename(card, kctl, "Headphone");
}
staticconststruct usb_feature_control_info *get_feature_control_info(int control)
{ int i;
for (i = 0; i < ARRAY_SIZE(audio_feature_info); ++i) { if (audio_feature_info[i].control == control) return &audio_feature_info[i];
} return NULL;
}
staticvoid __build_feature_ctl(struct usb_mixer_interface *mixer, conststruct usbmix_name_map *imap, unsignedint ctl_mask, int control, struct usb_audio_term *iterm, struct usb_audio_term *oterm, int unitid, int nameid, int readonly_mask)
{ conststruct usb_feature_control_info *ctl_info; unsignedint len = 0; int mapped_name = 0; struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; conststruct usbmix_name_map *map; unsignedint range;
if (control == UAC_FU_GRAPHIC_EQUALIZER) { /* FIXME: not supported yet */ return;
}
map = find_map(imap, unitid, control); if (check_ignored_ctl(map)) return;
if (ctl_mask == 0) {
cval->channels = 1; /* master channel */
cval->master_readonly = readonly_mask;
} else { int i, c = 0; for (i = 0; i < 16; i++) if (ctl_mask & BIT(i))
c++;
cval->channels = c;
cval->ch_readonly = readonly_mask;
}
/* * If all channels in the mask are marked read-only, make the control * read-only. snd_usb_set_cur_mix_value() will check the mask again and won't * issue write commands to read-only channels.
*/ if (cval->channels == readonly_mask)
kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval); else
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
mapped_name = len != 0; if (!len && nameid)
len = snd_usb_copy_string_desc(mixer->chip, nameid,
kctl->id.name, sizeof(kctl->id.name));
switch (control) { case UAC_FU_MUTE: case UAC_FU_VOLUME: /* * determine the control name. the rule is: * - if a name id is given in descriptor, use it. * - if the connected input can be determined, then use the name * of terminal type. * - if the connected output can be determined, use it. * - otherwise, anonymous name.
*/ if (!len) { if (iterm)
len = get_term_name(mixer->chip, iterm,
kctl->id.name, sizeof(kctl->id.name), 1); if (!len && oterm)
len = get_term_name(mixer->chip, oterm,
kctl->id.name, sizeof(kctl->id.name), 1); if (!len)
snprintf(kctl->id.name, sizeof(kctl->id.name), "Feature %d", unitid);
}
if (!mapped_name)
check_no_speaker_on_headset(kctl, mixer->chip->card);
/* * determine the stream direction: * if the connected output is USB stream, then it's likely a * capture stream. otherwise it should be playback (hopefully :)
*/ if (!mapped_name && oterm && !(oterm->type >> 16)) { if ((oterm->type & 0xff00) == 0x0100)
append_ctl_name(kctl, " Capture"); else
append_ctl_name(kctl, " Playback");
}
append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); break; default: if (!len)
strscpy(kctl->id.name, audio_feature_info[control-1].name, sizeof(kctl->id.name)); break;
}
/* get min/max values */
get_min_max_with_quirks(cval, 0, kctl);
/* skip a bogus volume range */ if (cval->max <= cval->min) {
usb_audio_dbg(mixer->chip, "[%d] FU [%s] skipped due to invalid volume\n",
cval->head.id, kctl->id.name);
snd_ctl_free_one(kctl); return;
}
range = (cval->max - cval->min) / cval->res; /* * Are there devices with volume range more than 255? I use a bit more * to be sure. 384 is a resolution magic number found on Logitech * devices. It will definitively catch all buggy Logitech devices.
*/ if (range > 384) {
usb_audio_warn(mixer->chip, "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
range);
usb_audio_warn(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d",
cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max, cval->res);
}
usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max, cval->res);
snd_usb_mixer_add_control(&cval->head, kctl);
}
staticvoid build_feature_ctl(struct mixer_build *state, void *raw_desc, unsignedint ctl_mask, int control, struct usb_audio_term *iterm, int unitid, int readonly_mask)
{ struct uac_feature_unit_descriptor *desc = raw_desc; int nameid = uac_feature_unit_iFeature(desc);
if (name_len == 0)
strscpy(name, "Unknown", name_size);
/* * sound/core/ctljack.c has a convention of naming jack controls * by ending in " Jack". Make it slightly more useful by * indicating Input or Output after the terminal name.
*/ if (is_input)
strlcat(name, " - Input Jack", name_size); else
strlcat(name, " - Output Jack", name_size);
}
/* get connector value to "wake up" the USB audio */ staticint connector_mixer_resume(struct usb_mixer_elem_list *list)
{ struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list);
/* Build a mixer control for a UAC connector control (jack-detect) */ staticvoid build_connector_control(struct usb_mixer_interface *mixer, conststruct usbmix_name_map *imap, struct usb_audio_term *term, bool is_input)
{ struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; conststruct usbmix_name_map *map;
map = find_map(imap, term->id, 0); if (check_ignored_ctl(map)) return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return;
snd_usb_mixer_elem_init_std(&cval->head, mixer, term->id);
/* set up a specific resume callback */
cval->head.resume = connector_mixer_resume;
/* * 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.
*/ if (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_connector_ctl_ro, cval); if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
usb_mixer_elem_info_free(cval); return;
}
if (state->mixer->protocol != UAC_VERSION_2) return -EINVAL;
/* * The only property of this unit we are interested in is the * clock source validity. If that isn't readable, just bail out.
*/ if (!uac_v2v3_control_is_readable(hdr->bmControls,
UAC2_CS_CONTROL_CLOCK_VALID)) return 0;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM;
/* * parse a feature unit * * most of controls are defined here.
*/ staticint parse_audio_feature_unit(struct mixer_build *state, int unitid, void *_ftr)
{ int channels, i, j; struct usb_audio_term iterm; unsignedint master_bits; int err, csize; struct uac_feature_unit_descriptor *hdr = _ftr;
__u8 *bmaControls;
if (channels > 32) {
usb_audio_info(state->chip, "usbmixer: too many channels (%d) in unit %d\n",
channels, unitid); return -EINVAL;
}
/* parse the source unit */
err = parse_audio_unit(state, hdr->bSourceID); if (err < 0) return err;
/* determine the input source type and name */
err = check_input_term(state, hdr->bSourceID, &iterm); if (err < 0) return err;
master_bits = snd_usb_combine_bytes(bmaControls, csize); /* master configuration quirks */ switch (state->chip->usb_id) { case USB_ID(0x08bb, 0x2702):
usb_audio_info(state->chip, "usbmixer: master volume quirk for PCM2702 chip\n"); /* disable non-functional volume control */
master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME); break; case USB_ID(0x1130, 0xf211):
usb_audio_info(state->chip, "usbmixer: volume control quirk for Tenx TP6911 Audio Headset\n"); /* disable non-functional volume control */
channels = 0; break;
}
if (state->mixer->protocol == UAC_VERSION_1) { /* check all control types */ for (i = 0; i < 10; i++) { unsignedint ch_bits = 0; int control = audio_feature_info[i].control;
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize); if (mask & BIT(i))
ch_bits |= BIT(j);
} /* audio class v1 controls are never read-only */
/* * The first channel must be set * (for ease of programming).
*/ if (ch_bits & 1)
build_feature_ctl(state, _ftr, ch_bits, control,
&iterm, unitid, 0); if (master_bits & BIT(i))
build_feature_ctl(state, _ftr, 0, control,
&iterm, unitid, 0);
}
} else { /* UAC_VERSION_2/3 */ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { unsignedint ch_bits = 0; unsignedint ch_read_only = 0; int control = audio_feature_info[i].control;
/* * NOTE: build_feature_ctl() will mark the control * read-only if all channels are marked read-only in * the descriptors. Otherwise, the control will be * reported as writeable, but the driver will not * actually issue a write command for read-only * channels.
*/
/* * The first channel must be set * (for ease of programming).
*/ if (ch_bits & 1)
build_feature_ctl(state, _ftr, ch_bits, control,
&iterm, unitid, ch_read_only); if (uac_v2v3_control_is_readable(master_bits, control))
build_feature_ctl(state, _ftr, 0, control,
&iterm, unitid,
!uac_v2v3_control_is_writeable(master_bits,
control));
}
}
return 0;
}
/* * Mixer Unit
*/
/* check whether the given in/out overflows bmMixerControls matrix */ staticbool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc, int protocol, int num_ins, int num_outs)
{
u8 *hdr = (u8 *)desc;
u8 *c = uac_mixer_unit_bmControls(desc, protocol);
size_t rest; /* remaining bytes after bmMixerControls */
/* * build a mixer unit control * * the callbacks are identical with feature unit. * input channel number (zero based) is given in control field instead.
*/ staticvoid build_mixer_unit_ctl(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc, int in_pin, int in_ch, int num_outs, int unitid, struct usb_audio_term *iterm)
{ struct usb_mixer_elem_info *cval; unsignedint i, len; struct snd_kcontrol *kctl; conststruct usbmix_name_map *map;
map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return;
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = in_ch + 1; /* based on 1 */
cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) {
__u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
if (check_matrix_bitmap(c, in_ch, i, num_outs)) {
cval->cmask |= BIT(i);
cval->channels++;
}
}
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.