Blame src/mixer/simple_none.c

Packit 4a16fb
/**
Packit 4a16fb
 * \file mixer/simple_none.c
Packit 4a16fb
 * \brief Mixer Simple Element Class Interface
Packit 4a16fb
 * \author Jaroslav Kysela <perex@perex.cz>
Packit 4a16fb
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 * \date 2001-2004
Packit 4a16fb
 *
Packit 4a16fb
 * Mixer simple element class interface.
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  Mixer Interface - simple controls
Packit 4a16fb
 *  Copyright (c) 2000,2004 by Jaroslav Kysela <perex@perex.cz>
Packit 4a16fb
 *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
#include <stdio.h>
Packit 4a16fb
#include <stdlib.h>
Packit 4a16fb
#include <unistd.h>
Packit 4a16fb
#include <string.h>
Packit 4a16fb
#include <fcntl.h>
Packit 4a16fb
#include <sys/ioctl.h>
Packit 4a16fb
#include <assert.h>
Packit 4a16fb
#include <math.h>
Packit 4a16fb
#include <limits.h>
Packit 4a16fb
#include "local.h"
Packit 4a16fb
#include "config.h"
Packit 4a16fb
#include "mixer_simple.h"
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
#define MIXER_COMPARE_WEIGHT_SIMPLE_BASE        0
Packit 4a16fb
#define MIXER_COMPARE_WEIGHT_NEXT_BASE          10000000
Packit 4a16fb
#define MIXER_COMPARE_WEIGHT_NOT_FOUND          1000000000
Packit 4a16fb
Packit 4a16fb
typedef enum _selem_ctl_type {
Packit 4a16fb
	CTL_SINGLE,
Packit 4a16fb
	CTL_GLOBAL_ENUM,
Packit 4a16fb
	CTL_GLOBAL_SWITCH,
Packit 4a16fb
	CTL_GLOBAL_VOLUME,
Packit 4a16fb
	CTL_GLOBAL_ROUTE,
Packit 4a16fb
	CTL_PLAYBACK_ENUM,
Packit 4a16fb
	CTL_PLAYBACK_SWITCH,
Packit 4a16fb
	CTL_PLAYBACK_VOLUME,
Packit 4a16fb
	CTL_PLAYBACK_ROUTE,
Packit 4a16fb
	CTL_CAPTURE_ENUM,
Packit 4a16fb
	CTL_CAPTURE_SWITCH,
Packit 4a16fb
	CTL_CAPTURE_VOLUME,
Packit 4a16fb
	CTL_CAPTURE_ROUTE,
Packit 4a16fb
	CTL_CAPTURE_SOURCE,
Packit 4a16fb
	CTL_LAST = CTL_CAPTURE_SOURCE,
Packit 4a16fb
} selem_ctl_type_t;
Packit 4a16fb
Packit 4a16fb
typedef struct _selem_ctl {
Packit 4a16fb
	snd_hctl_elem_t *elem;
Packit 4a16fb
	snd_ctl_elem_type_t type;
Packit 4a16fb
	unsigned int inactive: 1;
Packit 4a16fb
	unsigned int values;
Packit 4a16fb
	long min, max;
Packit 4a16fb
} selem_ctl_t;
Packit 4a16fb
Packit 4a16fb
typedef struct _selem_none {
Packit 4a16fb
	sm_selem_t selem;
Packit 4a16fb
	selem_ctl_t ctls[CTL_LAST + 1];
Packit 4a16fb
	unsigned int capture_item;
Packit 4a16fb
	struct selem_str {
Packit 4a16fb
		unsigned int range: 1;	/* Forced range */
Packit 4a16fb
		unsigned int db_initialized: 1;
Packit 4a16fb
		unsigned int db_init_error: 1;
Packit 4a16fb
		long min, max;
Packit 4a16fb
		unsigned int channels;
Packit 4a16fb
		long vol[32];
Packit 4a16fb
		unsigned int sw;
Packit 4a16fb
		unsigned int *db_info;
Packit 4a16fb
	} str[2];
