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