• 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 <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 "local.h"
42 #include "config.h"
43 #include "mixer_simple.h"
44 
45 #ifndef DOC_HIDDEN
46 
47 #define MIXER_COMPARE_WEIGHT_SIMPLE_BASE        0
48 #define MIXER_COMPARE_WEIGHT_NEXT_BASE          10000000
49 #define MIXER_COMPARE_WEIGHT_NOT_FOUND          1000000000
50 
51 typedef enum _selem_ctl_type {
52 	CTL_SINGLE,
53 	CTL_GLOBAL_ENUM,
54 	CTL_GLOBAL_SWITCH,
55 	CTL_GLOBAL_VOLUME,
56 	CTL_GLOBAL_ROUTE,
57 	CTL_PLAYBACK_ENUM,
58 	CTL_PLAYBACK_SWITCH,
59 	CTL_PLAYBACK_VOLUME,
60 	CTL_PLAYBACK_ROUTE,
61 	CTL_CAPTURE_ENUM,
62 	CTL_CAPTURE_SWITCH,
63 	CTL_CAPTURE_VOLUME,
64 	CTL_CAPTURE_ROUTE,
65 	CTL_CAPTURE_SOURCE,
66 	CTL_LAST = CTL_CAPTURE_SOURCE,
67 } selem_ctl_type_t;
68 
69 typedef struct _selem_ctl {
70 	snd_hctl_elem_t *elem;
71 	snd_ctl_elem_type_t type;
72 	unsigned int inactive: 1;
73 	unsigned int values;
74 	long min, max;
75 } selem_ctl_t;
76 
77 typedef struct _selem_none {
78 	sm_selem_t selem;
79 	selem_ctl_t ctls[CTL_LAST + 1];
80 	unsigned int capture_item;
81 	struct selem_str {
82 		unsigned int range: 1;	/* Forced range */
83 		unsigned int db_initialized: 1;
84 		unsigned int db_init_error: 1;
85 		long min, max;
86 		unsigned int channels;
87 		long vol[32];
88 		unsigned int sw;
89 		unsigned int *db_info;
90 	} str[2];
91 } selem_none_t;
92 
93 static const struct mixer_name_table {
94 	const char *longname;
95 	const char *shortname;
96 } name_table[] = {
97 	{"Tone Control - Switch", "Tone"},
98 	{"Tone Control - Bass", "Bass"},
99 	{"Tone Control - Treble", "Treble"},
100 	{"Synth Tone Control - Switch", "Synth Tone"},
101 	{"Synth Tone Control - Bass", "Synth Bass"},
102 	{"Synth Tone Control - Treble", "Synth Treble"},
103 	{0, 0},
104 };
105 
106 #endif /* !DOC_HIDDEN */
107 
get_short_name(const char * lname)108 static const char *get_short_name(const char *lname)
109 {
110 	const struct mixer_name_table *p;
111 	for (p = name_table; p->longname; p++) {
112 		if (!strcmp(lname, p->longname))
113 			return p->shortname;
114 	}
115 	return lname;
116 }
117 
compare_mixer_priority_lookup(const char ** name,const char * const * names,int coef)118 static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
119 {
120 	int res;
121 
122 	for (res = 0; *names; names++, res += coef) {
123 		if (!strncmp(*name, *names, strlen(*names))) {
124 			*name += strlen(*names);
125 			if (**name == ' ')
126 				(*name)++;
127 			return res+1;
128 		}
129 	}
130 	return MIXER_COMPARE_WEIGHT_NOT_FOUND;
131 }
132 
get_compare_weight(const char * name,unsigned int idx)133 static int get_compare_weight(const char *name, unsigned int idx)
134 {
135 	static const char *const names[] = {
136 		"Master",
137 		"Headphone",
138 		"Speaker",
139 		"Tone",
140 		"Bass",
141 		"Treble",
142 		"3D Control",
143 		"PCM",
144 		"Front",
145 		"Surround",
146 		"Center",
147 		"LFE",
148 		"Side",
149 		"Synth",
150 		"FM",
151 		"Wave",
152 		"Music",
153 		"DSP",
154 		"Line",
155 		"CD",
156 		"Mic",
157 		"Video",
158 		"Zoom Video",
159 		"Phone",
160 		"I2S",
161 		"IEC958",
162 		"PC Speaker",
163 		"Beep",
164 		"Aux",
165 		"Mono",
166 		"Playback",
167 		"Capture",
168 		"Mix",
169 		NULL
170 	};
171 	static const char *const names1[] = {
172 		"-",
173 		NULL,
174 	};
175 	static const char *const names2[] = {
176 		"Mono",
177 		"Digital",
178 		"Switch",
179 		"Depth",
180 		"Wide",
181 		"Space",
182 		"Level",
183 		"Center",
184 		"Output",
185 		"Boost",
186 		"Tone",
187 		"Bass",
188 		"Treble",
189 		NULL,
190 	};
191 	const char *name1;
192 	int res, res1;
193 
194 	if ((res = compare_mixer_priority_lookup((const char **)&name, names, 1000)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
195 		return MIXER_COMPARE_WEIGHT_NOT_FOUND;
196 	if (*name == '\0')
197 		goto __res;
198 	for (name1 = name; *name1 != '\0'; name1++);
199 	for (name1--; name1 != name && *name1 != ' '; name1--);
200 	while (name1 != name && *name1 == ' ')
201 		name1--;
202 	if (name1 != name) {
203 		for (; name1 != name && *name1 != ' '; name1--);
204 		name = name1;
205 		if ((res1 = compare_mixer_priority_lookup((const char **)&name, names1, 200)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
206 			return res;
207 		res += res1;
208 	} else {
209 		name = name1;
210 	}
211 	if ((res1 = compare_mixer_priority_lookup((const char **)&name, names2, 20)) == MIXER_COMPARE_WEIGHT_NOT_FOUND)
212 		return res;
213       __res:
214 	return MIXER_COMPARE_WEIGHT_SIMPLE_BASE + res + idx;
215 }
216 
to_user(selem_none_t * s,int dir,selem_ctl_t * c,long value)217 static long to_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
218 {
219 	int64_t n;
220 	if (c->max == c->min)
221 		return s->str[dir].min;
222 	n = (int64_t) (value - c->min) * (s->str[dir].max - s->str[dir].min);
223 	return s->str[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min);
224 }
225 
from_user(selem_none_t * s,int dir,selem_ctl_t * c,long value)226 static long from_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
227 {
228 	int64_t n;
229 	if (s->str[dir].max == s->str[dir].min)
230 		return c->min;
231 	n = (int64_t) (value - s->str[dir].min) * (c->max - c->min);
232 	return c->min + (n + (s->str[dir].max - s->str[dir].min) / 2) / (s->str[dir].max - s->str[dir].min);
233 }
234 
elem_read_volume(selem_none_t * s,int dir,selem_ctl_type_t type)235 static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
236 {
237 	snd_ctl_elem_value_t ctl = {0};
238 	unsigned int idx;
239 	int err;
240 	selem_ctl_t *c = &s->ctls[type];
241 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
242 		return err;
243 	for (idx = 0; idx < s->str[dir].channels; idx++) {
244 		unsigned int idx1 = idx;
245 		if (idx >= c->values)
246 			idx1 = 0;
247 		s->str[dir].vol[idx] =
248 			to_user(s, dir, c,
249 				snd_ctl_elem_value_get_integer(&ctl, idx1));
250 	}
251 	return 0;
252 }
253 
elem_read_switch(selem_none_t * s,int dir,selem_ctl_type_t type)254 static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
255 {
256 	snd_ctl_elem_value_t ctl = {0};
257 	unsigned int idx;
258 	int err;
259 	selem_ctl_t *c = &s->ctls[type];
260 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
261 		return err;
262 	for (idx = 0; idx < s->str[dir].channels; idx++) {
263 		unsigned int idx1 = idx;
264 		if (idx >= c->values)
265 			idx1 = 0;
266 		if (!snd_ctl_elem_value_get_integer(&ctl, idx1))
267 			s->str[dir].sw &= ~(1 << idx);
268 	}
269 	return 0;
270 }
271 
elem_read_route(selem_none_t * s,int dir,selem_ctl_type_t type)272 static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type)
273 {
274 	snd_ctl_elem_value_t ctl = {0};
275 	unsigned int idx;
276 	int err;
277 	selem_ctl_t *c = &s->ctls[type];
278 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
279 		return err;
280 	for (idx = 0; idx < s->str[dir].channels; idx++) {
281 		unsigned int idx1 = idx;
282 		if (idx >= c->values)
283 			idx1 = 0;
284 		if (!snd_ctl_elem_value_get_integer(&ctl,
285 						    idx1 * c->values + idx1))
286 			s->str[dir].sw &= ~(1 << idx);
287 	}
288 	return 0;
289 }
290 
elem_read_enum(selem_none_t * s)291 static int elem_read_enum(selem_none_t *s)
292 {
293 	snd_ctl_elem_value_t ctl = {0};
294 	unsigned int idx;
295 	int err;
296 	int type;
297 	selem_ctl_t *c;
298 	type = CTL_GLOBAL_ENUM;
299 	if ((s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) ==
300 						(SM_CAP_CENUM | SM_CAP_PENUM))
301 		type = CTL_GLOBAL_ENUM;
302 	else if (s->selem.caps & SM_CAP_PENUM)
303 		type = CTL_PLAYBACK_ENUM;
304 	else if (s->selem.caps & SM_CAP_CENUM)
305 		type = CTL_CAPTURE_ENUM;
306 	c = &s->ctls[type];
307 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
308 		return err;
309 	for (idx = 0; idx < s->str[0].channels; idx++) {
310 		unsigned int idx1 = idx;
311 		if (idx >= c->values)
312 			idx1 = 0;
313 		s->str[0].vol[idx] =
314 				snd_ctl_elem_value_get_enumerated(&ctl, idx1);
315 	}
316 	return 0;
317 }
318 
selem_read(snd_mixer_elem_t * elem)319 static int selem_read(snd_mixer_elem_t *elem)
320 {
321 	selem_none_t *s;
322 	unsigned int idx;
323 	int err = 0;
324 	long pvol[32], cvol[32];
325 	unsigned int psw, csw;
326 
327 	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
328 	s = snd_mixer_elem_get_private(elem);
329 
330 	memcpy(pvol, s->str[SM_PLAY].vol, sizeof(pvol));
331 	memset(&s->str[SM_PLAY].vol, 0, sizeof(s->str[SM_PLAY].vol));
332 	psw = s->str[SM_PLAY].sw;
333 	s->str[SM_PLAY].sw = ~0U;
334 	memcpy(cvol, s->str[SM_CAPT].vol, sizeof(cvol));
335 	memset(&s->str[SM_CAPT].vol, 0, sizeof(s->str[SM_CAPT].vol));
336 	csw = s->str[SM_CAPT].sw;
337 	s->str[SM_CAPT].sw = ~0U;
338 
339 	if (s->ctls[CTL_GLOBAL_ENUM].elem) {
340 		err = elem_read_enum(s);
341 		if (err < 0)
342 			return err;
343 		goto __skip_cswitch;
344 	}
345 
346 	if (s->ctls[CTL_CAPTURE_ENUM].elem) {
347 		err = elem_read_enum(s);
348 		if (err < 0)
349 			return err;
350 		goto __skip_cswitch;
351 	}
352 
353 	if (s->ctls[CTL_PLAYBACK_ENUM].elem) {
354 		err = elem_read_enum(s);
355 		if (err < 0)
356 			return err;
357 		goto __skip_cswitch;
358 	}
359 
360 
361 	if (s->ctls[CTL_PLAYBACK_VOLUME].elem)
362 		err = elem_read_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
363 	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
364 		err = elem_read_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
365 	else if (s->ctls[CTL_SINGLE].elem &&
366 		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
367 		err = elem_read_volume(s, SM_PLAY, CTL_SINGLE);
368 	if (err < 0)
369 		return err;
370 
371 	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) {
372 		s->str[SM_PLAY].sw = 0;
373 		goto __skip_pswitch;
374 	}
375 	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
376 		err = elem_read_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
377 		if (err < 0)
378 			return err;
379 	}
380 	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
381 		err = elem_read_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
382 		if (err < 0)
383 			return err;
384 	}
385 	if (s->ctls[CTL_SINGLE].elem &&
386 	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
387 		err = elem_read_switch(s, SM_PLAY, CTL_SINGLE);
388 		if (err < 0)
389 			return err;
390 	}
391 	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
392 		err = elem_read_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
393 		if (err < 0)
394 			return err;
395 	}
396 	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
397 		err = elem_read_route(s, SM_PLAY, CTL_GLOBAL_ROUTE);
398 		if (err < 0)
399 			return err;
400 	}
401       __skip_pswitch:
402 
403 	if (s->ctls[CTL_CAPTURE_VOLUME].elem)
404 		err = elem_read_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
405 	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
406 		err = elem_read_volume(s, SM_CAPT, CTL_GLOBAL_VOLUME);
407 	else if (s->ctls[CTL_SINGLE].elem &&
408 		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
409 		err = elem_read_volume(s, SM_CAPT, CTL_SINGLE);
410 	if (err < 0)
411 		return err;
412 
413 	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) {
414 		s->str[SM_CAPT].sw = 0;
415 		goto __skip_cswitch;
416 	}
417 	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
418 		err = elem_read_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
419 		if (err < 0)
420 			return err;
421 	}
422 	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
423 		err = elem_read_switch(s, SM_CAPT, CTL_GLOBAL_SWITCH);
424 		if (err < 0)
425 			return err;
426 	}
427 	if (s->ctls[CTL_SINGLE].elem &&
428 	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
429 		err = elem_read_switch(s, SM_CAPT, CTL_SINGLE);
430 		if (err < 0)
431 			return err;
432 	}
433 	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
434 		err = elem_read_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
435 		if (err < 0)
436 			return err;
437 	}
438 	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
439 		err = elem_read_route(s, SM_CAPT, CTL_GLOBAL_ROUTE);
440 		if (err < 0)
441 			return err;
442 	}
443 	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
444 		snd_ctl_elem_value_t ctl = {0};
445 		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
446 		err = snd_hctl_elem_read(c->elem, &ctl);
447 		if (err < 0)
448 			return err;
449 		for (idx = 0; idx < s->str[SM_CAPT].channels; idx++) {
450 			unsigned int idx1 = idx;
451 			if (idx >= c->values)
452 				idx1 = 0;
453 			if (snd_ctl_elem_value_get_enumerated(&ctl, idx1) !=
454 								s->capture_item)
455 				s->str[SM_CAPT].sw &= ~(1 << idx);
456 		}
457 	}
458       __skip_cswitch:
459 
460 	if (memcmp(pvol, s->str[SM_PLAY].vol, sizeof(pvol)) ||
461 	    psw != s->str[SM_PLAY].sw ||
462 	    memcmp(cvol, s->str[SM_CAPT].vol, sizeof(cvol)) ||
463 	    csw != s->str[SM_CAPT].sw)
464 		return 1;
465 	return 0;
466 }
467 
elem_write_volume(selem_none_t * s,int dir,selem_ctl_type_t type)468 static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
469 {
470 	snd_ctl_elem_value_t ctl = {0};
471 	unsigned int idx;
472 	int err;
473 	selem_ctl_t *c = &s->ctls[type];
474 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
475 		return err;
476 	for (idx = 0; idx < c->values; idx++)
477 		snd_ctl_elem_value_set_integer(&ctl, idx,
478 				from_user(s, dir, c, s->str[dir].vol[idx]));
479 	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
480 		return err;
481 	return 0;
482 }
483 
elem_write_switch(selem_none_t * s,int dir,selem_ctl_type_t type)484 static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
485 {
486 	snd_ctl_elem_value_t ctl = {0};
487 	unsigned int idx;
488 	int err;
489 	selem_ctl_t *c = &s->ctls[type];
490 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
491 		return err;
492 	for (idx = 0; idx < c->values; idx++)
493 		snd_ctl_elem_value_set_integer(&ctl, idx,
494 					!!(s->str[dir].sw & (1 << idx)));
495 	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
496 		return err;
497 	return 0;
498 }
499 
elem_write_switch_constant(selem_none_t * s,selem_ctl_type_t type,int val)500 static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val)
501 {
502 	snd_ctl_elem_value_t ctl = {0};
503 	unsigned int idx;
504 	int err;
505 	selem_ctl_t *c = &s->ctls[type];
506 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
507 		return err;
508 	for (idx = 0; idx < c->values; idx++)
509 		snd_ctl_elem_value_set_integer(&ctl, idx, !!val);
510 	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
511 		return err;
512 	return 0;
513 }
514 
elem_write_route(selem_none_t * s,int dir,selem_ctl_type_t type)515 static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type)
516 {
517 	snd_ctl_elem_value_t ctl = {0};
518 	unsigned int idx;
519 	int err;
520 	selem_ctl_t *c = &s->ctls[type];
521 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
522 		return err;
523 	for (idx = 0; idx < c->values * c->values; idx++)
524 		snd_ctl_elem_value_set_integer(&ctl, idx, 0);
525 	for (idx = 0; idx < c->values; idx++)
526 		snd_ctl_elem_value_set_integer(&ctl, idx * c->values + idx,
527 					       !!(s->str[dir].sw & (1 << idx)));
528 	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
529 		return err;
530 	return 0;
531 }
532 
elem_write_enum(selem_none_t * s)533 static int elem_write_enum(selem_none_t *s)
534 {
535 	snd_ctl_elem_value_t ctl = {0};
536 	unsigned int idx;
537 	int err;
538 	int type;
539 	selem_ctl_t *c;
540 	type = CTL_GLOBAL_ENUM;
541 	if ((s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) ==
542 						(SM_CAP_CENUM | SM_CAP_PENUM))
543 		type = CTL_GLOBAL_ENUM;
544 	else if (s->selem.caps & SM_CAP_PENUM)
545 		type = CTL_PLAYBACK_ENUM;
546 	else if (s->selem.caps & SM_CAP_CENUM)
547 		type = CTL_CAPTURE_ENUM;
548 	c = &s->ctls[type];
549 	if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
550 		return err;
551 	for (idx = 0; idx < c->values; idx++)
552 		snd_ctl_elem_value_set_enumerated(&ctl, idx,
553 					(unsigned int)s->str[0].vol[idx]);
554 	if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
555 		return err;
556 	return 0;
557 }
558 
selem_write_main(snd_mixer_elem_t * elem)559 static int selem_write_main(snd_mixer_elem_t *elem)
560 {
561 	selem_none_t *s;
562 	unsigned int idx;
563 	int err;
564 
565 	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
566 	s = snd_mixer_elem_get_private(elem);
567 
568 	if (s->ctls[CTL_GLOBAL_ENUM].elem)
569 		return elem_write_enum(s);
570 
571 	if (s->ctls[CTL_PLAYBACK_ENUM].elem)
572 		return elem_write_enum(s);
573 
574 	if (s->ctls[CTL_CAPTURE_ENUM].elem)
575 		return elem_write_enum(s);
576 
577 	if (s->ctls[CTL_SINGLE].elem) {
578 		if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
579 			err = elem_write_volume(s, SM_PLAY, CTL_SINGLE);
580 		else
581 			err = elem_write_switch(s, SM_PLAY, CTL_SINGLE);
582 		if (err < 0)
583 			return err;
584 	}
585 	if (s->ctls[CTL_GLOBAL_VOLUME].elem) {
586 		err = elem_write_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
587 		if (err < 0)
588 			return err;
589 	}
590 	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
591 		if (s->ctls[CTL_PLAYBACK_SWITCH].elem &&
592 					s->ctls[CTL_CAPTURE_SWITCH].elem)
593 			err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH,
594 							 1);
595 		else
596 			err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
597 		if (err < 0)
598 			return err;
599 	}
600 	if (s->ctls[CTL_PLAYBACK_VOLUME].elem) {
601 		err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
602 		if (err < 0)
603 			return err;
604 	}
605 	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
606 		err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
607 		if (err < 0)
608 			return err;
609 	}
610 	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
611 		err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
612 		if (err < 0)
613 			return err;
614 	}
615 	if (s->ctls[CTL_CAPTURE_VOLUME].elem) {
616 		err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
617 		if (err < 0)
618 			return err;
619 	}
620 	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
621 		err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
622 		if (err < 0)
623 			return err;
624 	}
625 	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
626 		err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
627 		if (err < 0)
628 			return err;
629 	}
630 	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
631 		snd_ctl_elem_value_t ctl = {0};
632 		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
633 		if ((err = snd_hctl_elem_read(c->elem, &ctl)) < 0)
634 			return err;
635 		for (idx = 0; idx < c->values; idx++) {
636 			if (s->str[SM_CAPT].sw & (1 << idx))
637 				snd_ctl_elem_value_set_enumerated(&ctl,
638 							idx, s->capture_item);
639 		}
640 		if ((err = snd_hctl_elem_write(c->elem, &ctl)) < 0)
641 			return err;
642 		/* update the element, don't remove */
643 		err = selem_read(elem);
644 		if (err < 0)
645 			return err;
646 	}
647 	return 0;
648 }
649 
selem_write(snd_mixer_elem_t * elem)650 static int selem_write(snd_mixer_elem_t *elem)
651 {
652 	int err;
653 
654 	err = selem_write_main(elem);
655 	if (err < 0)
656 		selem_read(elem);
657 	return err;
658 }
659 
selem_free(snd_mixer_elem_t * elem)660 static void selem_free(snd_mixer_elem_t *elem)
661 {
662 	selem_none_t *simple = snd_mixer_elem_get_private(elem);
663 	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
664 	if (simple->selem.id)
665 		snd_mixer_selem_id_free(simple->selem.id);
666 	/* free db range information */
667 	free(simple->str[0].db_info);
668 	free(simple->str[1].db_info);
669 	free(simple);
670 }
671 
simple_update(snd_mixer_elem_t * melem)672 static int simple_update(snd_mixer_elem_t *melem)
673 {
674 	selem_none_t *simple;
675 	unsigned int caps, pchannels, cchannels;
676 	long pmin, pmax, cmin, cmax;
677 	selem_ctl_t *ctl;
678 
679 	caps = 0;
680 	pchannels = 0;
681 	pmin = LONG_MAX;
682 	pmax = LONG_MIN;
683 	cchannels = 0;
684 	cmin = LONG_MAX;
685 	cmax = LONG_MIN;
686 	assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE);
687 	simple = snd_mixer_elem_get_private(melem);
688 	ctl = &simple->ctls[CTL_SINGLE];
689 	if (ctl->elem) {
690 		pchannels = cchannels = ctl->values;
691 		if (ctl->type == SND_CTL_ELEM_TYPE_INTEGER) {
692 			caps |= SM_CAP_GVOLUME;
693 			pmin = cmin = ctl->min;
694 			pmax = cmax = ctl->max;
695 		} else
696 			caps |= SM_CAP_GSWITCH;
697 	}
698 	ctl = &simple->ctls[CTL_GLOBAL_SWITCH];
699 	if (ctl->elem) {
700 		if (pchannels < ctl->values)
701 			pchannels = ctl->values;
702 		if (cchannels < ctl->values)
703 			cchannels = ctl->values;
704 		caps |= SM_CAP_GSWITCH;
705 	}
706 	ctl = &simple->ctls[CTL_GLOBAL_ROUTE];
707 	if (ctl->elem) {
708 		if (pchannels < ctl->values)
709 			pchannels = ctl->values;
710 		if (cchannels < ctl->values)
711 			cchannels = ctl->values;
712 		caps |= SM_CAP_GSWITCH;
713 	}
714 	ctl = &simple->ctls[CTL_GLOBAL_VOLUME];
715 	if (ctl->elem) {
716 		if (pchannels < ctl->values)
717 			pchannels = ctl->values;
718 		if (pmin > ctl->min)
719 			pmin = ctl->min;
720 		if (pmax < ctl->max)
721 			pmax = ctl->max;
722 		if (cchannels < ctl->values)
723 			cchannels = ctl->values;
724 		if (cmin > ctl->min)
725 			cmin = ctl->min;
726 		if (cmax < ctl->max)
727 			cmax = ctl->max;
728 		caps |= SM_CAP_GVOLUME;
729 	}
730 	ctl = &simple->ctls[CTL_PLAYBACK_SWITCH];
731 	if (ctl->elem) {
732 		if (pchannels < ctl->values)
733 			pchannels = ctl->values;
734 		caps |= SM_CAP_PSWITCH;
735 		caps &= ~SM_CAP_GSWITCH;
736 	}
737 	ctl = &simple->ctls[CTL_PLAYBACK_ROUTE];
738 	if (ctl->elem) {
739 		if (pchannels < ctl->values)
740 			pchannels = ctl->values;
741 		caps |= SM_CAP_PSWITCH;
742 		caps &= ~SM_CAP_GSWITCH;
743 	}
744 	ctl = &simple->ctls[CTL_CAPTURE_SWITCH];
745 	if (ctl->elem) {
746 		if (cchannels < ctl->values)
747 			cchannels = ctl->values;
748 		caps |= SM_CAP_CSWITCH;
749 		caps &= ~SM_CAP_GSWITCH;
750 	}
751 	ctl = &simple->ctls[CTL_CAPTURE_ROUTE];
752 	if (ctl->elem) {
753 		if (cchannels < ctl->values)
754 			cchannels = ctl->values;
755 		caps |= SM_CAP_CSWITCH;
756 		caps &= ~SM_CAP_GSWITCH;
757 	}
758 	ctl = &simple->ctls[CTL_PLAYBACK_VOLUME];
759 	if (ctl->elem) {
760 		if (pchannels < ctl->values)
761 			pchannels = ctl->values;
762 		if (pmin > ctl->min)
763 			pmin = ctl->min;
764 		if (pmax < ctl->max)
765 			pmax = ctl->max;
766 		caps |= SM_CAP_PVOLUME;
767 		caps &= ~SM_CAP_GVOLUME;
768 	}
769 	ctl = &simple->ctls[CTL_CAPTURE_VOLUME];
770 	if (ctl->elem) {
771 		if (cchannels < ctl->values)
772 			cchannels = ctl->values;
773 		if (cmin > ctl->min)
774 			cmin = ctl->min;
775 		if (cmax < ctl->max)
776 			cmax = ctl->max;
777 		caps |= SM_CAP_CVOLUME;
778 		caps &= ~SM_CAP_GVOLUME;
779 	}
780 	ctl = &simple->ctls[CTL_CAPTURE_SOURCE];
781 	if (ctl->elem) {
782 		if (cchannels < ctl->values)
783 			cchannels = ctl->values;
784 		caps |= SM_CAP_CSWITCH | SM_CAP_CSWITCH_EXCL;
785 		caps &= ~SM_CAP_GSWITCH;
786 	}
787 	ctl = &simple->ctls[CTL_GLOBAL_ENUM];
788 	if (ctl->elem) {
789 		if (pchannels < ctl->values)
790 			pchannels = ctl->values;
791 		caps |= SM_CAP_PENUM | SM_CAP_CENUM;
792 	}
793 	ctl = &simple->ctls[CTL_PLAYBACK_ENUM];
794 	if (ctl->elem) {
795 		if (pchannels < ctl->values)
796 			pchannels = ctl->values;
797 		caps |= SM_CAP_PENUM;
798 	}
799 	ctl = &simple->ctls[CTL_CAPTURE_ENUM];
800 	if (ctl->elem) {
801 		if (pchannels < ctl->values)
802 			pchannels = ctl->values;
803 		caps |= SM_CAP_CENUM;
804 	}
805 	if (pchannels > 32)
806 		pchannels = 32;
807 	if (cchannels > 32)
808 		cchannels = 32;
809 	if (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))
810 		caps |= SM_CAP_PSWITCH_JOIN;
811 	if (caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME))
812 		caps |= SM_CAP_PVOLUME_JOIN;
813 	if (caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))
814 		caps |= SM_CAP_CSWITCH_JOIN;
815 	if (caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME))
816 		caps |= SM_CAP_CVOLUME_JOIN;
817 	if (pchannels > 1 || cchannels > 1) {
818 		if (simple->ctls[CTL_SINGLE].elem &&
819 		    simple->ctls[CTL_SINGLE].values > 1) {
820 			if (caps & SM_CAP_GSWITCH)
821 				caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
822 			else
823 				caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
824 		}
825 		if (simple->ctls[CTL_GLOBAL_ROUTE].elem ||
826 		    (simple->ctls[CTL_GLOBAL_SWITCH].elem &&
827 		     simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) {
828 			caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
829 		}
830 		if (simple->ctls[CTL_GLOBAL_VOLUME].elem &&
831 		    simple->ctls[CTL_GLOBAL_VOLUME].values > 1) {
832 			caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
833 		}
834 	}
835 	if (pchannels > 1) {
836 		if (simple->ctls[CTL_PLAYBACK_ROUTE].elem ||
837 		    (simple->ctls[CTL_PLAYBACK_SWITCH].elem &&
838 		     simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) {
839 			caps &= ~SM_CAP_PSWITCH_JOIN;
840 		}
841 		if (simple->ctls[CTL_PLAYBACK_VOLUME].elem &&
842 		    simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) {
843 			caps &= ~SM_CAP_PVOLUME_JOIN;
844 		}
845 	}
846 	if (cchannels > 1) {
847 		if (simple->ctls[CTL_CAPTURE_ROUTE].elem ||
848 		    (simple->ctls[CTL_CAPTURE_SWITCH].elem &&
849 		     simple->ctls[CTL_CAPTURE_SWITCH].values > 1) ||
850 		    (simple->ctls[CTL_CAPTURE_SOURCE].elem &&
851 		     simple->ctls[CTL_CAPTURE_SOURCE].values > 1)) {
852 			caps &= ~SM_CAP_CSWITCH_JOIN;
853 		}
854 		if (simple->ctls[CTL_CAPTURE_VOLUME].elem &&
855 		    simple->ctls[CTL_CAPTURE_VOLUME].values > 1) {
856 			caps &= ~SM_CAP_CVOLUME_JOIN;
857 		}
858 	}
859 
860 	/* exceptions */
861 	if ((caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) &&
862 	    (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == (caps & SM_CAP_GSWITCH)) {
863 		caps &= ~(SM_CAP_GSWITCH|SM_CAP_CSWITCH_JOIN|SM_CAP_CSWITCH_EXCL);
864 		caps |= SM_CAP_PSWITCH;
865 	}
866 
867 	if ((caps & SM_CAP_GSWITCH) &&
868 	    (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
869 		caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;
870 
871 	if ((caps & SM_CAP_GVOLUME) &&
872 	    (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
873 		caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;
874 
875 	simple->selem.caps = caps;
876 	simple->str[SM_PLAY].channels = pchannels;
877 	if (!simple->str[SM_PLAY].range) {
878 		simple->str[SM_PLAY].min = pmin != LONG_MAX ? pmin : 0;
879 		simple->str[SM_PLAY].max = pmax != LONG_MIN ? pmax : 0;
880 	}
881 	simple->str[SM_CAPT].channels = cchannels;
882 	if (!simple->str[SM_CAPT].range) {
883 		simple->str[SM_CAPT].min = cmin != LONG_MAX ? cmin : 0;
884 		simple->str[SM_CAPT].max = cmax != LONG_MIN ? cmax : 0;
885 	}
886 	return 0;
887 }
888 
889 #ifndef DOC_HIDDEN
890 static const struct suf {
891 	const char *suffix;
892 	selem_ctl_type_t type;
893 } suffixes[] = {
894 	{" Playback Enum", CTL_PLAYBACK_ENUM},
895 	{" Playback Switch", CTL_PLAYBACK_SWITCH},
896 	{" Playback Route", CTL_PLAYBACK_ROUTE},
897 	{" Playback Volume", CTL_PLAYBACK_VOLUME},
898 	{" Capture Enum", CTL_CAPTURE_ENUM},
899 	{" Capture Switch", CTL_CAPTURE_SWITCH},
900 	{" Capture Route", CTL_CAPTURE_ROUTE},
901 	{" Capture Volume", CTL_CAPTURE_VOLUME},
902 	{" Enum", CTL_GLOBAL_ENUM},
903 	{" Switch", CTL_GLOBAL_SWITCH},
904 	{" Route", CTL_GLOBAL_ROUTE},
905 	{" Volume", CTL_GLOBAL_VOLUME},
906 	{NULL, 0}
907 };
908 #endif
909 
910 /* Return base length */
base_len(const char * name,selem_ctl_type_t * type)911 static int base_len(const char *name, selem_ctl_type_t *type)
912 {
913 	const struct suf *p;
914 	size_t nlen = strlen(name);
915 
916 	/* exception: "Capture Volume" and "Capture Switch" */
917 	if (!strcmp(name, "Capture Volume")) {
918 		*type = CTL_CAPTURE_VOLUME;
919 		return strlen("Capture");
920 	}
921 	if (!strcmp(name, "Capture Switch")) {
922 		*type = CTL_CAPTURE_SWITCH;
923 		return strlen("Capture");
924 	}
925 
926 	for (p = suffixes; p->suffix; p++) {
927 		size_t slen = strlen(p->suffix);
928 		size_t l;
929 		if (nlen > slen) {
930 			l = nlen - slen;
931 			if (strncmp(name + l, p->suffix, slen) == 0 &&
932 			    (l < 1 || name[l-1] != '-')) {	/* 3D Control - Switch */
933 				*type = p->type;
934 				return l;
935 			}
936 		}
937 	}
938 
939 	/* Special case - handle "Input Source" as a capture route.
940 	 * Note that it's *NO* capture source.  A capture source is split over
941 	 * sub-elements, and multiple capture-sources will result in an error.
942 	 * That's why some drivers use "Input Source" as a workaround.
943 	 * Hence, this is a workaround for a workaround to get the things
944 	 * straight back again.  Sigh.
945 	 */
946 	if (!strcmp(name, "Input Source")) {
947 		*type = CTL_CAPTURE_ROUTE;
948 		return strlen(name);
949 	}
950 	if (strstr(name, "3D Control")) {
951 		if (strstr(name, "Depth")) {
952 			*type = CTL_PLAYBACK_VOLUME;
953 			return strlen(name);
954 		}
955 	}
956 
957 	*type = CTL_SINGLE;
958 	return strlen(name);
959 }
960 
961 
962 /*
963  * Simple Mixer Operations
964  */
965 
_snd_mixer_selem_set_volume(snd_mixer_elem_t * elem,int dir,snd_mixer_selem_channel_id_t channel,long value)966 static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value)
967 {
968 	selem_none_t *s = snd_mixer_elem_get_private(elem);
969 	if (s->selem.caps & SM_CAP_GVOLUME)
970 		dir = SM_PLAY;
971 	if ((unsigned int) channel >= s->str[dir].channels)
972 		return 0;
973 	if (value < s->str[dir].min || value > s->str[dir].max)
974 		return 0;
975 	if (s->selem.caps &
976 	    (dir == SM_PLAY ? SM_CAP_PVOLUME_JOIN : SM_CAP_CVOLUME_JOIN))
977 		channel = 0;
978 	if (value != s->str[dir].vol[channel]) {
979 		s->str[dir].vol[channel] = value;
980 		return 1;
981 	}
982 	return 0;
983 }
984 
_snd_mixer_selem_set_switch(snd_mixer_elem_t * elem,int dir,snd_mixer_selem_channel_id_t channel,int value)985 static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value)
986 {
987 	selem_none_t *s = snd_mixer_elem_get_private(elem);
988 	if ((unsigned int) channel >= s->str[dir].channels)
989 		return 0;
990 	if (s->selem.caps &
991 	    (dir == SM_PLAY ? SM_CAP_PSWITCH_JOIN : SM_CAP_CSWITCH_JOIN))
992 		channel = 0;
993 	if (value) {
994 		if (!(s->str[dir].sw & (1 << channel))) {
995 			s->str[dir].sw |= 1 << channel;
996 			return 1;
997 		}
998 	} else {
999 		if (s->str[dir].sw & (1 << channel)) {
1000 			s->str[dir].sw &= ~(1 << channel);
1001 			return 1;
1002 		}
1003 	}
1004 	return 0;
1005 }
1006 
is_ops(snd_mixer_elem_t * elem,int dir,int cmd,int val)1007 static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
1008 {
1009 	selem_none_t *s = snd_mixer_elem_get_private(elem);
1010 
1011 	switch (cmd) {
1012 
1013 	case SM_OPS_IS_ACTIVE: {
1014 		selem_ctl_type_t ctl;
1015 		for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++)
1016 			if (s->ctls[ctl].elem != NULL && s->ctls[ctl].inactive)
1017 				return 0;
1018 		return 1;
1019 	}
1020 
1021 	case SM_OPS_IS_MONO:
1022 		return s->str[dir].channels == 1;
1023 
1024 	case SM_OPS_IS_CHANNEL:
1025 		return (unsigned int) val < s->str[dir].channels;
1026 
1027 	case SM_OPS_IS_ENUMERATED:
1028 		if (val == 1) {
1029 			if (dir == SM_PLAY && (s->selem.caps & SM_CAP_PENUM) && !(s->selem.caps & SM_CAP_CENUM) )
1030 				return 1;
1031 			if (dir == SM_CAPT && (s->selem.caps & SM_CAP_CENUM) && !(s->selem.caps & SM_CAP_PENUM) )
1032 				return 1;
1033 			return 0;
1034 		}
1035 		if (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) )
1036 			return 1;
1037 		return 0;
1038 
1039 	case SM_OPS_IS_ENUMCNT:
1040 		/* Both */
1041 		if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) ) {
1042 			if (! s->ctls[CTL_GLOBAL_ENUM].elem)
1043 				return -EINVAL;
1044 			return s->ctls[CTL_GLOBAL_ENUM].max;
1045 		/* Only Playback */
1046 		} else if (s->selem.caps & SM_CAP_PENUM ) {
1047 			if (! s->ctls[CTL_PLAYBACK_ENUM].elem)
1048 				return -EINVAL;
1049 			return s->ctls[CTL_PLAYBACK_ENUM].max;
1050 		/* Only Capture */
1051 		} else if (s->selem.caps & SM_CAP_CENUM ) {
1052 			if (! s->ctls[CTL_CAPTURE_ENUM].elem)
1053 				return -EINVAL;
1054 			return s->ctls[CTL_CAPTURE_ENUM].max;
1055 		}
1056 
1057 	}
1058 
1059 	return 1;
1060 }
1061 
get_range_ops(snd_mixer_elem_t * elem,int dir,long * min,long * max)1062 static int get_range_ops(snd_mixer_elem_t *elem, int dir,
1063 			 long *min, long *max)
1064 {
1065 	selem_none_t *s = snd_mixer_elem_get_private(elem);
1066 	*min = s->str[dir].min;
1067 	*max = s->str[dir].max;
1068 	return 0;
1069 }
1070 
set_range_ops(snd_mixer_elem_t * elem,int dir,long min,long max)1071 static int set_range_ops(snd_mixer_elem_t *elem, int dir,
1072 			 long min, long max)
1073 {
1074 	selem_none_t *s = snd_mixer_elem_get_private(elem);
1075 	int err;
1076 
1077 	s->str[dir].range = 1;
1078 	s->str[dir].min = min;
1079 	s->str[dir].max = max;
1080 	if ((err = selem_read(elem)) < 0)
1081 		return err;
1082 	return 0;
1083 }
1084 
get_volume_ops(snd_mixer_elem_t * elem,int dir,snd_mixer_selem_channel_id_t channel,long * value)1085 static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
1086 			  snd_mixer_selem_channel_id_t channel, long *value)
1087 {
1088 	selem_none_t *s = snd_mixer_elem_get_private(elem);
1089 	if (s->selem.caps & SM_CAP_GVOLUME)
1090 		dir = SM_PLAY;
1091 	if ((unsigned int) channel >= s->str[dir].channels)
1092 		return -EINVAL;
1093 	*value = s->str[dir].vol[channel];
1094 	return 0;
1095 }
1096 
1097 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec);
1098 
convert_to_dB(snd_hctl_elem_t * ctl,struct selem_str * rec,long volume,long * db_gain)1099 static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
1100 			 long volume, long *db_gain)
1101 {
1102 	if (init_db_range(ctl, rec) < 0)
1103 		return -EINVAL;
1104 	return snd_tlv_convert_to_dB(rec->db_info, rec->min, rec->max,
1105 				     volume, db_gain);
1106 }
1107 
1108 /* initialize dB range information, reading TLV via hcontrol
1109  */
init_db_range(snd_hctl_elem_t * ctl,struct selem_str * rec)1110 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec)
1111 {
1112 	snd_ctl_elem_info_t info = {0};
1113 	unsigned int *tlv = NULL;
1114 	const unsigned int tlv_size = 4096;
1115 	unsigned int *dbrec;
1116 	int db_size;
1117 
1118 	if (rec->db_init_error)
1119 		return -EINVAL;
1120 	if (rec->db_initialized)
1121 		return 0;
1122 
1123 	if (snd_hctl_elem_info(ctl, &info) < 0)
1124 		goto error;
1125 	if (!snd_ctl_elem_info_is_tlv_readable(&info))
1126 		goto error;
1127 	tlv = malloc(tlv_size);
1128 	if (!tlv)
1129 		return -ENOMEM;
1130 	if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0)
1131 		goto error;
1132 	db_size = snd_tlv_parse_dB_info(tlv, tlv_size, &dbrec);
1133 	if (db_size < 0)
1134 		goto error;
1135 	rec->db_info = malloc(db_size);
1136 	if (!rec->db_info)
1137 		goto error;
1138 	memcpy(rec->db_info, dbrec, db_size);
1139 	free(tlv);
1140 	rec->db_initialized = 1;
1141 	return 0;
1142 
1143  error:
1144 	free(tlv);
1145 	rec->db_init_error = 1;
1146 	return -EINVAL;
1147 }
1148 
1149 /* get selem_ctl for TLV access */
get_selem_ctl(selem_none_t * s,int dir)1150 static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir)
1151 {
1152 	selem_ctl_t *c;
1153 	if (dir == SM_PLAY)
1154 		c = &s->ctls[CTL_PLAYBACK_VOLUME];
1155 	else if (dir == SM_CAPT)
1156 		c = &s->ctls[CTL_CAPTURE_VOLUME];
1157 	else
1158 		return NULL;
1159 	if (! c->elem) {
1160 		c = &s->ctls[CTL_GLOBAL_VOLUME];
1161 		if (! c->elem)
1162 			return NULL;
1163 	}
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