Packit 4a16fb
} selem_none_t;
Packit 4a16fb
Packit 4a16fb
static const struct mixer_name_table {
Packit 4a16fb
	const char *longname;
Packit 4a16fb
	const char *shortname;
Packit 4a16fb
} name_table[] = {
Packit 4a16fb
	{"Tone Control - Switch", "Tone"},
Packit 4a16fb
	{"Tone Control - Bass", "Bass"},
Packit 4a16fb
	{"Tone Control - Treble", "Treble"},
Packit 4a16fb
	{"Synth Tone Control - Switch", "Synth Tone"},
Packit 4a16fb
	{"Synth Tone Control - Bass", "Synth Bass"},
Packit 4a16fb
	{"Synth Tone Control - Treble", "Synth Treble"},
Packit 4a16fb
	{0, 0},
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
#endif /* !DOC_HIDDEN */
Packit 4a16fb
Packit 4a16fb
static const char *get_short_name(const char *lname)
Packit 4a16fb
{
Packit 4a16fb
	const struct mixer_name_table *p;
Packit 4a16fb
	for (p = name_table; p->longname; p++) {
Packit 4a16fb
		if (!strcmp(lname, p->longname))
Packit 4a16fb
			return p->shortname;
Packit 4a16fb
	}
Packit 4a16fb
	return lname;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
Packit 4a16fb
{
Packit 4a16fb
	int res;
Packit 4a16fb
Packit 4a16fb
	for (res = 0; *names; names++, res += coef) {
Packit 4a16fb
		if (!strncmp(*name, *names, strlen(*names))) {
Packit 4a16fb
			*name += strlen(*names);
Packit 4a16fb
			if (**name == ' ')
Packit 4a16fb
				(*name)++;
Packit 4a16fb
			return res+1;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	return MIXER_COMPARE_WEIGHT_NOT_FOUND;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_compare_weight(const char *name, unsigned int idx)
Packit 4a16fb
{
Packit 4a16fb
	static const char *const names[] = {
Packit 4a16fb
		"Master",
Packit 4a16fb
		"Headphone",
Packit 4a16fb
		"Speaker",
Packit 4a16fb
		"Tone",
Packit 4a16fb
		"Bass",
Packit 4a16fb
		"Treble",
Packit 4a16fb
		"3D Control",
Packit 4a16fb
		"PCM",
Packit 4a16fb
		"Front",
Packit 4a16fb
		"Surround",
Packit 4a16fb
		"Center",
Packit 4a16fb
		"LFE",
Packit 4a16fb
		"Side",
Packit 4a16fb
		"Synth",
Packit 4a16fb
		"FM",
Packit 4a16fb
		"Wave",
Packit 4a16fb
		"Music",
Packit 4a16fb
		"DSP",
Packit 4a16fb
		"Line",
Packit 4a16fb
		"CD",
Packit 4a16fb
		"Mic",
Packit 4a16fb
		"Video",
Packit 4a16fb
		"Zoom Video",
Packit 4a16fb
		"Phone",
Packit 4a16fb
		"I2S",
Packit 4a16fb
		"IEC958",
Packit 4a16fb
		"PC Speaker",
Packit 4a16fb
		"Beep",
Packit 4a16fb
		"Aux",
Packit 4a16fb
		"Mono",
Packit 4a16fb
		"Playback",
Packit 4a16fb
		"Capture",
Packit 4a16fb
		"Mix",
Packit 4a16fb
		NULL
Packit 4a16fb
	};
Packit 4a16fb
	static const char *const names1[] = {
Packit 4a16fb
		"-",
Packit 4a16fb
		NULL,
Packit 4a16fb
	};
Packit 4a16fb
	static const char *const names2[] = {
Packit 4a16fb
		"Mono",
Packit 4a16fb
		"Digital",
Packit 4a16fb
		"Switch",
Packit 4a16fb
		"Depth",
Packit 4a16fb
		"Wide",
Packit 4a16fb
		"Space",
Packit 4a16fb
		"Level",
Packit 4a16fb
		"Center",
Packit 4a16fb
		"Output",
Packit 4a16fb
		"Boost",
Packit 4a16fb
		"Tone",
Packit 4a16fb
		"Bass",
Packit 4a16fb
		"Treble",
Packit 4a16fb
		NULL,
Packit 4a16fb
	};
Packit 4a16fb
	const char *name1;
Packit 4a16fb
	int res, res1;
Packit 4a16fb
Packit 4a16fb
	if ((res = compare_mixer_priority_lookup((const char **)&name, names, 1000)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
Packit 4a16fb
		return MIXER_COMPARE_WEIGHT_NOT_FOUND;
Packit 4a16fb
	if (*name == '\0')
Packit 4a16fb
		goto __res;
Packit 4a16fb
	for (name1 = name; *name1 != '\0'; name1++);
Packit 4a16fb
	for (name1--; name1 != name && *name1 != ' '; name1--);
Packit 4a16fb
	while (name1 != name && *name1 == ' ')
Packit 4a16fb
		name1--;
Packit 4a16fb
	if (name1 != name) {
Packit 4a16fb
		for (; name1 != name && *name1 != ' '; name1--);
Packit 4a16fb
		name = name1;
Packit 4a16fb
		if ((res1 = compare_mixer_priority_lookup((const char **)&name, names1, 200)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
Packit 4a16fb
			return res;
Packit 4a16fb
		res += res1;
Packit 4a16fb
	} else {
Packit 4a16fb
		name = name1;
Packit 4a16fb
	}
Packit 4a16fb
	if ((res1 = compare_mixer_priority_lookup((const char **)&name, names2, 20)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
Packit 4a16fb
		return res;
Packit 4a16fb
      __res:
Packit 4a16fb
	return MIXER_COMPARE_WEIGHT_SIMPLE_BASE + res + idx;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static long to_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
Packit 4a16fb
{
Packit 4a16fb
	int64_t n;
Packit 4a16fb
	if (c->max == c->min)
Packit 4a16fb
		return s->str[dir].min;
Packit 4a16fb
	n = (int64_t) (value - c->min) * (s->str[dir].max - s->str[dir].min);
Packit 4a16fb
	return s->str[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static long from_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
Packit 4a16fb
{
Packit 4a16fb
	int64_t n;
Packit 4a16fb
	if (s->str[dir].max == s->str[dir].min)
Packit 4a16fb
		return c->min;
Packit 4a16fb
	n = (int64_t) (value - s->str[dir].min) * (c->max - c->min);
Packit 4a16fb
	return c->min + (n + (s->str[dir].max - s->str[dir].min) / 2) / (s->str[dir].max - s->str[dir].min);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < s->str[dir].channels; idx++) {
Packit 4a16fb
		unsigned int idx1 = idx;
Packit 4a16fb
		if (idx >= c->values)
Packit 4a16fb
			idx1 = 0;
Packit 4a16fb
		s->str[dir].vol[idx] =
Packit 4a16fb
			to_user(s, dir, c,
Packit 4a16fb
				snd_ctl_elem_value_get_integer(&ctl, idx1));
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < s->str[dir].channels; idx++) {
Packit 4a16fb
		unsigned int idx1 = idx;
Packit 4a16fb
		if (idx >= c->values)
Packit 4a16fb
			idx1 = 0;
Packit 4a16fb
		if (!snd_ctl_elem_value_get_integer(&ctl, idx1))
Packit 4a16fb
			s->str[dir].sw &= ~(1 << idx);
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < s->str[dir].channels; idx++) {
Packit 4a16fb
		unsigned int idx1 = idx;
Packit 4a16fb
		if (idx >= c->values)
Packit 4a16fb
			idx1 = 0;
Packit 4a16fb
		if (!snd_ctl_elem_value_get_integer(&ctl,
Packit 4a16fb
						    idx1 * c->values + idx1))
Packit 4a16fb
			s->str[dir].sw &= ~(1 << idx);
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_read_enum(selem_none_t *s)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	int type;
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
	type = CTL_GLOBAL_ENUM;
Packit 4a16fb
	if ((s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) ==
Packit 4a16fb
						(SM_CAP_CENUM | SM_CAP_PENUM))
Packit 4a16fb
		type = CTL_GLOBAL_ENUM;
Packit 4a16fb
	else if (s->selem.caps & SM_CAP_PENUM)
Packit 4a16fb
		type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
	else if (s->selem.caps & SM_CAP_CENUM)
Packit 4a16fb
		type = CTL_CAPTURE_ENUM;
Packit 4a16fb
	c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < s->str[0].channels; idx++) {
Packit 4a16fb
		unsigned int idx1 = idx;
Packit 4a16fb
		if (idx >= c->values)
Packit 4a16fb
			idx1 = 0;
Packit 4a16fb
		s->str[0].vol[idx] =
Packit 4a16fb
				snd_ctl_elem_value_get_enumerated(&ctl, idx1);
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int selem_read(snd_mixer_elem_t *elem)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s;
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err = 0;
Packit 4a16fb
	long pvol[32], cvol[32];
Packit 4a16fb
	unsigned int psw, csw;
Packit 4a16fb
Packit 4a16fb
	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
Packit 4a16fb
	s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
Packit 4a16fb
	memcpy(pvol, s->str[SM_PLAY].vol, sizeof(pvol));
Packit 4a16fb
	memset(&s->str[SM_PLAY].vol, 0, sizeof(s->str[SM_PLAY].vol));
Packit 4a16fb
	psw = s->str[SM_PLAY].sw;
Packit 4a16fb
	s->str[SM_PLAY].sw = ~0U;
Packit 4a16fb
	memcpy(cvol, s->str[SM_CAPT].vol, sizeof(cvol));
Packit 4a16fb
	memset(&s->str[SM_CAPT].vol, 0, sizeof(s->str[SM_CAPT].vol));
Packit 4a16fb
	csw = s->str[SM_CAPT].sw;
Packit 4a16fb
	s->str[SM_CAPT].sw = ~0U;
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_ENUM].elem) {
Packit 4a16fb
		err = elem_read_enum(s);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		goto __skip_cswitch;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_ENUM].elem) {
Packit 4a16fb
		err = elem_read_enum(s);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		goto __skip_cswitch;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_ENUM].elem) {
Packit 4a16fb
		err = elem_read_enum(s);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		goto __skip_cswitch;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_VOLUME].elem)
Packit 4a16fb
		err = elem_read_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
Packit 4a16fb
	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
Packit 4a16fb
		err = elem_read_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
Packit 4a16fb
	else if (s->ctls[CTL_SINGLE].elem &&
Packit 4a16fb
		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
Packit 4a16fb
		err = elem_read_volume(s, SM_PLAY, CTL_SINGLE);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) {
Packit 4a16fb
		s->str[SM_PLAY].sw = 0;
Packit 4a16fb
		goto __skip_pswitch;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
Packit 4a16fb
		err = elem_read_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
Packit 4a16fb
		err = elem_read_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_SINGLE].elem &&
Packit 4a16fb
	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
Packit 4a16fb
		err = elem_read_switch(s, SM_PLAY, CTL_SINGLE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
Packit 4a16fb
		err = elem_read_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
Packit 4a16fb
		err = elem_read_route(s, SM_PLAY, CTL_GLOBAL_ROUTE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
      __skip_pswitch:
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_VOLUME].elem)
Packit 4a16fb
		err = elem_read_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
Packit 4a16fb
	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
Packit 4a16fb
		err = elem_read_volume(s, SM_CAPT, CTL_GLOBAL_VOLUME);
Packit 4a16fb
	else if (s->ctls[CTL_SINGLE].elem &&
Packit 4a16fb
		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
Packit 4a16fb
		err = elem_read_volume(s, SM_CAPT, CTL_SINGLE);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) {
Packit 4a16fb
		s->str[SM_CAPT].sw = 0;
Packit 4a16fb
		goto __skip_cswitch;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
Packit 4a16fb
		err = elem_read_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
Packit 4a16fb
		err = elem_read_switch(s, SM_CAPT, CTL_GLOBAL_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_SINGLE].elem &&
Packit 4a16fb
	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
Packit 4a16fb
		err = elem_read_switch(s, SM_CAPT, CTL_SINGLE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
Packit 4a16fb
		err = elem_read_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
Packit 4a16fb
		err = elem_read_route(s, SM_CAPT, CTL_GLOBAL_ROUTE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
Packit 4a16fb
		snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
Packit 4a16fb
		err = snd_hctl_elem_read(c->elem, &ctl;;
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		for (idx = 0; idx < s->str[SM_CAPT].channels; idx++) {
Packit 4a16fb
			unsigned int idx1 = idx;
Packit 4a16fb
			if (idx >= c->values)
Packit 4a16fb
				idx1 = 0;
Packit 4a16fb
			if (snd_ctl_elem_value_get_enumerated(&ctl, idx1) !=
Packit 4a16fb
								s->capture_item)
Packit 4a16fb
				s->str[SM_CAPT].sw &= ~(1 << idx);
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
      __skip_cswitch:
Packit 4a16fb
Packit 4a16fb
	if (memcmp(pvol, s->str[SM_PLAY].vol, sizeof(pvol)) ||
Packit 4a16fb
	    psw != s->str[SM_PLAY].sw ||
Packit 4a16fb
	    memcmp(cvol, s->str[SM_CAPT].vol, sizeof(cvol)) ||
Packit 4a16fb
	    csw != s->str[SM_CAPT].sw)
Packit 4a16fb
		return 1;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < c->values; idx++)
Packit 4a16fb
		snd_ctl_elem_value_set_integer(&ctl, idx,
Packit 4a16fb
				from_user(s, dir, c, s->str[dir].vol[idx]));
Packit 4a16fb
	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < c->values; idx++)
Packit 4a16fb
		snd_ctl_elem_value_set_integer(&ctl, idx,
Packit 4a16fb
					!!(s->str[dir].sw & (1 << idx)));
Packit 4a16fb
	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < c->values; idx++)
Packit 4a16fb
		snd_ctl_elem_value_set_integer(&ctl, idx, !!val);
Packit 4a16fb
	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	selem_ctl_t *c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < c->values * c->values; idx++)
Packit 4a16fb
		snd_ctl_elem_value_set_integer(&ctl, idx, 0);
Packit 4a16fb
	for (idx = 0; idx < c->values; idx++)
Packit 4a16fb
		snd_ctl_elem_value_set_integer(&ctl, idx * c->values + idx,
Packit 4a16fb
					       !!(s->str[dir].sw & (1 << idx)));
Packit 4a16fb
	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int elem_write_enum(selem_none_t *s)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
	int type;
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
	type = CTL_GLOBAL_ENUM;
Packit 4a16fb
	if ((s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) ==
Packit 4a16fb
						(SM_CAP_CENUM | SM_CAP_PENUM))
Packit 4a16fb
		type = CTL_GLOBAL_ENUM;
Packit 4a16fb
	else if (s->selem.caps & SM_CAP_PENUM)
Packit 4a16fb
		type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
	else if (s->selem.caps & SM_CAP_CENUM)
Packit 4a16fb
		type = CTL_CAPTURE_ENUM;
Packit 4a16fb
	c = &s->ctls[type];
Packit 4a16fb
	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	for (idx = 0; idx < c->values; idx++)
Packit 4a16fb
		snd_ctl_elem_value_set_enumerated(&ctl, idx,
Packit 4a16fb
					(unsigned int)s->str[0].vol[idx]);
Packit 4a16fb
	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int selem_write_main(snd_mixer_elem_t *elem)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s;
Packit 4a16fb
	unsigned int idx;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
Packit 4a16fb
	s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_ENUM].elem)
Packit 4a16fb
		return elem_write_enum(s);
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_ENUM].elem)
Packit 4a16fb
		return elem_write_enum(s);
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_ENUM].elem)
Packit 4a16fb
		return elem_write_enum(s);
Packit 4a16fb
Packit 4a16fb
	if (s->ctls[CTL_SINGLE].elem) {
Packit 4a16fb
		if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
Packit 4a16fb
			err = elem_write_volume(s, SM_PLAY, CTL_SINGLE);
Packit 4a16fb
		else
Packit 4a16fb
			err = elem_write_switch(s, SM_PLAY, CTL_SINGLE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_VOLUME].elem) {
Packit 4a16fb
		err = elem_write_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
Packit 4a16fb
		if (s->ctls[CTL_PLAYBACK_SWITCH].elem &&
Packit 4a16fb
					s->ctls[CTL_CAPTURE_SWITCH].elem)
Packit 4a16fb
			err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH,
Packit 4a16fb
							 1);
Packit 4a16fb
		else
Packit 4a16fb
			err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_VOLUME].elem) {
Packit 4a16fb
		err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
Packit 4a16fb
		err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
Packit 4a16fb
		err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_VOLUME].elem) {
Packit 4a16fb
		err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
Packit 4a16fb
		err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
Packit 4a16fb
		err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
Packit 4a16fb
		snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
Packit 4a16fb
		if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		for (idx = 0; idx < c->values; idx++) {
Packit 4a16fb
			if (s->str[SM_CAPT].sw & (1 << idx))
Packit 4a16fb
				snd_ctl_elem_value_set_enumerated(&ctl,
Packit 4a16fb
							idx, s->capture_item);
Packit 4a16fb
		}
Packit 4a16fb
		if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		/* update the element, don't remove */
Packit 4a16fb
		err = selem_read(elem);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int selem_write(snd_mixer_elem_t *elem)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	
Packit 4a16fb
	err = selem_write_main(elem);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		selem_read(elem);
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void selem_free(snd_mixer_elem_t *elem)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *simple = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
Packit 4a16fb
	if (simple->selem.id)
Packit 4a16fb
		snd_mixer_selem_id_free(simple->selem.id);
Packit 4a16fb
	/* free db range information */
Packit 4a16fb
	free(simple->str[0].db_info);
Packit 4a16fb
	free(simple->str[1].db_info);
Packit 4a16fb
	free(simple);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int simple_update(snd_mixer_elem_t *melem)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *simple;
Packit 4a16fb
	unsigned int caps, pchannels, cchannels;
Packit 4a16fb
	long pmin, pmax, cmin, cmax;
Packit 4a16fb
	selem_ctl_t *ctl;
Packit 4a16fb
Packit 4a16fb
	caps = 0;
Packit 4a16fb
	pchannels = 0;
Packit 4a16fb
	pmin = LONG_MAX;
Packit 4a16fb
	pmax = LONG_MIN;
Packit 4a16fb
	cchannels = 0;
Packit 4a16fb
	cmin = LONG_MAX;
Packit 4a16fb
	cmax = LONG_MIN;
Packit 4a16fb
	assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE);
Packit 4a16fb
	simple = snd_mixer_elem_get_private(melem);
Packit 4a16fb
	ctl = &simple->ctls[CTL_SINGLE];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		pchannels = cchannels = ctl->values;
Packit 4a16fb
		if (ctl->type == SND_CTL_ELEM_TYPE_INTEGER) {
Packit 4a16fb
			caps |= SM_CAP_GVOLUME;
Packit 4a16fb
			pmin = cmin = ctl->min;
Packit 4a16fb
			pmax = cmax = ctl->max;
Packit 4a16fb
		} else
Packit 4a16fb
			caps |= SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_GLOBAL_SWITCH];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_GLOBAL_ROUTE];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_GLOBAL_VOLUME];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		if (pmin > ctl->min)
