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) return0;
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) return0; return1;
}
/* 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) return0; 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;
}
} return0;
}
/* *findanaudiocontrolunitwiththegivenunitid
*/ staticvoid *find_audio_control_unit(struct mixer_build *state, unsignedchar unit)
{ /* we just parse the header */ struct uac_feature_unit_descriptor *hdr = NULL;
/* *copyastringwiththegivenid
*/ 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) return0;
buf[len] = 0; return len;
}
/* *convertfromthebyte/wordonusbdescriptortothezero-basedinteger
*/ 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;
}
/* *convertfromthezero-basedinttothebyte/wordforusbdescriptor
*/ 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;
} return0; /* 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;
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, *toensuretheyarenotoverridenbytherecursioncalls
*/
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; return0;
}
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, *toensuretheyarenotoverridenbytherecursioncalls
*/
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); return0;
}
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;
}
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. *notethatdetectionbetweenfirmware2.1.1.7(N101) *andlater2.1.1.21isnotveryclearfromdatasheets. *Ihopethattheminvalueis-15360fornewerfirmware--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 *theminimumasdefault
*/ 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);
}
/* *retrievetheminimumandmaximumvaluesforthespecifiedcontrol
*/ 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 * *Somedevicesreportsmallerresolutionsthanactually *reacting.Theydon'treturnerrorsbutsimplyclip *totheloweralignedvalue.
*/ 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 *whileALSATLVcontainsin1/100dBunit
*/
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; *hereweuse-96dBasthethreshold
*/ 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++;
} return0;
} 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;
} return0;
}
/* 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; return0;
}
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);
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: /* *determinethecontrolname.theruleis: *-ifanameidisgivenindescriptor,useit. *-iftheconnectedinputcanbedetermined,thenusethename *ofterminaltype. *-iftheconnectedoutputcanbedetermined,useit. *-otherwise,anonymousname.
*/ 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);
/* 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;
}
/* 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);
get_connector_value(cval, NULL, NULL); return0;
}
/* 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;
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;
num_ins = desc->bNrInPins; for (i = 0; i < num_ins; i++) {
err = parse_audio_unit(state, desc->baSourceID[i]); if (err < 0) return err;
}
type = le16_to_cpu(desc->wProcessType); for (info = list; info && info->type; info++) if (info->type == type) break; if (!info || !info->type)
info = &default_info;
/* check the static mapping table at first */
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) { /* no mapping ? */ switch (state->mixer->protocol) { case UAC_VERSION_1: case UAC_VERSION_2: default: /* if iSelector is given, use it */
nameid = uac_selector_unit_iSelector(desc); if (nameid)
len = snd_usb_copy_string_desc(state->chip,
nameid, kctl->id.name, sizeof(kctl->id.name)); break; case UAC_VERSION_3: /* TODO: Class-Specific strings not yet supported */ break;
}
/* ... or pick up the terminal name at next */ if (!len)
len = get_term_name(state->chip, &state->oterm,
kctl->id.name, sizeof(kctl->id.name), 0); /* ... or use the fixed string "USB" as the last resort */ if (!len)
strscpy(kctl->id.name, "USB", sizeof(kctl->id.name));
error_name: for (i = 0; i < desc->bNrInPins; i++)
kfree(namelist[i]);
kfree(namelist);
error_cval:
usb_mixer_elem_info_free(cval); return err;
}
/* *parseanaudiounitrecursively
*/
staticint parse_audio_unit(struct mixer_build *state, int unitid)
{ unsignedchar *p1; int protocol = state->mixer->protocol;
if (test_and_set_bit(unitid, state->unitbitmap)) return0; /* the unit already visited */
p1 = find_audio_control_unit(state, unitid); if (!p1) {
usb_audio_err(state->chip, "unit %d not found!\n", unitid); return -EINVAL;
}
if (!snd_usb_validate_audio_desc(p1, protocol)) {
usb_audio_dbg(state->chip, "invalid unit %d\n", unitid); return0; /* skip invalid unit */
}
switch (PTYPE(protocol, p1[2])) { case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): return parse_audio_input_terminal(state, unitid, p1); 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_audio_mixer_unit(state, unitid, p1); case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): return parse_clock_source_unit(state, unitid, p1); case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): return parse_audio_selector_unit(state, unitid, p1); case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): return parse_audio_feature_unit(state, unitid, p1); 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_audio_processing_unit(state, unitid, p1); 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_audio_extension_unit(state, unitid, p1); case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): return0; /* FIXME - effect units not implemented yet */ default:
usb_audio_err(state->chip, "unit %u: unexpected type 0x%02x\n",
unitid, p1[2]); return -EINVAL;
}
}
assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc; if (!assoc) return -EINVAL;
/* Detect BADD capture/playback channels from AS EP descriptors */ for (i = 0; i < assoc->bInterfaceCount; i++) { int intf = assoc->bFirstInterface + i;
switch (maxpacksize) { default:
usb_audio_err(mixer->chip, "incorrect wMaxPacketSize 0x%x for BADD profile\n",
maxpacksize); return -EINVAL; case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24:
chmask = 1; break; case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24:
chmask = 3; break;
}
if (dir_in)
c_chmask = chmask; else
p_chmask = chmask;
}
if (!list->is_std_info) continue;
info = mixer_elem_list_to_info(list); /* invalidate cache, so the value is read from the device */
info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&list->kctl->id);
}
}
if (!list->kctl) continue; if (!list->is_std_info) continue;
info = mixer_elem_list_to_info(list); if (count > 1 && info->control != control) continue;
switch (attribute) { case UAC2_CS_CUR: /* invalidate cache, so the value is read from the device */ if (channel)
info->cached &= ~BIT(channel); else/* master channel */
info->cached = 0;
staticvoid snd_usb_mixer_interrupt(struct urb *urb)
{ struct usb_mixer_interface *mixer = urb->context; int len = urb->actual_length; int ustatus = urb->status;
if (ustatus != 0) goto requeue;
if (mixer->protocol == UAC_VERSION_1) { struct uac1_status_word *status;
for (status = urb->transfer_buffer;
len >= sizeof(*status);
len -= sizeof(*status), status++) {
dev_dbg(&urb->dev->dev, "status interrupt: %02x %02x\n",
status->bStatusType,
status->bOriginator);
/* ignore any notifications not from the control interface */ if ((status->bStatusType & UAC1_STATUS_TYPE_ORIG_MASK) !=
UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF) continue;
for (msg = urb->transfer_buffer;
len >= sizeof(*msg);
len -= sizeof(*msg), msg++) { /* drop vendor specific and endpoint requests */ if ((msg->bInfo & UAC2_INTERRUPT_DATA_MSG_VENDOR) ||
(msg->bInfo & UAC2_INTERRUPT_DATA_MSG_EP)) continue;
/* create the handler for the optional status interrupt endpoint */ staticint snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
{ struct usb_endpoint_descriptor *ep; void *transfer_buffer; int buffer_length; unsignedint epnum;
/* we need one interrupt input endpoint */ if (get_iface_desc(mixer->hostif)->bNumEndpoints < 1) return0;
ep = get_endpoint(mixer->hostif, 0); if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_int(ep)) return0;
void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
{ if (mixer->disconnected) return; if (mixer->urb)
usb_kill_urb(mixer->urb); if (mixer->rc_urb)
usb_kill_urb(mixer->rc_urb); if (mixer->private_free)
mixer->private_free(mixer);
mixer->disconnected = true;
}
/* stop any bus activity of a mixer */ staticvoid snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer)
{
usb_kill_urb(mixer->urb);
usb_kill_urb(mixer->rc_urb);
}
staticint snd_usb_mixer_activate(struct usb_mixer_interface *mixer)
{ int err;
if (mixer->urb) {
err = usb_submit_urb(mixer->urb, GFP_NOIO); if (err < 0) return err;
}
return0;
}
int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
{
snd_usb_mixer_inactivate(mixer); if (mixer->private_suspend)
mixer->private_suspend(mixer); return0;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.118Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
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.