⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbmixer.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	{ 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 + -