Packit 4a16fb
			pmin = ctl->min;
Packit 4a16fb
		if (pmax < ctl->max)
Packit 4a16fb
			pmax = ctl->max;
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		if (cmin > ctl->min)
Packit 4a16fb
			cmin = ctl->min;
Packit 4a16fb
		if (cmax < ctl->max)
Packit 4a16fb
			cmax = ctl->max;
Packit 4a16fb
		caps |= SM_CAP_GVOLUME;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_PLAYBACK_SWITCH];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_PSWITCH;
Packit 4a16fb
		caps &= ~SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_PLAYBACK_ROUTE];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_PSWITCH;
Packit 4a16fb
		caps &= ~SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_CAPTURE_SWITCH];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_CSWITCH;
Packit 4a16fb
		caps &= ~SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_CAPTURE_ROUTE];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_CSWITCH;
Packit 4a16fb
		caps &= ~SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_PLAYBACK_VOLUME];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		if (pmin > ctl->min)
Packit 4a16fb
			pmin = ctl->min;
Packit 4a16fb
		if (pmax < ctl->max)
Packit 4a16fb
			pmax = ctl->max;
Packit 4a16fb
		caps |= SM_CAP_PVOLUME;
Packit 4a16fb
		caps &= ~SM_CAP_GVOLUME;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_CAPTURE_VOLUME];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		if (cmin > ctl->min)
Packit 4a16fb
			cmin = ctl->min;
Packit 4a16fb
		if (cmax < ctl->max)
Packit 4a16fb
			cmax = ctl->max;
Packit 4a16fb
		caps |= SM_CAP_CVOLUME;
Packit 4a16fb
		caps &= ~SM_CAP_GVOLUME;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_CAPTURE_SOURCE];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (cchannels < ctl->values)
Packit 4a16fb
			cchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_CSWITCH | SM_CAP_CSWITCH_EXCL;
Packit 4a16fb
		caps &= ~SM_CAP_GSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_GLOBAL_ENUM];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_PENUM | SM_CAP_CENUM;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_PLAYBACK_ENUM];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_PENUM;
Packit 4a16fb
	}
Packit 4a16fb
	ctl = &simple->ctls[CTL_CAPTURE_ENUM];
Packit 4a16fb
	if (ctl->elem) {
Packit 4a16fb
		if (pchannels < ctl->values)
Packit 4a16fb
			pchannels = ctl->values;
Packit 4a16fb
		caps |= SM_CAP_CENUM;
Packit 4a16fb
	}
Packit 4a16fb
	if (pchannels > 32)
Packit 4a16fb
		pchannels = 32;
Packit 4a16fb
	if (cchannels > 32)
Packit 4a16fb
		cchannels = 32;
Packit 4a16fb
	if (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))
Packit 4a16fb
		caps |= SM_CAP_PSWITCH_JOIN;
Packit 4a16fb
	if (caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME))
Packit 4a16fb
		caps |= SM_CAP_PVOLUME_JOIN;
Packit 4a16fb
	if (caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))
Packit 4a16fb
		caps |= SM_CAP_CSWITCH_JOIN;
Packit 4a16fb
	if (caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME))
Packit 4a16fb
		caps |= SM_CAP_CVOLUME_JOIN;
Packit 4a16fb
	if (pchannels > 1 || cchannels > 1) {
Packit 4a16fb
		if (simple->ctls[CTL_SINGLE].elem &&
Packit 4a16fb
		    simple->ctls[CTL_SINGLE].values > 1) {
Packit 4a16fb
			if (caps & SM_CAP_GSWITCH)
Packit 4a16fb
				caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
Packit 4a16fb
			else
Packit 4a16fb
				caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
Packit 4a16fb
		}
Packit 4a16fb
		if (simple->ctls[CTL_GLOBAL_ROUTE].elem ||
Packit 4a16fb
		    (simple->ctls[CTL_GLOBAL_SWITCH].elem &&
Packit 4a16fb
		     simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) {
Packit 4a16fb
			caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
Packit 4a16fb
		}
Packit 4a16fb
		if (simple->ctls[CTL_GLOBAL_VOLUME].elem &&
Packit 4a16fb
		    simple->ctls[CTL_GLOBAL_VOLUME].values > 1) {
Packit 4a16fb
			caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	if (pchannels > 1) {
Packit 4a16fb
		if (simple->ctls[CTL_PLAYBACK_ROUTE].elem ||
Packit 4a16fb
		    (simple->ctls[CTL_PLAYBACK_SWITCH].elem &&
Packit 4a16fb
		     simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) {
Packit 4a16fb
			caps &= ~SM_CAP_PSWITCH_JOIN;
Packit 4a16fb
		}
Packit 4a16fb
		if (simple->ctls[CTL_PLAYBACK_VOLUME].elem &&
Packit 4a16fb
		    simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) {
Packit 4a16fb
			caps &= ~SM_CAP_PVOLUME_JOIN;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	if (cchannels > 1) {
Packit 4a16fb
		if (simple->ctls[CTL_CAPTURE_ROUTE].elem ||
Packit 4a16fb
		    (simple->ctls[CTL_CAPTURE_SWITCH].elem &&
Packit 4a16fb
		     simple->ctls[CTL_CAPTURE_SWITCH].values > 1) ||
Packit 4a16fb
		    (simple->ctls[CTL_CAPTURE_SOURCE].elem &&
Packit 4a16fb
		     simple->ctls[CTL_CAPTURE_SOURCE].values > 1)) {
Packit 4a16fb
			caps &= ~SM_CAP_CSWITCH_JOIN;
Packit 4a16fb
		}
Packit 4a16fb
		if (simple->ctls[CTL_CAPTURE_VOLUME].elem &&
Packit 4a16fb
		    simple->ctls[CTL_CAPTURE_VOLUME].values > 1) {
Packit 4a16fb
			caps &= ~SM_CAP_CVOLUME_JOIN;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* exceptions */
Packit 4a16fb
	if ((caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) &&
Packit 4a16fb
	    (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == (caps & SM_CAP_GSWITCH)) {
Packit 4a16fb
		caps &= ~(SM_CAP_GSWITCH|SM_CAP_CSWITCH_JOIN|SM_CAP_CSWITCH_EXCL);
Packit 4a16fb
		caps |= SM_CAP_PSWITCH;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if ((caps & SM_CAP_GSWITCH) &&
Packit 4a16fb
	    (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
Packit 4a16fb
		caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;
Packit 4a16fb
Packit 4a16fb
	if ((caps & SM_CAP_GVOLUME) &&
Packit 4a16fb
	    (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
Packit 4a16fb
		caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;
Packit 4a16fb
Packit 4a16fb
	simple->selem.caps = caps;
Packit 4a16fb
	simple->str[SM_PLAY].channels = pchannels;
Packit 4a16fb
	if (!simple->str[SM_PLAY].range) {
Packit 4a16fb
		simple->str[SM_PLAY].min = pmin != LONG_MAX ? pmin : 0;
Packit 4a16fb
		simple->str[SM_PLAY].max = pmax != LONG_MIN ? pmax : 0;
Packit 4a16fb
	}
Packit 4a16fb
	simple->str[SM_CAPT].channels = cchannels;
Packit 4a16fb
	if (!simple->str[SM_CAPT].range) {
Packit 4a16fb
		simple->str[SM_CAPT].min = cmin != LONG_MAX ? cmin : 0;
Packit 4a16fb
		simple->str[SM_CAPT].max = cmax != LONG_MIN ? cmax : 0;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}	   
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
static const struct suf {
Packit 4a16fb
	const char *suffix;
Packit 4a16fb
	selem_ctl_type_t type;
Packit 4a16fb
} suffixes[] = {
Packit 4a16fb
	{" Playback Enum", CTL_PLAYBACK_ENUM},
Packit 4a16fb
	{" Playback Switch", CTL_PLAYBACK_SWITCH},
Packit 4a16fb
	{" Playback Route", CTL_PLAYBACK_ROUTE},
Packit 4a16fb
	{" Playback Volume", CTL_PLAYBACK_VOLUME},
Packit 4a16fb
	{" Capture Enum", CTL_CAPTURE_ENUM},
Packit 4a16fb
	{" Capture Switch", CTL_CAPTURE_SWITCH},
Packit 4a16fb
	{" Capture Route", CTL_CAPTURE_ROUTE},
Packit 4a16fb
	{" Capture Volume", CTL_CAPTURE_VOLUME},
Packit 4a16fb
	{" Enum", CTL_GLOBAL_ENUM},
Packit 4a16fb
	{" Switch", CTL_GLOBAL_SWITCH},
Packit 4a16fb
	{" Route", CTL_GLOBAL_ROUTE},
Packit 4a16fb
	{" Volume", CTL_GLOBAL_VOLUME},
Packit 4a16fb
	{NULL, 0}
Packit 4a16fb
};
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
/* Return base length or 0 on failure */
Packit 4a16fb
static int base_len(const char *name, selem_ctl_type_t *type)
Packit 4a16fb
{
Packit 4a16fb
	const struct suf *p;
Packit 4a16fb
	size_t nlen = strlen(name);
Packit 4a16fb
	p = suffixes;
Packit 4a16fb
	while (p->suffix) {
Packit 4a16fb
		size_t slen = strlen(p->suffix);
Packit 4a16fb
		size_t l;
Packit 4a16fb
		if (nlen > slen) {
Packit 4a16fb
			l = nlen - slen;
Packit 4a16fb
			if (strncmp(name + l, p->suffix, slen) == 0 &&
Packit 4a16fb
			    (l < 1 || name[l-1] != '-')) {	/* 3D Control - Switch */
Packit 4a16fb
				*type = p->type;
Packit 4a16fb
				return l;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
		p++;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* Special case - handle "Input Source" as a capture route.
Packit 4a16fb
	 * Note that it's *NO* capture source.  A capture source is split over
Packit 4a16fb
	 * sub-elements, and multiple capture-sources will result in an error.
Packit 4a16fb
	 * That's why some drivers use "Input Source" as a workaround.
Packit 4a16fb
	 * Hence, this is a workaround for a workaround to get the things
Packit 4a16fb
	 * straight back again.  Sigh.
Packit 4a16fb
	 */
Packit 4a16fb
	if (!strcmp(name, "Input Source")) {
Packit 4a16fb
		*type = CTL_CAPTURE_ROUTE;
Packit 4a16fb
		return strlen(name);
Packit 4a16fb
	}
Packit 4a16fb
	if (strstr(name, "3D Control")) {
Packit 4a16fb
		if (strstr(name, "Depth")) {
Packit 4a16fb
			*type = CTL_PLAYBACK_VOLUME;
Packit 4a16fb
			return strlen(name);
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 * Simple Mixer Operations
Packit 4a16fb
 */
Packit 4a16fb
	
Packit 4a16fb
static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GVOLUME)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	if ((unsigned int) channel >= s->str[dir].channels)
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (value < s->str[dir].min || value > s->str[dir].max)
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (s->selem.caps & 
Packit 4a16fb
	    (dir == SM_PLAY ? SM_CAP_PVOLUME_JOIN : SM_CAP_CVOLUME_JOIN))
Packit 4a16fb
		channel = 0;
Packit 4a16fb
	if (value != s->str[dir].vol[channel]) {
Packit 4a16fb
		s->str[dir].vol[channel] = value;
Packit 4a16fb
		return 1;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	if ((unsigned int) channel >= s->str[dir].channels)
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (s->selem.caps & 
Packit 4a16fb
	    (dir == SM_PLAY ? SM_CAP_PSWITCH_JOIN : SM_CAP_CSWITCH_JOIN))
Packit 4a16fb
		channel = 0;
Packit 4a16fb
	if (value) {
Packit 4a16fb
		if (!(s->str[dir].sw & (1 << channel))) {
Packit 4a16fb
			s->str[dir].sw |= 1 << channel;
Packit 4a16fb
			return 1;
Packit 4a16fb
		}
Packit 4a16fb
	} else {
Packit 4a16fb
		if (s->str[dir].sw & (1 << channel)) {
Packit 4a16fb
			s->str[dir].sw &= ~(1 << channel);
Packit 4a16fb
			return 1;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	
Packit 4a16fb
	switch (cmd) {
Packit 4a16fb
Packit 4a16fb
	case SM_OPS_IS_ACTIVE: {
Packit 4a16fb
		selem_ctl_type_t ctl;
Packit 4a16fb
		for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++)
Packit 4a16fb
			if (s->ctls[ctl].elem != NULL && s->ctls[ctl].inactive)
Packit 4a16fb
				return 0;
Packit 4a16fb
		return 1;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	case SM_OPS_IS_MONO:
Packit 4a16fb
		return s->str[dir].channels == 1;
Packit 4a16fb
Packit 4a16fb
	case SM_OPS_IS_CHANNEL:
Packit 4a16fb
		return (unsigned int) val < s->str[dir].channels;
Packit 4a16fb
Packit 4a16fb
	case SM_OPS_IS_ENUMERATED:
Packit 4a16fb
		if (val == 1) {
Packit 4a16fb
			if (dir == SM_PLAY && (s->selem.caps & SM_CAP_PENUM) && !(s->selem.caps & SM_CAP_CENUM) )
Packit 4a16fb
				return 1;
Packit 4a16fb
			if (dir == SM_CAPT && (s->selem.caps & SM_CAP_CENUM) && !(s->selem.caps & SM_CAP_PENUM) )
Packit 4a16fb
				return 1;
Packit 4a16fb
			return 0;
Packit 4a16fb
		}
Packit 4a16fb
		if (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) )
Packit 4a16fb
			return 1;
Packit 4a16fb
		return 0;
Packit 4a16fb
	
Packit 4a16fb
	case SM_OPS_IS_ENUMCNT:
Packit 4a16fb
		/* Both */
Packit 4a16fb
		if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) ) {
Packit 4a16fb
			if (! s->ctls[CTL_GLOBAL_ENUM].elem)
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			return s->ctls[CTL_GLOBAL_ENUM].max;
Packit 4a16fb
		/* Only Playback */
Packit 4a16fb
		} else if (s->selem.caps & SM_CAP_PENUM ) {
Packit 4a16fb
			if (! s->ctls[CTL_PLAYBACK_ENUM].elem)
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			return s->ctls[CTL_PLAYBACK_ENUM].max;
Packit 4a16fb
		/* Only Capture */
Packit 4a16fb
		} else if (s->selem.caps & SM_CAP_CENUM ) {
Packit 4a16fb
			if (! s->ctls[CTL_CAPTURE_ENUM].elem)
Packit 4a16fb
				return -EINVAL;
Packit 4a16fb
			return s->ctls[CTL_CAPTURE_ENUM].max;
Packit 4a16fb
		}
Packit 4a16fb
Packit 4a16fb
	}
Packit 4a16fb
	
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_range_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			 long *min, long *max)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	*min = s->str[dir].min;
Packit 4a16fb
	*max = s->str[dir].max;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int set_range_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			 long min, long max)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	s->str[dir].range = 1;
Packit 4a16fb
	s->str[dir].min = min;
Packit 4a16fb
	s->str[dir].max = max;
Packit 4a16fb
	if ((err = selem_read(elem)) < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			  snd_mixer_selem_channel_id_t channel, long *value)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GVOLUME)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	if ((unsigned int) channel >= s->str[dir].channels)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	*value = s->str[dir].vol[channel];
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec);
Packit 4a16fb
Packit 4a16fb
static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
Packit 4a16fb
			 long volume, long *db_gain)
Packit 4a16fb
{
Packit 4a16fb
	if (init_db_range(ctl, rec) < 0)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	return snd_tlv_convert_to_dB(rec->db_info, rec->min, rec->max,
Packit 4a16fb
				     volume, db_gain);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* initialize dB range information, reading TLV via hcontrol
Packit 4a16fb
 */
Packit 4a16fb
static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_info_t info = {0};
Packit 4a16fb
	unsigned int *tlv = NULL;
Packit 4a16fb
	const unsigned int tlv_size = 4096;
Packit 4a16fb
	unsigned int *dbrec;
Packit 4a16fb
	int db_size;
Packit 4a16fb
Packit 4a16fb
	if (rec->db_init_error)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	if (rec->db_initialized)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	if (snd_hctl_elem_info(ctl, &info) < 0)
Packit 4a16fb
		goto error;
Packit 4a16fb
	if (!snd_ctl_elem_info_is_tlv_readable(&info))
Packit 4a16fb
		goto error;
Packit 4a16fb
	tlv = malloc(tlv_size);
Packit 4a16fb
	if (!tlv)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0)
Packit 4a16fb
		goto error;
Packit 4a16fb
	db_size = snd_tlv_parse_dB_info(tlv, tlv_size, &dbrec);
Packit 4a16fb
	if (db_size < 0)
Packit 4a16fb
		goto error;
Packit 4a16fb
	rec->db_info = malloc(db_size);
Packit 4a16fb
	if (!rec->db_info)
Packit 4a16fb
		goto error;
Packit 4a16fb
	memcpy(rec->db_info, dbrec, db_size);
Packit 4a16fb
	free(tlv);
Packit 4a16fb
	rec->db_initialized = 1;
Packit 4a16fb
	return 0;
Packit 4a16fb
Packit 4a16fb
 error:
Packit 4a16fb
	free(tlv);
Packit 4a16fb
	rec->db_init_error = 1;
Packit 4a16fb
	return -EINVAL;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* get selem_ctl for TLV access */
Packit 4a16fb
static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir)
Packit 4a16fb
{
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
	if (dir == SM_PLAY)
Packit 4a16fb
		c = &s->ctls[CTL_PLAYBACK_VOLUME];
Packit 4a16fb
	else if (dir == SM_CAPT)
Packit 4a16fb
		c = &s->ctls[CTL_CAPTURE_VOLUME];
Packit 4a16fb
	else
Packit 4a16fb
		return NULL;
Packit 4a16fb
	if (! c->elem) {
Packit 4a16fb
		c = &s->ctls[CTL_GLOBAL_VOLUME];
Packit 4a16fb
		if (! c->elem)
Packit 4a16fb
			return NULL;
Packit 4a16fb
	}
Packit 4a16fb
	if (c->type != SND_CTL_ELEM_TYPE_INTEGER)
Packit 4a16fb
		return NULL;
Packit 4a16fb
	return c;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_dB_range(snd_hctl_elem_t *ctl, struct selem_str *rec,
Packit 4a16fb
			long *min, long *max)
Packit 4a16fb
{
Packit 4a16fb
	if (init_db_range(ctl, rec) < 0)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
Packit 4a16fb
	return snd_tlv_get_dB_range(rec->db_info, rec->min, rec->max, min, max);
Packit 4a16fb
}
Packit 4a16fb
	
Packit 4a16fb
static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			    long *min, long *max)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GVOLUME)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	c = get_selem_ctl(s, dir);
Packit 4a16fb
	if (! c)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	return get_dB_range(c->elem, &s->str[dir], min, max);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int convert_from_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
Packit 4a16fb
			   long db_gain, long *value, int xdir)
Packit 4a16fb
{
Packit 4a16fb
	if (init_db_range(ctl, rec) < 0)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
Packit 4a16fb
	return snd_tlv_convert_from_dB(rec->db_info, rec->min, rec->max,
Packit 4a16fb
				       db_gain, value, xdir);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int ask_vol_dB_ops(snd_mixer_elem_t *elem,
Packit 4a16fb
			  int dir,
Packit 4a16fb
			  long value,
Packit 4a16fb
			  long *dBvalue)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
Packit 4a16fb
	c = get_selem_ctl(s, dir);
Packit 4a16fb
	if (! c)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	int res = convert_to_dB(c->elem, &s->str[dir], value, dBvalue);
Packit 4a16fb
	return res;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_dB_ops(snd_mixer_elem_t *elem,
Packit 4a16fb
                      int dir,
Packit 4a16fb
                      snd_mixer_selem_channel_id_t channel,
Packit 4a16fb
                      long *value)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
	int err;
Packit 4a16fb
	long volume, db_gain;
Packit 4a16fb
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GVOLUME)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	c = get_selem_ctl(s, dir);
Packit 4a16fb
	if (! c)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	if ((err = get_volume_ops(elem, dir, channel, &volume)) < 0)
Packit 4a16fb
		goto _err;
Packit 4a16fb
	if ((err = convert_to_dB(c->elem, &s->str[dir], volume, &db_gain)) < 0)
Packit 4a16fb
		goto _err;
Packit 4a16fb
	err = 0;
Packit 4a16fb
	*value = db_gain;
Packit 4a16fb
 _err:
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			  snd_mixer_selem_channel_id_t channel, int *value)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GSWITCH)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	if ((unsigned int) channel >= s->str[dir].channels)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	*value = !!(s->str[dir].sw & (1 << channel));
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			  snd_mixer_selem_channel_id_t channel, long value)
Packit 4a16fb
{
Packit 4a16fb
	int changed;
Packit 4a16fb
	changed = _snd_mixer_selem_set_volume(elem, dir, channel, value);
Packit 4a16fb
	if (changed < 0)
Packit 4a16fb
		return changed;
Packit 4a16fb
	if (changed)
Packit 4a16fb
		return selem_write(elem);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int ask_dB_vol_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
		          long dbValue, long *value, int xdir)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GVOLUME)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	c = get_selem_ctl(s, dir);
Packit 4a16fb
	if (! c)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	return convert_from_dB(c->elem, &s->str[dir], dbValue, value, xdir);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int set_dB_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
		      snd_mixer_selem_channel_id_t channel,
Packit 4a16fb
		      long db_gain, int xdir)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	selem_ctl_t *c;
Packit 4a16fb
	long value;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GVOLUME)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	c = get_selem_ctl(s, dir);
Packit 4a16fb
	if (! c)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	err = convert_from_dB(c->elem, &s->str[dir], db_gain, &value, xdir);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return set_volume_ops(elem, dir, channel, value);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
Packit 4a16fb
			  snd_mixer_selem_channel_id_t channel, int value)
Packit 4a16fb
{
Packit 4a16fb
	int changed;
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	if (s->selem.caps & SM_CAP_GSWITCH)
Packit 4a16fb
		dir = SM_PLAY;
Packit 4a16fb
	if (dir == SM_PLAY) {
Packit 4a16fb
		if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)))
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
	} else {
Packit 4a16fb
		if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)))
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	changed = _snd_mixer_selem_set_switch(elem, dir, channel, value);
Packit 4a16fb
	if (changed < 0)
