📄 usbmixer.c
字号:
{ 0x0707, "Analog Tape" }, { 0x0708, "Phonograph" }, { 0x0709, "VCR Audio" }, { 0x070a, "Video Disk Audio" }, { 0x070b, "DVD Audio" }, { 0x070c, "TV Tuner Audio" }, { 0x070d, "Satellite Rec Audio" }, { 0x070e, "Cable Tuner Audio" }, { 0x070f, "DSS Audio" }, { 0x0710, "Radio Receiver" }, { 0x0711, "Radio Transmitter" }, { 0x0712, "Multi-Track Recorder" }, { 0x0713, "Synthesizer" }, { 0 },};static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, unsigned char *name, int maxlen, int term_only){ struct iterm_name_combo *names; if (iterm->name) return snd_usb_copy_string_desc(state, iterm->name, name, maxlen); /* virtual type - not a real terminal */ if (iterm->type >> 16) { if (term_only) return 0; switch (iterm->type >> 16) { case SELECTOR_UNIT: strcpy(name, "Selector"); return 8; case PROCESSING_UNIT: strcpy(name, "Process Unit"); return 12; case EXTENSION_UNIT: strcpy(name, "Ext Unit"); return 8; case MIXER_UNIT: strcpy(name, "Mixer"); return 5; default: return sprintf(name, "Unit %d", iterm->id); } } switch (iterm->type & 0xff00) { case 0x0100: strcpy(name, "PCM"); return 3; case 0x0200: strcpy(name, "Mic"); return 3; case 0x0400: strcpy(name, "Headset"); return 7; case 0x0500: strcpy(name, "Phone"); return 5; } for (names = iterm_names; names->type; names++) if (names->type == iterm->type) { strcpy(name, names->name); return strlen(names->name); } return 0;}/* * parse the source unit recursively until it reaches to a terminal * or a branched unit. */static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term){ unsigned char *p1; memset(term, 0, sizeof(*term)); while ((p1 = find_audio_control_unit(state, id)) != NULL) { term->id = id; switch (p1[2]) { case INPUT_TERMINAL: term->type = combine_word(p1 + 4); term->channels = p1[7]; term->chconfig = combine_word(p1 + 8); term->name = p1[11]; return 0; case FEATURE_UNIT: id = p1[4]; break; /* continue to parse */ case MIXER_UNIT: term->type = p1[2] << 16; /* virtual type */ term->channels = p1[5 + p1[4]]; term->chconfig = combine_word(p1 + 6 + p1[4]); term->name = p1[p1[0] - 1]; return 0; case SELECTOR_UNIT: /* call recursively to retrieve the channel info */ if (check_input_term(state, p1[5], term) < 0) return -ENODEV; term->type = p1[2] << 16; /* virtual type */ term->id = id; term->name = p1[9 + p1[0] - 1]; return 0; case PROCESSING_UNIT: case EXTENSION_UNIT: if (p1[6] == 1) { id = p1[7]; break; /* continue to parse */ } term->type = p1[2] << 16; /* virtual type */ term->channels = p1[7 + p1[6]]; term->chconfig = combine_word(p1 + 8 + p1[6]); term->name = p1[12 + p1[6] + p1[11 + p1[6]]]; return 0; default: return -ENODEV; } } return -ENODEV;}/* * Feature Unit *//* feature unit control information */struct usb_feature_control_info { const char *name; unsigned int type; /* control type (mute, volume, etc.) */};static struct usb_feature_control_info audio_feature_info[] = { { "Mute", USB_MIXER_INV_BOOLEAN }, { "Volume", USB_MIXER_S16 }, { "Tone Control - Bass", USB_MIXER_S8 }, { "Tone Control - Mid", USB_MIXER_S8 }, { "Tone Control - Treble", USB_MIXER_S8 }, { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */ { "Auto Gain Control", USB_MIXER_BOOLEAN }, { "Delay Control", USB_MIXER_U16 }, { "Bass Boost", USB_MIXER_BOOLEAN }, { "Loudness", USB_MIXER_BOOLEAN },};/* private_free callback */static void usb_mixer_elem_free(struct snd_kcontrol *kctl){ kfree(kctl->private_data); kctl->private_data = NULL;}/* * interface to ALSA control for feature/mixer units *//* * retrieve the minimum and maximum values for the specified control */static int get_min_max(struct usb_mixer_elem_info *cval, int default_min){ /* for failsafe */ cval->min = default_min; cval->max = cval->min + 1; cval->res = 1; if (cval->val_type == USB_MIXER_BOOLEAN || cval->val_type == USB_MIXER_INV_BOOLEAN) { cval->initialized = 1; } else { int minchn = 0; if (cval->cmask) { int i; for (i = 0; i < MAX_CHANNELS; i++) if (cval->cmask & (1 << i)) { minchn = i + 1; break; } } if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->mixer->ctrlif, cval->control, cval->id); return -EINVAL; } if (get_ctl_value(cval, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) { cval->res = 1; } else { int last_valid_res = cval->res; while (cval->res > 1) { if (set_ctl_value(cval, SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0) break; cval->res /= 2; } if (get_ctl_value(cval, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) cval->res = last_valid_res; } if (cval->res == 0) cval->res = 1; /* 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; get_cur_mix_value(cval, minchn, &saved); for (;;) { test = saved; if (test < cval->max) test += cval->res; else test -= cval->res; if (test < cval->min || test > cval->max || set_cur_mix_value(cval, minchn, test) || get_cur_mix_value(cval, minchn, &check)) { cval->res = last_valid_res; break; } if (test == check) break; cval->res *= 2; } set_cur_mix_value(cval, minchn, saved); } cval->initialized = 1; } return 0;}/* get a feature/mixer unit info */static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ struct usb_mixer_elem_info *cval = kcontrol->private_data; if (cval->val_type == USB_MIXER_BOOLEAN || cval->val_type == USB_MIXER_INV_BOOLEAN) uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; else uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = cval->channels; if (cval->val_type == USB_MIXER_BOOLEAN || cval->val_type == USB_MIXER_INV_BOOLEAN) { uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; } else { if (! cval->initialized) get_min_max(cval, 0); uinfo->value.integer.min = 0; uinfo->value.integer.max = (cval->max - cval->min + cval->res - 1) / cval->res; } return 0;}/* get the current value from feature/mixer unit */static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct usb_mixer_elem_info *cval = kcontrol->private_data; int c, cnt, val, err; if (cval->cmask) { cnt = 0; for (c = 0; c < MAX_CHANNELS; c++) { if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &val); if (err < 0) { if (cval->mixer->ignore_ctl_error) { ucontrol->value.integer.value[0] = cval->min; return 0; } snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); return err; } val = get_relative_value(cval, val); ucontrol->value.integer.value[cnt] = val; cnt++; } } } else { /* master channel */ err = get_cur_mix_value(cval, 0, &val); if (err < 0) { if (cval->mixer->ignore_ctl_error) { ucontrol->value.integer.value[0] = cval->min; return 0; } snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err); return err; } val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; } return 0;}/* put the current value to feature/mixer unit */static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct usb_mixer_elem_info *cval = kcontrol->private_data; int c, cnt, val, oval, err; int changed = 0; if (cval->cmask) { cnt = 0; for (c = 0; c < MAX_CHANNELS; c++) { if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &oval); if (err < 0) { if (cval->mixer->ignore_ctl_error) return 0; return err; } val = ucontrol->value.integer.value[cnt]; val = get_abs_value(cval, val); if (oval != val) { set_cur_mix_value(cval, c + 1, val); changed = 1; } get_cur_mix_value(cval, c + 1, &val); cnt++; } } } else { /* master channel */ err = get_cur_mix_value(cval, 0, &oval); if (err < 0 && cval->mixer->ignore_ctl_error) return 0; if (err < 0) return err; val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { set_cur_mix_value(cval, 0, val); changed = 1; } } return changed;}static struct 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,};/* * build a feature control */static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, unsigned int ctl_mask, int control, struct usb_audio_term *iterm, int unitid){ unsigned int len = 0; int mapped_name = 0; int nameid = desc[desc[0] - 1]; struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; control++; /* change from zero-based to 1-based value */ if (control == USB_FEATURE_GEQ) { /* FIXME: not supported yet */ return; } if (check_ignored_ctl(state, unitid, control)) return; cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (! cval) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return; } cval->mixer = state->mixer; cval->id = unitid; cval->control = control; cval->cmask = ctl_mask; cval->val_type = audio_feature_info[control-1].type; if (ctl_mask == 0) cval->channels = 1; /* master channel */ else { int i, c = 0; for (i = 0; i < 16; i++) if (ctl_mask & (1 << i)) c++; cval->channels = c; } /* get min/max values */ get_min_max(cval, 0); kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); kfree(cval); return; } kctl->private_free = usb_mixer_elem_free; len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; if (! len && nameid) len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { case USB_FEATURE_MUTE: case USB_FEATURE_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) { len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1); if (! len) len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); if (! len) len = snprintf(kctl->id.name, sizeof(kctl->id.name), "Feature %d", unitid); } /* 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 && ! (state->oterm.type >> 16)) { if ((state->oterm.type & 0xff00) == 0x0100) { len = strlcat(kctl->id.name, " Capture", sizeof(kctl->id.name)); } else { len = strlcat(kctl->id.name + len, " Playback", sizeof(kctl->id.name)); } } strlcat(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume", sizeof(kctl->id.name)); if (control == USB_FEATURE_VOLUME) { kctl->tlv.c = mixer_vol_tlv; kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; } break; default: if (! len) strlcpy(kctl->id.name, audio_feature_info[control-1].name, sizeof(kctl->id.name)); break; } /* 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 */ switch (state->chip->usb_id) { case USB_ID(0x0471, 0x0101): case USB_ID(0x0471, 0x0104): case USB_ID(0x0471, 0x0105): case USB_ID(0x0672, 0x1041): if (!strcmp(kctl->id.name, "PCM Playback Volume") && cval->min == -15616) { snd_printk(KERN_INFO "using volume control quirk for the UDA1321/N101 chip\n"); cval->max = -256; } } snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); add_control_to_empty(state, kctl);}/* * parse a feature unit * * most of controlls are defined here. */static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unsigned char *ftr){ int channels, i, j; struct usb_audio_term iterm; unsigned int master_bits, first_ch_bits; int err, csize; if (ftr[0] < 7 || ! (csize = ftr[5]) || ftr[0] < 7 + csize) { snd_printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); return -EINVAL; } /* parse the source unit */ if ((err = parse_audio_unit(state, ftr[4])) < 0) return err; /* determine the input source type and name */ if (check_input_term(state, ftr[4], &iterm) < 0) return -EINVAL; channels = (ftr[0] - 7) / csize - 1; master_bits = snd_usb_combine_bytes(ftr + 6, csize); if (channels > 0) first_ch_bits = snd_usb_combine_bytes(ftr + 6 + csize, csize); else first_ch_bits = 0; /* check all control types */ for (i = 0; i < 10; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -