Home | History | Annotate | Download | only in mixer
      1 /**
      2  * \file mixer/simple_none.c
      3  * \brief Mixer Simple Element Class Interface
      4  * \author Jaroslav Kysela <perex (at) perex.cz>
      5  * \author Abramo Bagnara <abramo (at) alsa-project.org>
      6  * \date 2001-2004
      7  *
      8  * Mixer simple element class interface.
      9  */
     10 /*
     11  *  Mixer Interface - simple controls
     12  *  Copyright (c) 2000,2004 by Jaroslav Kysela <perex (at) perex.cz>
     13  *  Copyright (c) 2001 by Abramo Bagnara <abramo (at) alsa-project.org>
     14  *
     15  *
     16  *   This library is free software; you can redistribute it and/or modify
     17  *   it under the terms of the GNU Lesser General Public License as
     18  *   published by the Free Software Foundation; either version 2.1 of
     19  *   the License, or (at your option) any later version.
     20  *
     21  *   This program is distributed in the hope that it will be useful,
     22  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     23  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     24  *   GNU Lesser General Public License for more details.
     25  *
     26  *   You should have received a copy of the GNU Lesser General Public
     27  *   License along with this library; if not, write to the Free Software
     28  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     29  *
     30  */
     31 
     32 #include <stdio.h>
     33 #include <stdlib.h>
     34 #include <unistd.h>
     35 #include <string.h>
     36 #include <fcntl.h>
     37 #include <sys/ioctl.h>
     38 #include <assert.h>
     39 #include <math.h>
     40 #include <limits.h>
     41 #include <alsa/asoundlib.h>
     42 #include "mixer_simple.h"
     43 
     44 #ifndef DOC_HIDDEN
     45 
     46 #define MIXER_COMPARE_WEIGHT_SIMPLE_BASE        0
     47 #define MIXER_COMPARE_WEIGHT_NEXT_BASE          10000000
     48 #define MIXER_COMPARE_WEIGHT_NOT_FOUND          1000000000
     49 
     50 typedef enum _selem_ctl_type {
     51 	CTL_SINGLE,
     52 	CTL_GLOBAL_ENUM,
     53 	CTL_GLOBAL_SWITCH,
     54 	CTL_GLOBAL_VOLUME,
     55 	CTL_GLOBAL_ROUTE,
     56 	CTL_PLAYBACK_ENUM,
     57 	CTL_PLAYBACK_SWITCH,
     58 	CTL_PLAYBACK_VOLUME,
     59 	CTL_PLAYBACK_ROUTE,
     60 	CTL_CAPTURE_ENUM,
     61 	CTL_CAPTURE_SWITCH,
     62 	CTL_CAPTURE_VOLUME,
     63 	CTL_CAPTURE_ROUTE,
     64 	CTL_CAPTURE_SOURCE,
     65 	CTL_LAST = CTL_CAPTURE_SOURCE,
     66 } selem_ctl_type_t;
     67 
     68 typedef struct _selem_ctl {
     69 	snd_hctl_elem_t *elem;
     70 	snd_ctl_elem_type_t type;
     71 	unsigned int inactive: 1;
     72 	unsigned int values;
     73 	long min, max;
     74 } selem_ctl_t;
     75 
     76 typedef struct _selem_none {
     77 	sm_selem_t selem;
     78 	selem_ctl_t ctls[CTL_LAST + 1];
     79 	unsigned int capture_item;
     80 	struct selem_str {
     81 		unsigned int range: 1;	/* Forced range */
     82 		unsigned int db_initialized: 1;
     83 		unsigned int db_init_error: 1;
     84 		long min, max;
     85 		unsigned int channels;
     86 		long vol[32];
     87 		unsigned int sw;
     88 		unsigned int *db_info;
     89 	} str[2];
     90 } selem_none_t;
     91 
     92 static const struct mixer_name_table {
     93 	const char *longname;
     94 	const char *shortname;
     95 } name_table[] = {
     96 	{"Tone Control - Switch", "Tone"},
     97 	{"Tone Control - Bass", "Bass"},
     98 	{"Tone Control - Treble", "Treble"},
     99 	{"Synth Tone Control - Switch", "Synth Tone"},
    100 	{"Synth Tone Control - Bass", "Synth Bass"},
    101 	{"Synth Tone Control - Treble", "Synth Treble"},
    102 	{0, 0},
    103 };
    104 
    105 #endif /* !DOC_HIDDEN */
    106 
    107 static const char *get_short_name(const char *lname)
    108 {
    109 	const struct mixer_name_table *p;
    110 	for (p = name_table; p->longname; p++) {
    111 		if (!strcmp(lname, p->longname))
    112 			return p->shortname;
    113 	}
    114 	return lname;
    115 }
    116 
    117 static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
    118 {
    119 	int res;
    120 
    121 	for (res = 0; *names; names++, res += coef) {
    122 		if (!strncmp(*name, *names, strlen(*names))) {
    123 			*name += strlen(*names);
    124 			if (**name == ' ')
    125 				(*name)++;
    126 			return res+1;
    127 		}
    128 	}
    129 	return MIXER_COMPARE_WEIGHT_NOT_FOUND;
    130 }
    131 
    132 static int get_compare_weight(const char *name, unsigned int idx)
    133 {
    134 	static const char *const names[] = {
    135 		"Master",
    136 		"Headphone",
    137 		"Tone",
    138 		"Bass",
    139 		"Treble",
    140 		"3D Control",
    141 		"PCM",
    142 		"Front",
    143 		"Surround",
    144 		"Center",
    145 		"LFE",
    146 		"Side",
    147 		"Synth",
    148 		"FM",
    149 		"Wave",
    150 		"Music",
    151 		"DSP",
    152 		"Line",
    153 		"CD",
    154 		"Mic",
    155 		"Video",
    156 		"Zoom Video",
    157 		"Phone",
    158 		"I2S",
    159 		"IEC958",
    160 		"PC Speaker",
    161 		"Aux",
    162 		"Mono",
    163 		"Playback",
    164 		"Capture",
    165 		"Mix",
    166 		NULL
    167 	};
    168 	static const char *const names1[] = {
    169 		"-",
    170 		NULL,
    171 	};
    172 	static const char *const names2[] = {
    173 		"Mono",
    174 		"Digital",
    175 		"Switch",
    176 		"Depth",
    177 		"Wide",
    178 		"Space",
    179 		"Level",
    180 		"Center",
    181 		"Output",
    182 		"Boost",
    183 		"Tone",
    184 		"Bass",
    185 		"Treble",
    186 		NULL,
    187 	};
    188 	const char *name1;
    189 	int res, res1;
    190 
    191 	if ((res = compare_mixer_priority_lookup((const char **)&name, names, 1000)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
    192 		return MIXER_COMPARE_WEIGHT_NOT_FOUND;
    193 	if (*name == '\0')
    194 		goto __res;
    195 	for (name1 = name; *name1 != '\0'; name1++);
    196 	for (name1--; name1 != name && *name1 != ' '; name1--);
    197 	while (name1 != name && *name1 == ' ')
    198 		name1--;
    199 	if (name1 != name) {
    200 		for (; name1 != name && *name1 != ' '; name1--);
    201 		name = name1;
    202 		if ((res1 = compare_mixer_priority_lookup((const char **)&name, names1, 200)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
    203 			return res;
    204 		res += res1;
    205 	} else {
    206 		name = name1;
    207 	}
    208 	if ((res1 = compare_mixer_priority_lookup((const char **)&name, names2, 20)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
    209 		return res;
    210       __res:
    211 	return MIXER_COMPARE_WEIGHT_SIMPLE_BASE + res + idx;
    212 }
    213 
    214 static long to_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
    215 {
    216 	int64_t n;
    217 	if (c->max == c->min)
    218 		return s->str[dir].min;
    219 	n = (int64_t) (value - c->min) * (s->str[dir].max - s->str[dir].min);
    220 	return s->str[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min);
    221 }
    222 
    223 static long from_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
    224 {
    225 	int64_t n;
    226 	if (s->str[dir].max == s->str[dir].min)
    227 		return c->min;
    228 	n = (int64_t) (value - s->str[dir].min) * (c->max - c->min);
    229 	return c->min + (n + (s->str[dir].max - s->str[dir].min) / 2) / (s->str[dir].max - s->str[dir].min);
    230 }
    231 
    232 static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
    233 {
    234 	snd_ctl_elem_value_t *ctl;
    235 	unsigned int idx;
    236 	int err;
    237 	selem_ctl_t *c = &s->ctls[type];
    238 	snd_ctl_elem_value_alloca(&ctl);
    239 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    240 		return err;
    241 	for (idx = 0; idx < s->str[dir].channels; idx++) {
    242 		unsigned int idx1 = idx;
    243 		if (idx >= c->values)
    244 			idx1 = 0;
    245 		s->str[dir].vol[idx] = to_user(s, dir, c, snd_ctl_elem_value_get_integer(ctl, idx1));
    246 	}
    247 	return 0;
    248 }
    249 
    250 static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
    251 {
    252 	snd_ctl_elem_value_t *ctl;
    253 	unsigned int idx;
    254 	int err;
    255 	selem_ctl_t *c = &s->ctls[type];
    256 	snd_ctl_elem_value_alloca(&ctl);
    257 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    258 		return err;
    259 	for (idx = 0; idx < s->str[dir].channels; idx++) {
    260 		unsigned int idx1 = idx;
    261 		if (idx >= c->values)
    262 			idx1 = 0;
    263 		if (!snd_ctl_elem_value_get_integer(ctl, idx1))
    264 			s->str[dir].sw &= ~(1 << idx);
    265 	}
    266 	return 0;
    267 }
    268 
    269 static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type)
    270 {
    271 	snd_ctl_elem_value_t *ctl;
    272 	unsigned int idx;
    273 	int err;
    274 	selem_ctl_t *c = &s->ctls[type];
    275 	snd_ctl_elem_value_alloca(&ctl);
    276 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    277 		return err;
    278 	for (idx = 0; idx < s->str[dir].channels; idx++) {
    279 		unsigned int idx1 = idx;
    280 		if (idx >= c->values)
    281 			idx1 = 0;
    282 		if (!snd_ctl_elem_value_get_integer(ctl, idx1 * c->values + idx1))
    283 			s->str[dir].sw &= ~(1 << idx);
    284 	}
    285 	return 0;
    286 }
    287 
    288 static int elem_read_enum(selem_none_t *s)
    289 {
    290 	snd_ctl_elem_value_t *ctl;
    291 	unsigned int idx;
    292 	int err;
    293 	int type;
    294 	selem_ctl_t *c;
    295 	type = CTL_GLOBAL_ENUM;
    296 	if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) )
    297 		type = CTL_GLOBAL_ENUM;
    298 	else if (s->selem.caps & SM_CAP_PENUM)
    299 		type = CTL_PLAYBACK_ENUM;
    300 	else if (s->selem.caps & SM_CAP_CENUM)
    301 		type = CTL_CAPTURE_ENUM;
    302 	c = &s->ctls[type];
    303 	snd_ctl_elem_value_alloca(&ctl);
    304 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    305 		return err;
    306 	for (idx = 0; idx < s->str[0].channels; idx++) {
    307 		unsigned int idx1 = idx;
    308 		if (idx >= c->values)
    309 			idx1 = 0;
    310 		s->str[0].vol[idx] = snd_ctl_elem_value_get_enumerated(ctl, idx1);
    311 	}
    312 	return 0;
    313 }
    314 
    315 static int selem_read(snd_mixer_elem_t *elem)
    316 {
    317 	selem_none_t *s;
    318 	unsigned int idx;
    319 	int err = 0;
    320 	long pvol[32], cvol[32];
    321 	unsigned int psw, csw;
    322 
    323 	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
    324 	s = snd_mixer_elem_get_private(elem);
    325 
    326 	memcpy(pvol, s->str[SM_PLAY].vol, sizeof(pvol));
    327 	memset(&s->str[SM_PLAY].vol, 0, sizeof(s->str[SM_PLAY].vol));
    328 	psw = s->str[SM_PLAY].sw;
    329 	s->str[SM_PLAY].sw = ~0U;
    330 	memcpy(cvol, s->str[SM_CAPT].vol, sizeof(cvol));
    331 	memset(&s->str[SM_CAPT].vol, 0, sizeof(s->str[SM_CAPT].vol));
    332 	csw = s->str[SM_CAPT].sw;
    333 	s->str[SM_CAPT].sw = ~0U;
    334 
    335 	if (s->ctls[CTL_GLOBAL_ENUM].elem) {
    336 		err = elem_read_enum(s);
    337 		if (err < 0)
    338 			return err;
    339 		goto __skip_cswitch;
    340 	}
    341 
    342 	if (s->ctls[CTL_CAPTURE_ENUM].elem) {
    343 		err = elem_read_enum(s);
    344 		if (err < 0)
    345 			return err;
    346 		goto __skip_cswitch;
    347 	}
    348 
    349 	if (s->ctls[CTL_PLAYBACK_ENUM].elem) {
    350 		err = elem_read_enum(s);
    351 		if (err < 0)
    352 			return err;
    353 		goto __skip_cswitch;
    354 	}
    355 
    356 
    357 	if (s->ctls[CTL_PLAYBACK_VOLUME].elem)
    358 		err = elem_read_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
    359 	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
    360 		err = elem_read_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
    361 	else if (s->ctls[CTL_SINGLE].elem &&
    362 		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
    363 		err = elem_read_volume(s, SM_PLAY, CTL_SINGLE);
    364 	if (err < 0)
    365 		return err;
    366 
    367 	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) {
    368 		s->str[SM_PLAY].sw = 0;
    369 		goto __skip_pswitch;
    370 	}
    371 	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
    372 		err = elem_read_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
    373 		if (err < 0)
    374 			return err;
    375 	}
    376 	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
    377 		err = elem_read_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
    378 		if (err < 0)
    379 			return err;
    380 	}
    381 	if (s->ctls[CTL_SINGLE].elem &&
    382 	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
    383 		err = elem_read_switch(s, SM_PLAY, CTL_SINGLE);
    384 		if (err < 0)
    385 			return err;
    386 	}
    387 	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
    388 		err = elem_read_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
    389 		if (err < 0)
    390 			return err;
    391 	}
    392 	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
    393 		err = elem_read_route(s, SM_PLAY, CTL_GLOBAL_ROUTE);
    394 		if (err < 0)
    395 			return err;
    396 	}
    397       __skip_pswitch:
    398 
    399 	if (s->ctls[CTL_CAPTURE_VOLUME].elem)
    400 		err = elem_read_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
    401 	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
    402 		err = elem_read_volume(s, SM_CAPT, CTL_GLOBAL_VOLUME);
    403 	else if (s->ctls[CTL_SINGLE].elem &&
    404 		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
    405 		err = elem_read_volume(s, SM_CAPT, CTL_SINGLE);
    406 	if (err < 0)
    407 		return err;
    408 
    409 	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) {
    410 		s->str[SM_CAPT].sw = 0;
    411 		goto __skip_cswitch;
    412 	}
    413 	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
    414 		err = elem_read_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
    415 		if (err < 0)
    416 			return err;
    417 	}
    418 	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
    419 		err = elem_read_switch(s, SM_CAPT, CTL_GLOBAL_SWITCH);
    420 		if (err < 0)
    421 			return err;
    422 	}
    423 	if (s->ctls[CTL_SINGLE].elem &&
    424 	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
    425 		err = elem_read_switch(s, SM_CAPT, CTL_SINGLE);
    426 		if (err < 0)
    427 			return err;
    428 	}
    429 	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
    430 		err = elem_read_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
    431 		if (err < 0)
    432 			return err;
    433 	}
    434 	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
    435 		err = elem_read_route(s, SM_CAPT, CTL_GLOBAL_ROUTE);
    436 		if (err < 0)
    437 			return err;
    438 	}
    439 	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
    440 		snd_ctl_elem_value_t *ctl;
    441 		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
    442 		snd_ctl_elem_value_alloca(&ctl);
    443 		err = snd_hctl_elem_read(c->elem, ctl);
    444 		if (err < 0)
    445 			return err;
    446 		for (idx = 0; idx < s->str[SM_CAPT].channels; idx++) {
    447 			unsigned int idx1 = idx;
    448 			if (idx >= c->values)
    449 				idx1 = 0;
    450 			if (snd_ctl_elem_value_get_enumerated(ctl, idx1) != s->capture_item)
    451 				s->str[SM_CAPT].sw &= ~(1 << idx);
    452 		}
    453 	}
    454       __skip_cswitch:
    455 
    456 	if (memcmp(pvol, s->str[SM_PLAY].vol, sizeof(pvol)) ||
    457 	    psw != s->str[SM_PLAY].sw ||
    458 	    memcmp(cvol, s->str[SM_CAPT].vol, sizeof(cvol)) ||
    459 	    csw != s->str[SM_CAPT].sw)
    460 		return 1;
    461 	return 0;
    462 }
    463 
    464 static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
    465 {
    466 	snd_ctl_elem_value_t *ctl;
    467 	unsigned int idx;
    468 	int err;
    469 	selem_ctl_t *c = &s->ctls[type];
    470 	snd_ctl_elem_value_alloca(&ctl);
    471 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    472 		return err;
    473 	for (idx = 0; idx < c->values; idx++)
    474 		snd_ctl_elem_value_set_integer(ctl, idx, from_user(s, dir, c, s->str[dir].vol[idx]));
    475 	if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
    476 		return err;
    477 	return 0;
    478 }
    479 
    480 static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
    481 {
    482 	snd_ctl_elem_value_t *ctl;
    483 	unsigned int idx;
    484 	int err;
    485 	selem_ctl_t *c = &s->ctls[type];
    486 	snd_ctl_elem_value_alloca(&ctl);
    487 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    488 		return err;
    489 	for (idx = 0; idx < c->values; idx++)
    490 		snd_ctl_elem_value_set_integer(ctl, idx, !!(s->str[dir].sw & (1 << idx)));
    491 	if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
    492 		return err;
    493 	return 0;
    494 }
    495 
    496 static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val)
    497 {
    498 	snd_ctl_elem_value_t *ctl;
    499 	unsigned int idx;
    500 	int err;
    501 	selem_ctl_t *c = &s->ctls[type];
    502 	snd_ctl_elem_value_alloca(&ctl);
    503 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    504 		return err;
    505 	for (idx = 0; idx < c->values; idx++)
    506 		snd_ctl_elem_value_set_integer(ctl, idx, !!val);
    507 	if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
    508 		return err;
    509 	return 0;
    510 }
    511 
    512 static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type)
    513 {
    514 	snd_ctl_elem_value_t *ctl;
    515 	unsigned int idx;
    516 	int err;
    517 	selem_ctl_t *c = &s->ctls[type];
    518 	snd_ctl_elem_value_alloca(&ctl);
    519 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    520 		return err;
    521 	for (idx = 0; idx < c->values * c->values; idx++)
    522 		snd_ctl_elem_value_set_integer(ctl, idx, 0);
    523 	for (idx = 0; idx < c->values; idx++)
    524 		snd_ctl_elem_value_set_integer(ctl, idx * c->values + idx, !!(s->str[dir].sw & (1 << idx)));
    525 	if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
    526 		return err;
    527 	return 0;
    528 }
    529 
    530 static int elem_write_enum(selem_none_t *s)
    531 {
    532 	snd_ctl_elem_value_t *ctl;
    533 	unsigned int idx;
    534 	int err;
    535 	int type;
    536 	selem_ctl_t *c;
    537 	type = CTL_GLOBAL_ENUM;
    538 	if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) ) == (SM_CAP_CENUM | SM_CAP_PENUM) )
    539 		type = CTL_GLOBAL_ENUM;
    540 	else if (s->selem.caps & SM_CAP_PENUM)
    541 		type = CTL_PLAYBACK_ENUM;
    542 	else if (s->selem.caps & SM_CAP_CENUM)
    543 		type = CTL_CAPTURE_ENUM;
    544 	c = &s->ctls[type];
    545 	snd_ctl_elem_value_alloca(&ctl);
    546 	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    547 		return err;
    548 	for (idx = 0; idx < c->values; idx++)
    549 		snd_ctl_elem_value_set_enumerated(ctl, idx, (unsigned int)s->str[0].vol[idx]);
    550 	if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
    551 		return err;
    552 	return 0;
    553 }
    554 
    555 static int selem_write_main(snd_mixer_elem_t *elem)
    556 {
    557 	selem_none_t *s;
    558 	unsigned int idx;
    559 	int err;
    560 
    561 	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
    562 	s = snd_mixer_elem_get_private(elem);
    563 
    564 	if (s->ctls[CTL_GLOBAL_ENUM].elem)
    565 		return elem_write_enum(s);
    566 
    567 	if (s->ctls[CTL_PLAYBACK_ENUM].elem)
    568 		return elem_write_enum(s);
    569 
    570 	if (s->ctls[CTL_CAPTURE_ENUM].elem)
    571 		return elem_write_enum(s);
    572 
    573 	if (s->ctls[CTL_SINGLE].elem) {
    574 		if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
    575 			err = elem_write_volume(s, SM_PLAY, CTL_SINGLE);
    576 		else
    577 			err = elem_write_switch(s, SM_PLAY, CTL_SINGLE);
    578 		if (err < 0)
    579 			return err;
    580 	}
    581 	if (s->ctls[CTL_GLOBAL_VOLUME].elem) {
    582 		err = elem_write_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
    583 		if (err < 0)
    584 			return err;
    585 	}
    586 	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
    587 		if (s->ctls[CTL_PLAYBACK_SWITCH].elem && s->ctls[CTL_CAPTURE_SWITCH].elem)
    588 			err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH, 1);
    589 		else
    590 			err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
    591 		if (err < 0)
    592 			return err;
    593 	}
    594 	if (s->ctls[CTL_PLAYBACK_VOLUME].elem) {
    595 		err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
    596 		if (err < 0)
    597 			return err;
    598 	}
    599 	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
    600 		err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
    601 		if (err < 0)
    602 			return err;
    603 	}
    604 	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
    605 		err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
    606 		if (err < 0)
    607 			return err;
    608 	}
    609 	if (s->ctls[CTL_CAPTURE_VOLUME].elem) {
    610 		err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
    611 		if (err < 0)
    612 			return err;
    613 	}
    614 	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
    615 		err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
    616 		if (err < 0)
    617 			return err;
    618 	}
    619 	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
    620 		err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
    621 		if (err < 0)
    622 			return err;
    623 	}
    624 	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
    625 		snd_ctl_elem_value_t *ctl;
    626 		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
    627 		snd_ctl_elem_value_alloca(&ctl);
    628 		if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
    629 			return err;
    630 		for (idx = 0; idx < c->values; idx++) {
    631 			if (s->str[SM_CAPT].sw & (1 << idx))
    632 				snd_ctl_elem_value_set_enumerated(ctl, idx, s->capture_item);
    633 		}
    634 		if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
    635 			return err;
    636 		/* update the element, don't remove */
    637 		err = selem_read(elem);
    638 		if (err < 0)
    639 			return err;
    640 	}
    641 	return 0;
    642 }
    643 
    644 static int selem_write(snd_mixer_elem_t *elem)
    645 {
    646 	int err;
    647 
    648 	err = selem_write_main(elem);
    649 	if (err < 0)
    650 		selem_read(elem);
    651 	return err;
    652 }
    653 
    654 static void selem_free(snd_mixer_elem_t *elem)
    655 {
    656 	selem_none_t *simple = snd_mixer_elem_get_private(elem);
    657 	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
    658 	if (simple->selem.id)
    659 		snd_mixer_selem_id_free(simple->selem.id);
    660 	/* free db range information */
    661 	free(simple->str[0].db_info);
    662 	free(simple->str[1].db_info);
    663 	free(simple);
    664 }
    665 
    666 static int simple_update(snd_mixer_elem_t *melem)
    667 {
    668 	selem_none_t *simple;
    669 	unsigned int caps, pchannels, cchannels;
    670 	long pmin, pmax, cmin, cmax;
    671 	selem_ctl_t *ctl;
    672 	const char *name;
    673 
    674 	caps = 0;
    675 	pchannels = 0;
    676 	pmin = LONG_MAX;
    677 	pmax = LONG_MIN;
    678 	cchannels = 0;
    679 	cmin = LONG_MAX;
    680 	cmax = LONG_MIN;
    681 	assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE);
    682 	simple = snd_mixer_elem_get_private(melem);
    683 	name = snd_mixer_selem_get_name(melem);
    684 	ctl = &simple->ctls[CTL_SINGLE];
    685 	if (ctl->elem) {
    686 		pchannels = cchannels = ctl->values;
    687 		if (ctl->type == SND_CTL_ELEM_TYPE_INTEGER) {
    688 			caps |= SM_CAP_GVOLUME;
    689 			pmin = cmin = ctl->min;
    690 			pmax = cmax = ctl->max;
    691 		} else
    692 			caps |= SM_CAP_GSWITCH;
    693 	}
    694 	ctl = &simple->ctls[CTL_GLOBAL_SWITCH];
    695 	if (ctl->elem) {
    696 		if (pchannels < ctl->values)
    697 			pchannels = ctl->values;
    698 		if (cchannels < ctl->values)
    699 			cchannels = ctl->values;
    700 		caps |= SM_CAP_GSWITCH;
    701 	}
    702 	ctl = &simple->ctls[CTL_GLOBAL_ROUTE];
    703 	if (ctl->elem) {
    704 		if (pchannels < ctl->values)
    705 			pchannels = ctl->values;
    706 		if (cchannels < ctl->values)
    707 			cchannels = ctl->values;
    708 		caps |= SM_CAP_GSWITCH;
    709 	}
    710 	ctl = &simple->ctls[CTL_GLOBAL_VOLUME];
    711 	if (ctl->elem) {
    712 		if (pchannels < ctl->values)
    713 			pchannels = ctl->values;
    714 		if (pmin > ctl->min)
    715 			pmin = ctl->min;
    716 		if (pmax < ctl->max)
    717 			pmax = ctl->max;
    718 		if (cchannels < ctl->values)
    719 			cchannels = ctl->values;
    720 		if (cmin > ctl->min)
    721 			cmin = ctl->min;
    722 		if (cmax < ctl->max)
    723 			cmax = ctl->max;
    724 		caps |= SM_CAP_GVOLUME;
    725 	}
    726 	ctl = &simple->ctls[CTL_PLAYBACK_SWITCH];
    727 	if (ctl->elem) {
    728 		if (pchannels < ctl->values)
    729 			pchannels = ctl->values;
    730 		caps |= SM_CAP_PSWITCH;
    731 		caps &= ~SM_CAP_GSWITCH;
    732 	}
    733 	ctl = &simple->ctls[CTL_PLAYBACK_ROUTE];
    734 	if (ctl->elem) {
    735 		if (pchannels < ctl->values)
    736 			pchannels = ctl->values;
    737 		caps |= SM_CAP_PSWITCH;
    738 		caps &= ~SM_CAP_GSWITCH;
    739 	}
    740 	ctl = &simple->ctls[CTL_CAPTURE_SWITCH];
    741 	if (ctl->elem) {
    742 		if (cchannels < ctl->values)
    743 			cchannels = ctl->values;
    744 		caps |= SM_CAP_CSWITCH;
    745 		caps &= ~SM_CAP_GSWITCH;
    746 	}
    747 	ctl = &simple->ctls[CTL_CAPTURE_ROUTE];
    748 	if (ctl->elem) {
    749 		if (cchannels < ctl->values)
    750 			cchannels = ctl->values;
    751 		caps |= SM_CAP_CSWITCH;
    752 		caps &= ~SM_CAP_GSWITCH;
    753 	}
    754 	ctl = &simple->ctls[CTL_PLAYBACK_VOLUME];
    755 	if (ctl->elem) {
    756 		if (pchannels < ctl->values)
    757 			pchannels = ctl->values;
    758 		if (pmin > ctl->min)
    759 			pmin = ctl->min;
    760 		if (pmax < ctl->max)
    761 			pmax = ctl->max;
    762 		caps |= SM_CAP_PVOLUME;
    763 		caps &= ~SM_CAP_GVOLUME;
    764 	}
    765 	ctl = &simple->ctls[CTL_CAPTURE_VOLUME];
    766 	if (ctl->elem) {
    767 		if (cchannels < ctl->values)
    768 			cchannels = ctl->values;
    769 		if (cmin > ctl->min)
    770 			cmin = ctl->min;
    771 		if (cmax < ctl->max)
    772 			cmax = ctl->max;
    773 		caps |= SM_CAP_CVOLUME;
    774 		caps &= ~SM_CAP_GVOLUME;
    775 	}
    776 	ctl = &simple->ctls[CTL_CAPTURE_SOURCE];
    777 	if (ctl->elem) {
    778 		if (cchannels < ctl->values)
    779 			cchannels = ctl->values;
    780 		caps |= SM_CAP_CSWITCH | SM_CAP_CSWITCH_EXCL;
    781 		caps &= ~SM_CAP_GSWITCH;
    782 	}
    783 	ctl = &simple->ctls[CTL_GLOBAL_ENUM];
    784 	if (ctl->elem) {
    785 		if (pchannels < ctl->values)
    786 			pchannels = ctl->values;
    787 		caps |= SM_CAP_PENUM | SM_CAP_CENUM;
    788 	}
    789 	ctl = &simple->ctls[CTL_PLAYBACK_ENUM];
    790 	if (ctl->elem) {
    791 		if (pchannels < ctl->values)
    792 			pchannels = ctl->values;
    793 		caps |= SM_CAP_PENUM;
    794 	}
    795 	ctl = &simple->ctls[CTL_CAPTURE_ENUM];
    796 	if (ctl->elem) {
    797 		if (pchannels < ctl->values)
    798 			pchannels = ctl->values;
    799 		caps |= SM_CAP_CENUM;
    800 	}
    801 	if (pchannels > 32)
    802 		pchannels = 32;
    803 	if (cchannels > 32)
    804 		cchannels = 32;
    805 	if (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))
    806 		caps |= SM_CAP_PSWITCH_JOIN;
    807 	if (caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME))
    808 		caps |= SM_CAP_PVOLUME_JOIN;
    809 	if (caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))
    810 		caps |= SM_CAP_CSWITCH_JOIN;
    811 	if (caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME))
    812 		caps |= SM_CAP_CVOLUME_JOIN;
    813 	if (pchannels > 1 || cchannels > 1) {
    814 		if (simple->ctls[CTL_SINGLE].elem &&
    815 		    simple->ctls[CTL_SINGLE].values > 1) {
    816 			if (caps & SM_CAP_GSWITCH)
    817 				caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
    818 			else
    819 				caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
    820 		}
    821 		if (simple->ctls[CTL_GLOBAL_ROUTE].elem ||
    822 		    (simple->ctls[CTL_GLOBAL_SWITCH].elem &&
    823 		     simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) {
    824 			caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
    825 		}
    826 		if (simple->ctls[CTL_GLOBAL_VOLUME].elem &&
    827 		    simple->ctls[CTL_GLOBAL_VOLUME].values > 1) {
    828 			caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
    829 		}
    830 	}
    831 	if (pchannels > 1) {
    832 		if (simple->ctls[CTL_PLAYBACK_ROUTE].elem ||
    833 		    (simple->ctls[CTL_PLAYBACK_SWITCH].elem &&
    834 		     simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) {
    835 			caps &= ~SM_CAP_PSWITCH_JOIN;
    836 		}
    837 		if (simple->ctls[CTL_PLAYBACK_VOLUME].elem &&
    838 		    simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) {
    839 			caps &= ~SM_CAP_PVOLUME_JOIN;
    840 		}
    841 	}
    842 	if (cchannels > 1) {
    843 		if (simple->ctls[CTL_CAPTURE_ROUTE].elem ||
    844 		    (simple->ctls[CTL_CAPTURE_SWITCH].elem &&
    845 		     simple->ctls[CTL_CAPTURE_SWITCH].values > 1) ||
    846 		    (simple->ctls[CTL_CAPTURE_SOURCE].elem &&
    847 		     simple->ctls[CTL_CAPTURE_SOURCE].values > 1)) {
    848 			caps &= ~SM_CAP_CSWITCH_JOIN;
    849 		}
    850 		if (simple->ctls[CTL_CAPTURE_VOLUME].elem &&
    851 		    simple->ctls[CTL_CAPTURE_VOLUME].values > 1) {
    852 			caps &= ~SM_CAP_CVOLUME_JOIN;
    853 		}
    854 	}
    855 
    856 	/* exceptions */
    857 	if ((caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) &&
    858 	    (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == (caps & SM_CAP_GSWITCH)) {
    859 		caps &= ~(SM_CAP_GSWITCH|SM_CAP_CSWITCH_JOIN|SM_CAP_CSWITCH_EXCL);
    860 		caps |= SM_CAP_PSWITCH;
    861 	}
    862 
    863 	if ((caps & SM_CAP_GSWITCH) &&
    864 	    (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
    865 		caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;
    866 
    867 	if ((caps & SM_CAP_GVOLUME) &&
    868 	    (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
    869 		caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;
    870 
    871 	simple->selem.caps = caps;
    872 	simple->str[SM_PLAY].channels = pchannels;
    873 	if (!simple->str[SM_PLAY].range) {
    874 		simple->str[SM_PLAY].min = pmin != LONG_MAX ? pmin : 0;
    875 		simple->str[SM_PLAY].max = pmax != LONG_MIN ? pmax : 0;
    876 	}
    877 	simple->str[SM_CAPT].channels = cchannels;
    878 	if (!simple->str[SM_CAPT].range) {
    879 		simple->str[SM_CAPT].min = cmin != LONG_MAX ? cmin : 0;
    880 		simple->str[SM_CAPT].max = cmax != LONG_MIN ? cmax : 0;
    881 	}
    882 	return 0;
    883 }
    884 
    885 #ifndef DOC_HIDDEN
    886 static const struct suf {
    887 	const char *suffix;
    888 	selem_ctl_type_t type;
    889 } suffixes[] = {
    890 	{" Playback Enum", CTL_PLAYBACK_ENUM},
    891 	{" Playback Switch", CTL_PLAYBACK_SWITCH},
    892 	{" Playback Route", CTL_PLAYBACK_ROUTE},
    893 	{" Playback Volume", CTL_PLAYBACK_VOLUME},
    894 	{" Capture Enum", CTL_CAPTURE_ENUM},
    895 	{" Capture Switch", CTL_CAPTURE_SWITCH},
    896 	{" Capture Route", CTL_CAPTURE_ROUTE},
    897 	{" Capture Volume", CTL_CAPTURE_VOLUME},
    898 	{" Enum", CTL_GLOBAL_ENUM},
    899 	{" Switch", CTL_GLOBAL_SWITCH},
    900 	{" Route", CTL_GLOBAL_ROUTE},
    901 	{" Volume", CTL_GLOBAL_VOLUME},
    902 	{NULL, 0}
    903 };
    904 #endif
    905 
    906 /* Return base length or 0 on failure */
    907 static int base_len(const char *name, selem_ctl_type_t *type)
    908 {
    909 	const struct suf *p;
    910 	size_t nlen = strlen(name);
    911 	p = suffixes;
    912 	while (p->suffix) {
    913 		size_t slen = strlen(p->suffix);
    914 		size_t l;
    915 		if (nlen > slen) {
    916 			l = nlen - slen;
    917 			if (strncmp(name + l, p->suffix, slen) == 0 &&
    918 			    (l < 1 || name[l-1] != '-')) {	/* 3D Control - Switch */
    919 				*type = p->type;
    920 				return l;
    921 			}
    922 		}
    923 		p++;
    924 	}
    925 
    926 	/* Special case - handle "Input Source" as a capture route.
    927 	 * Note that it's *NO* capture source.  A capture source is split over
    928 	 * sub-elements, and multiple capture-sources will result in an error.
    929 	 * That's why some drivers use "Input Source" as a workaround.
    930 	 * Hence, this is a workaround for a workaround to get the things
    931 	 * straight back again.  Sigh.
    932 	 */
    933 	if (!strcmp(name, "Input Source")) {
    934 		*type = CTL_CAPTURE_ROUTE;
    935 		return strlen(name);
    936 	}
    937 
    938 	return 0;
    939 }
    940 
    941 
    942 /*
    943  * Simple Mixer Operations
    944  */
    945 
    946 static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value)
    947 {
    948 	selem_none_t *s = snd_mixer_elem_get_private(elem);
    949 	if (s->selem.caps & SM_CAP_GVOLUME)
    950 		dir = SM_PLAY;
    951 	if ((unsigned int) channel >= s->str[dir].channels)
    952 		return 0;
    953 	if (value < s->str[dir].min || value > s->str[dir].max)
    954 		return 0;
    955 	if (s->selem.caps &
    956 	    (dir == SM_PLAY ? SM_CAP_PVOLUME_JOIN : SM_CAP_CVOLUME_JOIN))
    957 		channel = 0;
    958 	if (value != s->str[dir].vol[channel]) {
    959 		s->str[dir].vol[channel] = value;
    960 		return 1;
    961 	}
    962 	return 0;
    963 }
    964 
    965 static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value)
    966 {
    967 	selem_none_t *s = snd_mixer_elem_get_private(elem);
    968 	if ((unsigned int) channel >= s->str[dir].channels)
    969 		return 0;
    970 	if (s->selem.caps &
    971 	    (dir == SM_PLAY ? SM_CAP_PSWITCH_JOIN : SM_CAP_CSWITCH_JOIN))
    972 		channel = 0;
    973 	if (value) {
    974 		if (!(s->str[dir].sw & (1 << channel))) {
    975 			s->str[dir].sw |= 1 << channel;
    976 			return 1;
    977 		}
    978 	} else {
    979 		if (s->str[dir].sw & (1 << channel)) {
    980 			s->str[dir].sw &= ~(1 << channel);
    981 			return 1;
    982 		}
    983 	}
    984 	return 0;
    985 }
    986 
    987 static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
    988 {
    989 	selem_none_t *s = snd_mixer_elem_get_private(elem);
    990 
    991 	switch (cmd) {
    992 
    993 	case SM_OPS_IS_ACTIVE: {
    994 		selem_ctl_type_t ctl;
    995 		for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++)
    996 			if (s->ctls[ctl].elem != NULL && s->ctls[ctl].inactive)
    997 				return 0;
    998 		return 1;
    999 	}
   1000 
   1001 	case SM_OPS_IS_MONO:
   1002 		return s->str[dir].channels == 1;
   1003 
   1004 	case SM_OPS_IS_CHANNEL:
   1005 		return (unsigned int) val < s->str[dir].channels;
   1006 
   1007 	case SM_OPS_IS_ENUMERATED:
   1008 		if (val == 1) {
   1009 			if (dir == SM_PLAY && (s->selem.caps & SM_CAP_PENUM) && !(s->selem.caps & SM_CAP_CENUM) )
   1010 				return 1;
   1011 			if (dir == SM_CAPT && (s->selem.caps & SM_CAP_CENUM) && !(s->selem.caps & SM_CAP_PENUM) )
   1012 				return 1;
   1013 			return 0;
   1014 		}
   1015 		if (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) )
   1016 			return 1;
   1017 		return 0;
   1018 
   1019 	case SM_OPS_IS_ENUMCNT:
   1020 		/* Both */
   1021 		if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) ) {
   1022 			if (! s->ctls[CTL_GLOBAL_ENUM].elem)
   1023 				return -EINVAL;
   1024 			return s->ctls[CTL_GLOBAL_ENUM].max;
   1025 		/* Only Playback */
   1026 		} else if (s->selem.caps & SM_CAP_PENUM ) {
   1027 			if (! s->ctls[CTL_PLAYBACK_ENUM].elem)
   1028 				return -EINVAL;
   1029 			return s->ctls[CTL_PLAYBACK_ENUM].max;
   1030 		/* Only Capture */
   1031 		} else if (s->selem.caps & SM_CAP_CENUM ) {
   1032 			if (! s->ctls[CTL_CAPTURE_ENUM].elem)
   1033 				return -EINVAL;
   1034 			return s->ctls[CTL_CAPTURE_ENUM].max;
   1035 		}
   1036 
   1037 	}
   1038 
   1039 	return 1;
   1040 }
   1041 
   1042 static int get_range_ops(snd_mixer_elem_t *elem, int dir,
   1043 			 long *min, long *max)
   1044 {
   1045 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1046 	*min = s->str[dir].min;
   1047 	*max = s->str[dir].max;
   1048 	return 0;
   1049 }
   1050 
   1051 static int set_range_ops(snd_mixer_elem_t *elem, int dir,
   1052 			 long min, long max)
   1053 {
   1054 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1055 	int err;
   1056 
   1057 	s->str[dir].range = 1;
   1058 	s->str[dir].min = min;
   1059 	s->str[dir].max = max;
   1060 	if ((err = selem_read(elem)) < 0)
   1061 		return err;
   1062 	return 0;
   1063 }
   1064 
   1065 static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
   1066 			  snd_mixer_selem_channel_id_t channel, long *value)
   1067 {
   1068 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1069 	if (s->selem.caps & SM_CAP_GVOLUME)
   1070 		dir = SM_PLAY;
   1071 	if ((unsigned int) channel >= s->str[dir].channels)
   1072 		return -EINVAL;
   1073 	*value = s->str[dir].vol[channel];
   1074 	return 0;
   1075 }
   1076 
   1077 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec);
   1078 
   1079 static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
   1080 			 long volume, long *db_gain)
   1081 {
   1082 	if (init_db_range(ctl, rec) < 0)
   1083 		return -EINVAL;
   1084 	return snd_tlv_convert_to_dB(rec->db_info, rec->min, rec->max,
   1085 				     volume, db_gain);
   1086 }
   1087 
   1088 /* initialize dB range information, reading TLV via hcontrol
   1089  */
   1090 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec)
   1091 {
   1092 	snd_ctl_elem_info_t *info;
   1093 	unsigned int *tlv = NULL;
   1094 	const unsigned int tlv_size = 4096;
   1095 	unsigned int *dbrec;
   1096 	int db_size;
   1097 
   1098 	if (rec->db_init_error)
   1099 		return -EINVAL;
   1100 	if (rec->db_initialized)
   1101 		return 0;
   1102 
   1103 	snd_ctl_elem_info_alloca(&info);
   1104 	if (snd_hctl_elem_info(ctl, info) < 0)
   1105 		goto error;
   1106 	if (! snd_ctl_elem_info_is_tlv_readable(info))
   1107 		goto error;
   1108 	tlv = malloc(tlv_size);
   1109 	if (! tlv)
   1110 		return -ENOMEM;
   1111 	if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0)
   1112 		goto error;
   1113 	db_size = snd_tlv_parse_dB_info(tlv, tlv_size, &dbrec);
   1114 	if (db_size < 0)
   1115 		goto error;
   1116 	rec->db_info = malloc(db_size);
   1117 	if (!rec->db_info)
   1118 		goto error;
   1119 	memcpy(rec->db_info, dbrec, db_size);
   1120 	free(tlv);
   1121 	rec->db_initialized = 1;
   1122 	return 0;
   1123 
   1124  error:
   1125 	free(tlv);
   1126 	rec->db_init_error = 1;
   1127 	return -EINVAL;
   1128 }
   1129 
   1130 /* get selem_ctl for TLV access */
   1131 static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir)
   1132 {
   1133 	selem_ctl_t *c;
   1134 	if (dir == SM_PLAY)
   1135 		c = &s->ctls[CTL_PLAYBACK_VOLUME];
   1136 	else if (dir == SM_CAPT)
   1137 		c = &s->ctls[CTL_CAPTURE_VOLUME];
   1138 	else
   1139 		return NULL;
   1140 	if (! c->elem) {
   1141 		c = &s->ctls[CTL_GLOBAL_VOLUME];
   1142 		if (! c->elem)
   1143 			return NULL;
   1144 	}
   1145 	if (c->type != SND_CTL_ELEM_TYPE_INTEGER)
   1146 		return NULL;
   1147 	return c;
   1148 }
   1149 
   1150 static int get_dB_range(snd_hctl_elem_t *ctl, struct selem_str *rec,
   1151 			long *min, long *max)
   1152 {
   1153 	if (init_db_range(ctl, rec) < 0)
   1154 		return -EINVAL;
   1155 
   1156 	return snd_tlv_get_dB_range(rec->db_info, rec->min, rec->max, min, max);
   1157 }
   1158 
   1159 static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir,
   1160 			    long *min, long *max)
   1161 {
   1162 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1163 	selem_ctl_t *c;
   1164 
   1165 	if (s->selem.caps & SM_CAP_GVOLUME)
   1166 		dir = SM_PLAY;
   1167 	c = get_selem_ctl(s, dir);
   1168 	if (! c)
   1169 		return -EINVAL;
   1170 	return get_dB_range(c->elem, &s->str[dir], min, max);
   1171 }
   1172 
   1173 static int convert_from_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
   1174 			   long db_gain, long *value, int xdir)
   1175 {
   1176 	if (init_db_range(ctl, rec) < 0)
   1177 		return -EINVAL;
   1178 
   1179 	return snd_tlv_convert_from_dB(rec->db_info, rec->min, rec->max,
   1180 				       db_gain, value, xdir);
   1181 }
   1182 
   1183 static int ask_vol_dB_ops(snd_mixer_elem_t *elem,
   1184 			  int dir,
   1185 			  long value,
   1186 			  long *dBvalue)
   1187 {
   1188 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1189 	selem_ctl_t *c;
   1190 
   1191 	c = get_selem_ctl(s, dir);
   1192 	if (! c)
   1193 		return -EINVAL;
   1194 	int res = convert_to_dB(c->elem, &s->str[dir], value, dBvalue);
   1195 	return res;
   1196 }
   1197 
   1198 static int get_dB_ops(snd_mixer_elem_t *elem,
   1199                       int dir,
   1200                       snd_mixer_selem_channel_id_t channel,
   1201                       long *value)
   1202 {
   1203 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1204 	selem_ctl_t *c;
   1205 	int err;
   1206 	long volume, db_gain;
   1207 
   1208 	if (s->selem.caps & SM_CAP_GVOLUME)
   1209 		dir = SM_PLAY;
   1210 	c = get_selem_ctl(s, dir);
   1211 	if (! c)
   1212 		return -EINVAL;
   1213 	if ((err = get_volume_ops(elem, dir, channel, &volume)) < 0)
   1214 		goto _err;
   1215 	if ((err = convert_to_dB(c->elem, &s->str[dir], volume, &db_gain)) < 0)
   1216 		goto _err;
   1217 	err = 0;
   1218 	*value = db_gain;
   1219  _err:
   1220 	return err;
   1221 }
   1222 
   1223 static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
   1224 			  snd_mixer_selem_channel_id_t channel, int *value)
   1225 {
   1226 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1227 	if (s->selem.caps & SM_CAP_GSWITCH)
   1228 		dir = SM_PLAY;
   1229 	if ((unsigned int) channel >= s->str[dir].channels)
   1230 		return -EINVAL;
   1231 	*value = !!(s->str[dir].sw & (1 << channel));
   1232 	return 0;
   1233 }
   1234 
   1235 static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
   1236 			  snd_mixer_selem_channel_id_t channel, long value)
   1237 {
   1238 	int changed;
   1239 	changed = _snd_mixer_selem_set_volume(elem, dir, channel, value);
   1240 	if (changed < 0)
   1241 		return changed;
   1242 	if (changed)
   1243 		return selem_write(elem);
   1244 	return 0;
   1245 }
   1246 
   1247 static int ask_dB_vol_ops(snd_mixer_elem_t *elem, int dir,
   1248 		          long dbValue, long *value, int xdir)
   1249 {
   1250 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1251 	selem_ctl_t *c;
   1252 
   1253 	if (s->selem.caps & SM_CAP_GVOLUME)
   1254 		dir = SM_PLAY;
   1255 	c = get_selem_ctl(s, dir);
   1256 	if (! c)
   1257 		return -EINVAL;
   1258 	return convert_from_dB(c->elem, &s->str[dir], dbValue, value, xdir);
   1259 }
   1260 
   1261 static int set_dB_ops(snd_mixer_elem_t *elem, int dir,
   1262 		      snd_mixer_selem_channel_id_t channel,
   1263 		      long db_gain, int xdir)
   1264 {
   1265 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1266 	selem_ctl_t *c;
   1267 	long value;
   1268 	int err;
   1269 
   1270 	if (s->selem.caps & SM_CAP_GVOLUME)
   1271 		dir = SM_PLAY;
   1272 	c = get_selem_ctl(s, dir);
   1273 	if (! c)
   1274 		return -EINVAL;
   1275 	err = convert_from_dB(c->elem, &s->str[dir], db_gain, &value, xdir);
   1276 	if (err < 0)
   1277 		return err;
   1278 	return set_volume_ops(elem, dir, channel, value);
   1279 }
   1280 
   1281 static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
   1282 			  snd_mixer_selem_channel_id_t channel, int value)
   1283 {
   1284 	int changed;
   1285 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1286 	if (s->selem.caps & SM_CAP_GSWITCH)
   1287 		dir = SM_PLAY;
   1288 	if (dir == SM_PLAY) {
   1289 		if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)))
   1290 			return -EINVAL;
   1291 	} else {
   1292 		if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)))
   1293 			return -EINVAL;
   1294 	}
   1295 	changed = _snd_mixer_selem_set_switch(elem, dir, channel, value);
   1296 	if (changed < 0)
   1297 		return changed;
   1298 	if (changed)
   1299 		return selem_write(elem);
   1300 	return 0;
   1301 }
   1302 
   1303 static int enum_item_name_ops(snd_mixer_elem_t *elem,
   1304 			      unsigned int item,
   1305 			      size_t maxlen, char *buf)
   1306 {
   1307 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1308 	snd_ctl_elem_info_t *info;
   1309 	snd_hctl_elem_t *helem;
   1310 	int type;
   1311 
   1312 	type = CTL_GLOBAL_ENUM;
   1313 	helem = s->ctls[type].elem;
   1314 	if (!helem) {
   1315 		type = CTL_PLAYBACK_ENUM;
   1316 		helem = s->ctls[type].elem;
   1317 	}
   1318 	if (!helem) {
   1319 		type = CTL_CAPTURE_ENUM;
   1320 		helem = s->ctls[type].elem;
   1321 	}
   1322 	assert(helem);
   1323 	if (item >= (unsigned int)s->ctls[type].max)
   1324 		return -EINVAL;
   1325 	snd_ctl_elem_info_alloca(&info);
   1326 	snd_hctl_elem_info(helem, info);
   1327 	snd_ctl_elem_info_set_item(info, item);
   1328 	snd_hctl_elem_info(helem, info);
   1329 	strncpy(buf, snd_ctl_elem_info_get_item_name(info), maxlen);
   1330 	return 0;
   1331 }
   1332 
   1333 static int get_enum_item_ops(snd_mixer_elem_t *elem,
   1334 			     snd_mixer_selem_channel_id_t channel,
   1335 			     unsigned int *itemp)
   1336 {
   1337 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1338 	snd_ctl_elem_value_t *ctl;
   1339 	snd_hctl_elem_t *helem;
   1340 	int err;
   1341 
   1342 	if ((unsigned int) channel >= s->str[0].channels)
   1343 		return -EINVAL;
   1344 	helem = s->ctls[CTL_GLOBAL_ENUM].elem;
   1345 	if (!helem) helem = s->ctls[CTL_PLAYBACK_ENUM].elem;
   1346 	if (!helem) helem = s->ctls[CTL_CAPTURE_ENUM].elem;
   1347 	assert(helem);
   1348 	snd_ctl_elem_value_alloca(&ctl);
   1349 	err = snd_hctl_elem_read(helem, ctl);
   1350 	if (! err)
   1351 		*itemp = snd_ctl_elem_value_get_enumerated(ctl, channel);
   1352 	return err;
   1353 }
   1354 
   1355 static int set_enum_item_ops(snd_mixer_elem_t *elem,
   1356 			     snd_mixer_selem_channel_id_t channel,
   1357 			     unsigned int item)
   1358 {
   1359 	selem_none_t *s = snd_mixer_elem_get_private(elem);
   1360 	snd_ctl_elem_value_t *ctl;
   1361 	snd_hctl_elem_t *helem;
   1362 	int err;
   1363 	int type;
   1364 
   1365 	if ((unsigned int) channel >= s->str[0].channels) {
   1366 		return -EINVAL;
   1367 	}
   1368 	type = CTL_GLOBAL_ENUM;
   1369 	helem = s->ctls[type].elem;
   1370 	if (!helem) {
   1371 		type = CTL_PLAYBACK_ENUM;
   1372 		helem = s->ctls[type].elem;
   1373 	}
   1374 	if (!helem) {
   1375 		type = CTL_CAPTURE_ENUM;
   1376 		helem = s->ctls[type].elem;
   1377 	}
   1378 	assert(helem);
   1379 	if (item >= (unsigned int)s->ctls[type].max) {
   1380 		return -EINVAL;
   1381 	}
   1382 	snd_ctl_elem_value_alloca(&ctl);
   1383 	err = snd_hctl_elem_read(helem, ctl);
   1384 	if (err < 0) {
   1385 		return err;
   1386 	}
   1387 	snd_ctl_elem_value_set_enumerated(ctl, channel, item);
   1388 	return snd_hctl_elem_write(helem, ctl);
   1389 }
   1390 
   1391 static struct sm_elem_ops simple_none_ops = {
   1392 	.is		= is_ops,
   1393 	.get_range	= get_range_ops,
   1394 	.get_dB_range	= get_dB_range_ops,
   1395 	.set_range	= set_range_ops,
   1396 	.ask_vol_dB	= ask_vol_dB_ops,
   1397 	.ask_dB_vol	= ask_dB_vol_ops,
   1398 	.get_volume	= get_volume_ops,
   1399 	.get_dB		= get_dB_ops,
   1400 	.set_volume	= set_volume_ops,
   1401 	.set_dB		= set_dB_ops,
   1402 	.get_switch	= get_switch_ops,
   1403 	.set_switch	= set_switch_ops,
   1404 	.enum_item_name	= enum_item_name_ops,
   1405 	.get_enum_item	= get_enum_item_ops,
   1406 	.set_enum_item	= set_enum_item_ops
   1407 };
   1408 
   1409 static int simple_add1(snd_mixer_class_t *class, const char *name,
   1410 		       snd_hctl_elem_t *helem, selem_ctl_type_t type,
   1411 		       unsigned int value)
   1412 {
   1413 	snd_mixer_elem_t *melem;
   1414 	snd_mixer_selem_id_t *id;
   1415 	int new = 0;
   1416 	int err;
   1417 	snd_ctl_elem_info_t *info;
   1418 	selem_none_t *simple;
   1419 	const char *name1;
   1420 	snd_ctl_elem_type_t ctype;
   1421 	unsigned long values;
   1422 
   1423 	snd_ctl_elem_info_alloca(&info);
   1424 	err = snd_hctl_elem_info(helem, info);
   1425 	if (err < 0)
   1426 		return err;
   1427 	ctype = snd_ctl_elem_info_get_type(info);
   1428 	values = snd_ctl_elem_info_get_count(info);
   1429 	switch (type) {
   1430 	case CTL_SINGLE:
   1431 		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED)
   1432 			type = CTL_GLOBAL_ENUM;
   1433 		else if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN &&
   1434 		         ctype != SND_CTL_ELEM_TYPE_INTEGER)
   1435 			return 0;
   1436 		break;
   1437 	case CTL_GLOBAL_ROUTE:
   1438 	case CTL_PLAYBACK_ROUTE:
   1439 	case CTL_CAPTURE_ROUTE:
   1440 	{
   1441 		unsigned int n;
   1442 		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
   1443 			if (type == CTL_PLAYBACK_ROUTE)
   1444 				type = CTL_PLAYBACK_ENUM;
   1445 			else if (type == CTL_CAPTURE_ROUTE)
   1446 				type = CTL_CAPTURE_ENUM;
   1447 			else
   1448 				type = CTL_GLOBAL_ENUM;
   1449 			break;
   1450 		}
   1451 		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
   1452 			return 0;
   1453 		n = sqrt((double)values);
   1454 		if (n * n != values)
   1455 			return 0;
   1456 		values = n;
   1457 		break;
   1458 	}
   1459 	case CTL_GLOBAL_SWITCH:
   1460 	case CTL_PLAYBACK_SWITCH:
   1461 	case CTL_CAPTURE_SWITCH:
   1462 		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
   1463 			if (type == CTL_PLAYBACK_SWITCH)
   1464 				type = CTL_PLAYBACK_ENUM;
   1465 			else if (type == CTL_CAPTURE_SWITCH)
   1466 				type = CTL_CAPTURE_ENUM;
   1467 			else
   1468 				type = CTL_GLOBAL_ENUM;
   1469 			break;
   1470 		}
   1471 		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
   1472 			return 0;
   1473 		break;
   1474 	case CTL_GLOBAL_VOLUME:
   1475 	case CTL_PLAYBACK_VOLUME:
   1476 	case CTL_CAPTURE_VOLUME:
   1477 		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
   1478 			if (type == CTL_PLAYBACK_VOLUME)
   1479 				type = CTL_PLAYBACK_ENUM;
   1480 			else if (type == CTL_CAPTURE_VOLUME)
   1481 				type = CTL_CAPTURE_ENUM;
   1482 			else
   1483 				type = CTL_GLOBAL_ENUM;
   1484 			break;
   1485 		}
   1486 		if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
   1487 			return 0;
   1488 		break;
   1489 	case CTL_CAPTURE_SOURCE:
   1490 		if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED)
   1491 			return 0;
   1492 		break;
   1493 	case CTL_GLOBAL_ENUM:
   1494 	case CTL_PLAYBACK_ENUM:
   1495 	case CTL_CAPTURE_ENUM:
   1496 		if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED)
   1497 			return 0;
   1498 		break;
   1499 	default:
   1500 		assert(0);
   1501 		break;
   1502 	}
   1503 	name1 = get_short_name(name);
   1504 	if (snd_mixer_selem_id_malloc(&id))
   1505 		return -ENOMEM;
   1506 	snd_mixer_selem_id_set_name(id, name1);
   1507 	snd_mixer_selem_id_set_index(id, snd_hctl_elem_get_index(helem));
   1508 	melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
   1509 	if (!melem) {
   1510 		simple = calloc(1, sizeof(*simple));
   1511 		if (!simple) {
   1512 			snd_mixer_selem_id_free(id);
   1513 			return -ENOMEM;
   1514 		}
   1515 		simple->selem.id = id;
   1516 		simple->selem.ops = &simple_none_ops;
   1517 		err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
   1518 					 get_compare_weight(snd_mixer_selem_id_get_name(simple->selem.id), snd_mixer_selem_id_get_index(simple->selem.id)),
   1519 					 simple, selem_free);
   1520 		if (err < 0) {
   1521 			snd_mixer_selem_id_free(id);
   1522 			free(simple);
   1523 			return err;
   1524 		}
   1525 		new = 1;
   1526 	} else {
   1527 		simple = snd_mixer_elem_get_private(melem);
   1528 		snd_mixer_selem_id_free(id);
   1529 	}
   1530 	if (simple->ctls[type].elem) {
   1531 		SNDERR("helem (%s,'%s',%u,%u,%u) appears twice or more",
   1532 				snd_ctl_elem_iface_name(snd_hctl_elem_get_interface(helem)),
   1533 				snd_hctl_elem_get_name(helem),
   1534 				snd_hctl_elem_get_index(helem),
   1535 				snd_hctl_elem_get_device(helem),
   1536 				snd_hctl_elem_get_subdevice(helem));
   1537 		err = -EINVAL;
   1538 		goto __error;
   1539 	}
   1540 	simple->ctls[type].elem = helem;
   1541 	simple->ctls[type].type = snd_ctl_elem_info_get_type(info);
   1542 	simple->ctls[type].inactive = snd_ctl_elem_info_is_inactive(info);
   1543 	simple->ctls[type].values = values;
   1544 	if ( (type == CTL_GLOBAL_ENUM) ||
   1545 	     (type == CTL_PLAYBACK_ENUM) ||
   1546 	     (type == CTL_CAPTURE_ENUM) ) {
   1547 		simple->ctls[type].min = 0;
   1548 		simple->ctls[type].max = snd_ctl_elem_info_get_items(info);
   1549 	} else {
   1550 		if (ctype == SND_CTL_ELEM_TYPE_INTEGER) {
   1551 			simple->ctls[type].min = snd_ctl_elem_info_get_min(info);
   1552 			simple->ctls[type].max = snd_ctl_elem_info_get_max(info);
   1553 		}
   1554 	}
   1555 	switch (type) {
   1556 	case CTL_CAPTURE_SOURCE:
   1557 		simple->capture_item = value;
   1558 		break;
   1559 	default:
   1560 		break;
   1561 	}
   1562 	err = snd_mixer_elem_attach(melem, helem);
   1563 	if (err < 0)
   1564 		goto __error;
   1565 	err = simple_update(melem);
   1566 	if (err < 0) {
   1567 		if (new)
   1568 			goto __error;
   1569 		return err;
   1570 	}
   1571 	if (new)
   1572 		err = snd_mixer_elem_add(melem, class);
   1573 	else
   1574 		err = snd_mixer_elem_info(melem);
   1575 	if (err < 0)
   1576 		return err;
   1577 	err = selem_read(melem);
   1578 	if (err < 0)
   1579 		return err;
   1580 	if (err)
   1581 		err = snd_mixer_elem_value(melem);
   1582 	return err;
   1583       __error:
   1584 	if (new)
   1585 		snd_mixer_elem_free(melem);
   1586 	return -EINVAL;
   1587 }
   1588 
   1589 static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
   1590 {
   1591 	const char *name = snd_hctl_elem_get_name(helem);
   1592 	size_t len;
   1593 	selem_ctl_type_t type = CTL_SINGLE; /* to shut up warning */
   1594 	if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER)
   1595 		return 0;
   1596 	if (strcmp(name, "Capture Source") == 0) {
   1597 		snd_ctl_elem_info_t *info;
   1598 		unsigned int k, items;
   1599 		int err;
   1600 		snd_ctl_elem_info_alloca(&info);
   1601 		err = snd_hctl_elem_info(helem, info);
   1602 		assert(err >= 0);
   1603 		if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_ENUMERATED)
   1604 			return 0;
   1605 		items = snd_ctl_elem_info_get_items(info);
   1606 		for (k = 0; k < items; ++k) {
   1607 			const char *n;
   1608 			snd_ctl_elem_info_set_item(info, k);
   1609 			err = snd_hctl_elem_info(helem, info);
   1610 			if (err < 0)
   1611 				return err;
   1612 			n = snd_ctl_elem_info_get_item_name(info);
   1613 			err = simple_add1(class, n, helem, CTL_CAPTURE_SOURCE, k);
   1614 			if (err < 0)
   1615 				return err;
   1616 		}
   1617 		return 0;
   1618 	}
   1619 	len = base_len(name, &type);
   1620 	if (len == 0) {
   1621 		return simple_add1(class, name, helem, CTL_SINGLE, 0);
   1622 	} else {
   1623 		char ename[128];
   1624 		if (len >= sizeof(ename))
   1625 			len = sizeof(ename) - 1;
   1626 		memcpy(ename, name, len);
   1627 		ename[len] = 0;
   1628 		/* exception: Capture Volume and Capture Switch */
   1629 		if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture"))
   1630 			type = CTL_CAPTURE_VOLUME;
   1631 		else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture"))
   1632 			type = CTL_CAPTURE_SWITCH;
   1633 		return simple_add1(class, ename, helem, type, 0);
   1634 	}
   1635 }
   1636 
   1637 static int simple_event_remove(snd_hctl_elem_t *helem,
   1638 			       snd_mixer_elem_t *melem)
   1639 {
   1640 	selem_none_t *simple = snd_mixer_elem_get_private(melem);
   1641 	int err;
   1642 	int k;
   1643 	for (k = 0; k <= CTL_LAST; k++) {
   1644 		if (simple->ctls[k].elem == helem)
   1645 			break;
   1646 	}
   1647 	assert(k <= CTL_LAST);
   1648 	simple->ctls[k].elem = NULL;
   1649 	err = snd_mixer_elem_detach(melem, helem);
   1650 	if (err < 0)
   1651 		return err;
   1652 	if (snd_mixer_elem_empty(melem))
   1653 		return snd_mixer_elem_remove(melem);
   1654 	err = simple_update(melem);
   1655 	return snd_mixer_elem_info(melem);
   1656 }
   1657 
   1658 static int simple_event(snd_mixer_class_t *class, unsigned int mask,
   1659 			snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
   1660 {
   1661 	int err;
   1662 	if (mask == SND_CTL_EVENT_MASK_REMOVE)
   1663 		return simple_event_remove(helem, melem);
   1664 	if (mask & SND_CTL_EVENT_MASK_ADD) {
   1665 		err = simple_event_add(class, helem);
   1666 		if (err < 0)
   1667 			return err;
   1668 	}
   1669 	if (mask & SND_CTL_EVENT_MASK_INFO) {
   1670 		err = simple_event_remove(helem, melem);
   1671 		if (err < 0)
   1672 			return err;
   1673 		err = simple_event_add(class, helem);
   1674 		if (err < 0)
   1675 			return err;
   1676 		return 0;
   1677 	}
   1678 	if (mask & SND_CTL_EVENT_MASK_VALUE) {
   1679 		err = selem_read(melem);
   1680 		if (err < 0)
   1681 			return err;
   1682 		if (err) {
   1683 			err = snd_mixer_elem_value(melem);
   1684 			if (err < 0)
   1685 				return err;
   1686 		}
   1687 	}
   1688 	return 0;
   1689 }
   1690 
   1691 /**
   1692  * \brief Register mixer simple element class - none abstraction
   1693  * \param mixer Mixer handle
   1694  * \param options Options container
   1695  * \param classp Pointer to returned mixer simple element class handle (or NULL)
   1696  * \return 0 on success otherwise a negative error code
   1697  */
   1698 int snd_mixer_simple_none_register(snd_mixer_t *mixer,
   1699 				   struct snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED,
   1700 				   snd_mixer_class_t **classp)
   1701 {
   1702 	snd_mixer_class_t *class;
   1703 	int err;
   1704 
   1705 	if (snd_mixer_class_malloc(&class))
   1706 		return -ENOMEM;
   1707 	snd_mixer_class_set_event(class, simple_event);
   1708 	snd_mixer_class_set_compare(class, snd_mixer_selem_compare);
   1709 	err = snd_mixer_class_register(class, mixer);
   1710 	if (err < 0) {
   1711 		free(class);
   1712 		return err;
   1713 	}
   1714 	if (classp)
   1715 		*classp = class;
   1716 	return 0;
   1717 }
   1718