Packit 4a16fb
		return changed;
Packit 4a16fb
	if (changed)
Packit 4a16fb
		return selem_write(elem);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int enum_item_name_ops(snd_mixer_elem_t *elem,
Packit 4a16fb
			      unsigned int item,
Packit 4a16fb
			      size_t maxlen, char *buf)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	snd_ctl_elem_info_t info = {0};
Packit 4a16fb
	snd_hctl_elem_t *helem;
Packit 4a16fb
	int type;
Packit 4a16fb
Packit 4a16fb
	type = CTL_GLOBAL_ENUM;
Packit 4a16fb
	helem = s->ctls[type].elem;
Packit 4a16fb
	if (!helem) {
Packit 4a16fb
		type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
		helem = s->ctls[type].elem;
Packit 4a16fb
	}
Packit 4a16fb
	if (!helem) {
Packit 4a16fb
		type = CTL_CAPTURE_ENUM;
Packit 4a16fb
		helem = s->ctls[type].elem;
Packit 4a16fb
	}
Packit 4a16fb
	assert(helem);
Packit 4a16fb
	if (item >= (unsigned int)s->ctls[type].max)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	snd_hctl_elem_info(helem, &info;;
Packit 4a16fb
	snd_ctl_elem_info_set_item(&info, item);
Packit 4a16fb
	snd_hctl_elem_info(helem, &info;;
Packit 4a16fb
	strncpy(buf, snd_ctl_elem_info_get_item_name(&info), maxlen);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int get_enum_item_ops(snd_mixer_elem_t *elem,
Packit 4a16fb
			     snd_mixer_selem_channel_id_t channel,
Packit 4a16fb
			     unsigned int *itemp)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	snd_hctl_elem_t *helem;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	if ((unsigned int) channel >= s->str[0].channels)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	helem = s->ctls[CTL_GLOBAL_ENUM].elem;
Packit 4a16fb
	if (!helem) helem = s->ctls[CTL_PLAYBACK_ENUM].elem;
Packit 4a16fb
	if (!helem) helem = s->ctls[CTL_CAPTURE_ENUM].elem;
Packit 4a16fb
	assert(helem);
Packit 4a16fb
	err = snd_hctl_elem_read(helem, &ctl;;
Packit 4a16fb
	if (! err)
Packit 4a16fb
		*itemp = snd_ctl_elem_value_get_enumerated(&ctl, channel);
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int set_enum_item_ops(snd_mixer_elem_t *elem,
Packit 4a16fb
			     snd_mixer_selem_channel_id_t channel,
Packit 4a16fb
			     unsigned int item)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *s = snd_mixer_elem_get_private(elem);
Packit 4a16fb
	snd_ctl_elem_value_t ctl = {0};
Packit 4a16fb
	snd_hctl_elem_t *helem;
Packit 4a16fb
	int err;
Packit 4a16fb
	int type;
Packit 4a16fb
Packit 4a16fb
	if ((unsigned int) channel >= s->str[0].channels) {
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	type = CTL_GLOBAL_ENUM;
Packit 4a16fb
	helem = s->ctls[type].elem;
Packit 4a16fb
	if (!helem) {
Packit 4a16fb
		type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
		helem = s->ctls[type].elem;
Packit 4a16fb
	}
Packit 4a16fb
	if (!helem) {
Packit 4a16fb
		type = CTL_CAPTURE_ENUM;
Packit 4a16fb
		helem = s->ctls[type].elem;
Packit 4a16fb
	}
Packit 4a16fb
	assert(helem);
Packit 4a16fb
	if (item >= (unsigned int)s->ctls[type].max) {
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	err = snd_hctl_elem_read(helem, &ctl;;
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	snd_ctl_elem_value_set_enumerated(&ctl, channel, item);
Packit 4a16fb
	return snd_hctl_elem_write(helem, &ctl;;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static struct sm_elem_ops simple_none_ops = {
Packit 4a16fb
	.is		= is_ops,
Packit 4a16fb
	.get_range	= get_range_ops,
Packit 4a16fb
	.get_dB_range	= get_dB_range_ops,
Packit 4a16fb
	.set_range	= set_range_ops,
Packit 4a16fb
	.ask_vol_dB	= ask_vol_dB_ops,
Packit 4a16fb
	.ask_dB_vol	= ask_dB_vol_ops,
Packit 4a16fb
	.get_volume	= get_volume_ops,
Packit 4a16fb
	.get_dB		= get_dB_ops,
Packit 4a16fb
	.set_volume	= set_volume_ops,
Packit 4a16fb
	.set_dB		= set_dB_ops,
Packit 4a16fb
	.get_switch	= get_switch_ops,
Packit 4a16fb
	.set_switch	= set_switch_ops,
Packit 4a16fb
	.enum_item_name	= enum_item_name_ops,
Packit 4a16fb
	.get_enum_item	= get_enum_item_ops,
Packit 4a16fb
	.set_enum_item	= set_enum_item_ops
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
static int simple_add1(snd_mixer_class_t *class, const char *name,
Packit 4a16fb
		       snd_hctl_elem_t *helem, selem_ctl_type_t type,
Packit 4a16fb
		       unsigned int value)
Packit 4a16fb
{
Packit 4a16fb
	snd_mixer_elem_t *melem;
Packit 4a16fb
	snd_mixer_selem_id_t *id;
Packit 4a16fb
	int new = 0;
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_ctl_elem_info_t info = {0};
Packit 4a16fb
	selem_none_t *simple;
Packit 4a16fb
	const char *name1;
Packit 4a16fb
	snd_ctl_elem_type_t ctype;
Packit 4a16fb
	unsigned long values;
Packit 4a16fb
Packit 4a16fb
	err = snd_hctl_elem_info(helem, &info;;
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	ctype = snd_ctl_elem_info_get_type(&info;;
Packit 4a16fb
	values = snd_ctl_elem_info_get_count(&info;;
Packit 4a16fb
	switch (type) {
Packit 4a16fb
	case CTL_SINGLE:
Packit 4a16fb
		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED)
Packit 4a16fb
			type = CTL_GLOBAL_ENUM;
Packit 4a16fb
		else if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN &&
Packit 4a16fb
		         ctype != SND_CTL_ELEM_TYPE_INTEGER)
Packit 4a16fb
			return 0;
Packit 4a16fb
		break;
Packit 4a16fb
	case CTL_GLOBAL_ROUTE:
Packit 4a16fb
	case CTL_PLAYBACK_ROUTE:
Packit 4a16fb
	case CTL_CAPTURE_ROUTE:
Packit 4a16fb
	{
Packit 4a16fb
		unsigned int n;
Packit 4a16fb
		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
Packit 4a16fb
			if (type == CTL_PLAYBACK_ROUTE)
Packit 4a16fb
				type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
			else if (type == CTL_CAPTURE_ROUTE)
Packit 4a16fb
				type = CTL_CAPTURE_ENUM;
Packit 4a16fb
			else
Packit 4a16fb
				type = CTL_GLOBAL_ENUM;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
Packit 4a16fb
			return 0;
Packit 4a16fb
#ifdef HAVE_SOFT_FLOAT
Packit 4a16fb
		/* up to 256 channels */
Packit 4a16fb
		for (n = 1; n < 256; n++)
Packit 4a16fb
			if (n * n == values)
Packit 4a16fb
				break;
Packit 4a16fb
#else
Packit 4a16fb
		n = sqrt((double)values);
Packit 4a16fb
#endif
Packit 4a16fb
		if (n * n != values)
Packit 4a16fb
			return 0;
Packit 4a16fb
		values = n;
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
	case CTL_GLOBAL_SWITCH:
Packit 4a16fb
	case CTL_PLAYBACK_SWITCH:
Packit 4a16fb
	case CTL_CAPTURE_SWITCH:
Packit 4a16fb
		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
Packit 4a16fb
			if (type == CTL_PLAYBACK_SWITCH)
Packit 4a16fb
				type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
			else if (type == CTL_CAPTURE_SWITCH)
Packit 4a16fb
				type = CTL_CAPTURE_ENUM;
Packit 4a16fb
			else
Packit 4a16fb
				type = CTL_GLOBAL_ENUM;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
Packit 4a16fb
			return 0;
Packit 4a16fb
		break;
Packit 4a16fb
	case CTL_GLOBAL_VOLUME:
Packit 4a16fb
	case CTL_PLAYBACK_VOLUME:
Packit 4a16fb
	case CTL_CAPTURE_VOLUME:
Packit 4a16fb
		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
Packit 4a16fb
			if (type == CTL_PLAYBACK_VOLUME)
Packit 4a16fb
				type = CTL_PLAYBACK_ENUM;
Packit 4a16fb
			else if (type == CTL_CAPTURE_VOLUME)
Packit 4a16fb
				type = CTL_CAPTURE_ENUM;
Packit 4a16fb
			else
Packit 4a16fb
				type = CTL_GLOBAL_ENUM;
Packit 4a16fb
			break;
Packit 4a16fb
		}
Packit 4a16fb
		if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
Packit 4a16fb
			return 0;
Packit 4a16fb
		break;
Packit 4a16fb
	case CTL_CAPTURE_SOURCE:
Packit 4a16fb
		if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED)
Packit 4a16fb
			return 0;
Packit 4a16fb
		break;
Packit 4a16fb
	case CTL_GLOBAL_ENUM:
Packit 4a16fb
	case CTL_PLAYBACK_ENUM:
Packit 4a16fb
	case CTL_CAPTURE_ENUM:
Packit 4a16fb
		if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED)
Packit 4a16fb
			return 0;
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		assert(0);
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
	name1 = get_short_name(name);
Packit 4a16fb
	if (snd_mixer_selem_id_malloc(&id))
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	snd_mixer_selem_id_set_name(id, name1);
Packit 4a16fb
	snd_mixer_selem_id_set_index(id, snd_hctl_elem_get_index(helem));
Packit 4a16fb
	melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
Packit 4a16fb
	if (!melem) {
Packit 4a16fb
		simple = calloc(1, sizeof(*simple));
Packit 4a16fb
		if (!simple) {
Packit 4a16fb
			snd_mixer_selem_id_free(id);
Packit 4a16fb
			return -ENOMEM;
Packit 4a16fb
		}
Packit 4a16fb
		simple->selem.id = id;
Packit 4a16fb
		simple->selem.ops = &simple_none_ops;
Packit 4a16fb
		err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
Packit 4a16fb
			get_compare_weight(
Packit 4a16fb
				snd_mixer_selem_id_get_name(simple->selem.id),
Packit 4a16fb
				snd_mixer_selem_id_get_index(simple->selem.id)),
Packit 4a16fb
			simple, selem_free);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			snd_mixer_selem_id_free(id);
Packit 4a16fb
			free(simple);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		new = 1;
Packit 4a16fb
	} else {
Packit 4a16fb
		simple = snd_mixer_elem_get_private(melem);
Packit 4a16fb
		snd_mixer_selem_id_free(id);
Packit 4a16fb
	}
Packit 4a16fb
	if (simple->ctls[type].elem) {
Packit 4a16fb
		SNDERR("helem (%s,'%s',%u,%u,%u) appears twice or more",
Packit 4a16fb
		       snd_ctl_elem_iface_name(
Packit 4a16fb
				snd_hctl_elem_get_interface(helem)),
Packit 4a16fb
		       snd_hctl_elem_get_name(helem),
Packit 4a16fb
		       snd_hctl_elem_get_index(helem),
Packit 4a16fb
		       snd_hctl_elem_get_device(helem),
Packit 4a16fb
		       snd_hctl_elem_get_subdevice(helem));
Packit 4a16fb
		err = -EINVAL;
Packit 4a16fb
		goto __error;
Packit 4a16fb
	}
Packit 4a16fb
	simple->ctls[type].elem = helem;
Packit 4a16fb
	simple->ctls[type].type = snd_ctl_elem_info_get_type(&info;;
Packit 4a16fb
	simple->ctls[type].inactive = snd_ctl_elem_info_is_inactive(&info;;
Packit 4a16fb
	simple->ctls[type].values = values;
Packit 4a16fb
	if ( (type == CTL_GLOBAL_ENUM) ||
Packit 4a16fb
	     (type == CTL_PLAYBACK_ENUM) ||
Packit 4a16fb
	     (type == CTL_CAPTURE_ENUM) ) {
Packit 4a16fb
		simple->ctls[type].min = 0;
Packit 4a16fb
		simple->ctls[type].max = snd_ctl_elem_info_get_items(&info;;
Packit 4a16fb
	} else {
Packit 4a16fb
		if (ctype == SND_CTL_ELEM_TYPE_INTEGER) {
Packit 4a16fb
			simple->ctls[type].min =
Packit 4a16fb
					snd_ctl_elem_info_get_min(&info;;
Packit 4a16fb
			simple->ctls[type].max =
Packit 4a16fb
					snd_ctl_elem_info_get_max(&info;;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	switch (type) {
Packit 4a16fb
	case CTL_CAPTURE_SOURCE:
Packit 4a16fb
		simple->capture_item = value;
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
	err = snd_mixer_elem_attach(melem, helem);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		goto __error;
Packit 4a16fb
	err = simple_update(melem);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		if (new)
Packit 4a16fb
			goto __error;
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (new)
Packit 4a16fb
		err = snd_mixer_elem_add(melem, class);
Packit 4a16fb
	else
Packit 4a16fb
		err = snd_mixer_elem_info(melem);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = selem_read(melem);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (err)
Packit 4a16fb
		err = snd_mixer_elem_value(melem);
Packit 4a16fb
	return err;
Packit 4a16fb
      __error:
Packit 4a16fb
	if (new)
Packit 4a16fb
		snd_mixer_elem_free(melem);
Packit 4a16fb
	return -EINVAL;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
Packit 4a16fb
{
Packit 4a16fb
	const char *name = snd_hctl_elem_get_name(helem);
Packit 4a16fb
	size_t len;
Packit 4a16fb
	selem_ctl_type_t type = CTL_SINGLE; /* to shut up warning */
Packit 4a16fb
	if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER)
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (strcmp(name, "Capture Source") == 0) {
Packit 4a16fb
		snd_ctl_elem_info_t info = {0};
Packit 4a16fb
		unsigned int k, items;
Packit 4a16fb
		int err;
Packit 4a16fb
		err = snd_hctl_elem_info(helem, &info;;
Packit 4a16fb
		assert(err >= 0);
Packit 4a16fb
		if (snd_ctl_elem_info_get_type(&info) !=
Packit 4a16fb
						SND_CTL_ELEM_TYPE_ENUMERATED)
Packit 4a16fb
			return 0;
Packit 4a16fb
		items = snd_ctl_elem_info_get_items(&info;;
Packit 4a16fb
		for (k = 0; k < items; ++k) {
Packit 4a16fb
			const char *n;
Packit 4a16fb
			snd_ctl_elem_info_set_item(&info, k);
Packit 4a16fb
			err = snd_hctl_elem_info(helem, &info;;
Packit 4a16fb
			if (err < 0)
Packit 4a16fb
				return err;
Packit 4a16fb
			n = snd_ctl_elem_info_get_item_name(&info;;
Packit 4a16fb
			err = simple_add1(class, n, helem, CTL_CAPTURE_SOURCE,
Packit 4a16fb
					  k);
Packit 4a16fb
			if (err < 0)
Packit 4a16fb
				return err;
Packit 4a16fb
		}
Packit 4a16fb
		return 0;
Packit 4a16fb
	}
Packit 4a16fb
	len = base_len(name, &type);
Packit 4a16fb
	if (len == 0) {
Packit 4a16fb
		return simple_add1(class, name, helem, CTL_SINGLE, 0);
Packit 4a16fb
	} else {
Packit 4a16fb
		char ename[128];
Packit 4a16fb
		if (len >= sizeof(ename))
Packit 4a16fb
			len = sizeof(ename) - 1;
Packit 4a16fb
		memcpy(ename, name, len);
Packit 4a16fb
		ename[len] = 0;
Packit 4a16fb
		/* exception: Capture Volume and Capture Switch */
Packit 4a16fb
		if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture"))
Packit 4a16fb
			type = CTL_CAPTURE_VOLUME;
Packit 4a16fb
		else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture"))
Packit 4a16fb
			type = CTL_CAPTURE_SWITCH;
Packit 4a16fb
		return simple_add1(class, ename, helem, type, 0);
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int simple_event_remove(snd_hctl_elem_t *helem,
Packit 4a16fb
			       snd_mixer_elem_t *melem)
Packit 4a16fb
{
Packit 4a16fb
	selem_none_t *simple = snd_mixer_elem_get_private(melem);
Packit 4a16fb
	int err;
Packit 4a16fb
	int k;
Packit 4a16fb
	for (k = 0; k <= CTL_LAST; k++) {
Packit 4a16fb
		if (simple->ctls[k].elem == helem)
Packit 4a16fb
			break;
Packit 4a16fb
	}
Packit 4a16fb
	assert(k <= CTL_LAST);
Packit 4a16fb
	simple->ctls[k].elem = NULL;
Packit 4a16fb
	err = snd_mixer_elem_detach(melem, helem);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (snd_mixer_elem_empty(melem))
Packit 4a16fb
		return snd_mixer_elem_remove(melem);
Packit 4a16fb
	err = simple_update(melem);
Packit 4a16fb
	return snd_mixer_elem_info(melem);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int simple_event(snd_mixer_class_t *class, unsigned int mask,
Packit 4a16fb
			snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	if (mask == SND_CTL_EVENT_MASK_REMOVE)
Packit 4a16fb
		return simple_event_remove(helem, melem);
Packit 4a16fb
	if (mask & SND_CTL_EVENT_MASK_ADD) {
Packit 4a16fb
		err = simple_event_add(class, helem);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (mask & SND_CTL_EVENT_MASK_INFO) {
Packit 4a16fb
		err = simple_event_remove(helem, melem);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		err = simple_event_add(class, helem);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		return 0;
Packit 4a16fb
	}
Packit 4a16fb
	if (mask & SND_CTL_EVENT_MASK_VALUE) {
Packit 4a16fb
		err = selem_read(melem);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		if (err) {
Packit 4a16fb
			err = snd_mixer_elem_value(melem);
Packit 4a16fb
			if (err < 0)
Packit 4a16fb
				return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Register mixer simple element class - none abstraction
Packit 4a16fb
 * \param mixer Mixer handle
Packit 4a16fb
 * \param options Options container
Packit 4a16fb
 * \param classp Pointer to returned mixer simple element class handle (or NULL)
Packit 4a16fb
 * \return 0 on success otherwise a negative error code
Packit 4a16fb
 */
Packit 4a16fb
int snd_mixer_simple_none_register(snd_mixer_t *mixer,
Packit 4a16fb
				   struct snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED,
Packit 4a16fb
				   snd_mixer_class_t **classp)
Packit 4a16fb
{
Packit 4a16fb
	snd_mixer_class_t *class;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	if (snd_mixer_class_malloc(&class))
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	snd_mixer_class_set_event(class, simple_event);
Packit 4a16fb
	snd_mixer_class_set_compare(class, snd_mixer_selem_compare);
Packit 4a16fb
	err = snd_mixer_class_register(class, mixer);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		free(class);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (classp)
Packit 4a16fb
		*classp = class;
Packit 4a16fb
	return 0;
Packit 4a16fb
}