Blame src/mixer/simple_none.c

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