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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
29 *
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <string.h>
36#include <fcntl.h>
37#include <sys/ioctl.h>
38#include <assert.h>
39#include <math.h>
40#include <limits.h>
41#include "asoundlib.h"
42#include "mixer_simple.h"
43#include "config.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
51typedef 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
69typedef 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
77typedef 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
93static 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
108static 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
118static 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
133static 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
217static 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
226static 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
235static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
236{
237	snd_ctl_elem_value_t *ctl;
238	unsigned int idx;
239	int err;
240	selem_ctl_t *c = &s->ctls[type];
241	snd_ctl_elem_value_alloca(&ctl);
242	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
243		return err;
244	for (idx = 0; idx < s->str[dir].channels; idx++) {
245		unsigned int idx1 = idx;
246		if (idx >= c->values)
247			idx1 = 0;
248		s->str[dir].vol[idx] = to_user(s, dir, c, snd_ctl_elem_value_get_integer(ctl, idx1));
249	}
250	return 0;
251}
252
253static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
254{
255	snd_ctl_elem_value_t *ctl;
256	unsigned int idx;
257	int err;
258	selem_ctl_t *c = &s->ctls[type];
259	snd_ctl_elem_value_alloca(&ctl);
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
272static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type)
273{
274	snd_ctl_elem_value_t *ctl;
275	unsigned int idx;
276	int err;
277	selem_ctl_t *c = &s->ctls[type];
278	snd_ctl_elem_value_alloca(&ctl);
279	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
280		return err;
281	for (idx = 0; idx < s->str[dir].channels; idx++) {
282		unsigned int idx1 = idx;
283		if (idx >= c->values)
284			idx1 = 0;
285		if (!snd_ctl_elem_value_get_integer(ctl, idx1 * c->values + idx1))
286			s->str[dir].sw &= ~(1 << idx);
287	}
288	return 0;
289}
290
291static int elem_read_enum(selem_none_t *s)
292{
293	snd_ctl_elem_value_t *ctl;
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)) == (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	snd_ctl_elem_value_alloca(&ctl);
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] = snd_ctl_elem_value_get_enumerated(ctl, idx1);
314	}
315	return 0;
316}
317
318static 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;
444		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
445		snd_ctl_elem_value_alloca(&ctl);
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) != 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
467static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
468{
469	snd_ctl_elem_value_t *ctl;
470	unsigned int idx;
471	int err;
472	selem_ctl_t *c = &s->ctls[type];
473	snd_ctl_elem_value_alloca(&ctl);
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, 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
483static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
484{
485	snd_ctl_elem_value_t *ctl;
486	unsigned int idx;
487	int err;
488	selem_ctl_t *c = &s->ctls[type];
489	snd_ctl_elem_value_alloca(&ctl);
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, !!(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
499static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val)
500{
501	snd_ctl_elem_value_t *ctl;
502	unsigned int idx;
503	int err;
504	selem_ctl_t *c = &s->ctls[type];
505	snd_ctl_elem_value_alloca(&ctl);
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
515static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type)
516{
517	snd_ctl_elem_value_t *ctl;
518	unsigned int idx;
519	int err;
520	selem_ctl_t *c = &s->ctls[type];
521	snd_ctl_elem_value_alloca(&ctl);
522	if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
523		return err;
524	for (idx = 0; idx < c->values * c->values; idx++)
525		snd_ctl_elem_value_set_integer(ctl, idx, 0);
526	for (idx = 0; idx < c->values; idx++)
527		snd_ctl_elem_value_set_integer(ctl, idx * c->values + idx, !!(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
533static int elem_write_enum(selem_none_t *s)
534{
535	snd_ctl_elem_value_t *ctl;
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) ) == (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	snd_ctl_elem_value_alloca(&ctl);
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, (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
558static 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 && s->ctls[CTL_CAPTURE_SWITCH].elem)
591			err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH, 1);
592		else
593			err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
594		if (err < 0)
595			return err;
596	}
597	if (s->ctls[CTL_PLAYBACK_VOLUME].elem) {
598		err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
599		if (err < 0)
600			return err;
601	}
602	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
603		err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
604		if (err < 0)
605			return err;
606	}
607	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
608		err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
609		if (err < 0)
610			return err;
611	}
612	if (s->ctls[CTL_CAPTURE_VOLUME].elem) {
613		err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
614		if (err < 0)
615			return err;
616	}
617	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
618		err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
619		if (err < 0)
620			return err;
621	}
622	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
623		err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
624		if (err < 0)
625			return err;
626	}
627	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
628		snd_ctl_elem_value_t *ctl;
629		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
630		snd_ctl_elem_value_alloca(&ctl);
631		if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
632			return err;
633		for (idx = 0; idx < c->values; idx++) {
634			if (s->str[SM_CAPT].sw & (1 << idx))
635				snd_ctl_elem_value_set_enumerated(ctl, idx, s->capture_item);
636		}
637		if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
638			return err;
639		/* update the element, don't remove */
640		err = selem_read(elem);
641		if (err < 0)
642			return err;
643	}
644	return 0;
645}
646
647static int selem_write(snd_mixer_elem_t *elem)
648{
649	int err;
650
651	err = selem_write_main(elem);
652	if (err < 0)
653		selem_read(elem);
654	return err;
655}
656
657static void selem_free(snd_mixer_elem_t *elem)
658{
659	selem_none_t *simple = snd_mixer_elem_get_private(elem);
660	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
661	if (simple->selem.id)
662		snd_mixer_selem_id_free(simple->selem.id);
663	/* free db range information */
664	free(simple->str[0].db_info);
665	free(simple->str[1].db_info);
666	free(simple);
667}
668
669static int simple_update(snd_mixer_elem_t *melem)
670{
671	selem_none_t *simple;
672	unsigned int caps, pchannels, cchannels;
673	long pmin, pmax, cmin, cmax;
674	selem_ctl_t *ctl;
675	const char *name;
676
677	caps = 0;
678	pchannels = 0;
679	pmin = LONG_MAX;
680	pmax = LONG_MIN;
681	cchannels = 0;
682	cmin = LONG_MAX;
683	cmax = LONG_MIN;
684	assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE);
685	simple = snd_mixer_elem_get_private(melem);
686	name = snd_mixer_selem_get_name(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
889static 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 or 0 on failure */
910static int base_len(const char *name, selem_ctl_type_t *type)
911{
912	const struct suf *p;
913	size_t nlen = strlen(name);
914	p = suffixes;
915	while (p->suffix) {
916		size_t slen = strlen(p->suffix);
917		size_t l;
918		if (nlen > slen) {
919			l = nlen - slen;
920			if (strncmp(name + l, p->suffix, slen) == 0 &&
921			    (l < 1 || name[l-1] != '-')) {	/* 3D Control - Switch */
922				*type = p->type;
923				return l;
924			}
925		}
926		p++;
927	}
928
929	/* Special case - handle "Input Source" as a capture route.
930	 * Note that it's *NO* capture source.  A capture source is split over
931	 * sub-elements, and multiple capture-sources will result in an error.
932	 * That's why some drivers use "Input Source" as a workaround.
933	 * Hence, this is a workaround for a workaround to get the things
934	 * straight back again.  Sigh.
935	 */
936	if (!strcmp(name, "Input Source")) {
937		*type = CTL_CAPTURE_ROUTE;
938		return strlen(name);
939	}
940	if (strstr(name, "3D Control")) {
941		if (strstr(name, "Depth")) {
942			*type = CTL_PLAYBACK_VOLUME;
943			return strlen(name);
944		}
945	}
946	return 0;
947}
948
949
950/*
951 * Simple Mixer Operations
952 */
953
954static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value)
955{
956	selem_none_t *s = snd_mixer_elem_get_private(elem);
957	if (s->selem.caps & SM_CAP_GVOLUME)
958		dir = SM_PLAY;
959	if ((unsigned int) channel >= s->str[dir].channels)
960		return 0;
961	if (value < s->str[dir].min || value > s->str[dir].max)
962		return 0;
963	if (s->selem.caps &
964	    (dir == SM_PLAY ? SM_CAP_PVOLUME_JOIN : SM_CAP_CVOLUME_JOIN))
965		channel = 0;
966	if (value != s->str[dir].vol[channel]) {
967		s->str[dir].vol[channel] = value;
968		return 1;
969	}
970	return 0;
971}
972
973static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value)
974{
975	selem_none_t *s = snd_mixer_elem_get_private(elem);
976	if ((unsigned int) channel >= s->str[dir].channels)
977		return 0;
978	if (s->selem.caps &
979	    (dir == SM_PLAY ? SM_CAP_PSWITCH_JOIN : SM_CAP_CSWITCH_JOIN))
980		channel = 0;
981	if (value) {
982		if (!(s->str[dir].sw & (1 << channel))) {
983			s->str[dir].sw |= 1 << channel;
984			return 1;
985		}
986	} else {
987		if (s->str[dir].sw & (1 << channel)) {
988			s->str[dir].sw &= ~(1 << channel);
989			return 1;
990		}
991	}
992	return 0;
993}
994
995static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
996{
997	selem_none_t *s = snd_mixer_elem_get_private(elem);
998
999	switch (cmd) {
1000
1001	case SM_OPS_IS_ACTIVE: {
1002		selem_ctl_type_t ctl;
1003		for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++)
1004			if (s->ctls[ctl].elem != NULL && s->ctls[ctl].inactive)
1005				return 0;
1006		return 1;
1007	}
1008
1009	case SM_OPS_IS_MONO:
1010		return s->str[dir].channels == 1;
1011
1012	case SM_OPS_IS_CHANNEL:
1013		return (unsigned int) val < s->str[dir].channels;
1014
1015	case SM_OPS_IS_ENUMERATED:
1016		if (val == 1) {
1017			if (dir == SM_PLAY && (s->selem.caps & SM_CAP_PENUM) && !(s->selem.caps & SM_CAP_CENUM) )
1018				return 1;
1019			if (dir == SM_CAPT && (s->selem.caps & SM_CAP_CENUM) && !(s->selem.caps & SM_CAP_PENUM) )
1020				return 1;
1021			return 0;
1022		}
1023		if (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) )
1024			return 1;
1025		return 0;
1026
1027	case SM_OPS_IS_ENUMCNT:
1028		/* Both */
1029		if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) ) {
1030			if (! s->ctls[CTL_GLOBAL_ENUM].elem)
1031				return -EINVAL;
1032			return s->ctls[CTL_GLOBAL_ENUM].max;
1033		/* Only Playback */
1034		} else if (s->selem.caps & SM_CAP_PENUM ) {
1035			if (! s->ctls[CTL_PLAYBACK_ENUM].elem)
1036				return -EINVAL;
1037			return s->ctls[CTL_PLAYBACK_ENUM].max;
1038		/* Only Capture */
1039		} else if (s->selem.caps & SM_CAP_CENUM ) {
1040			if (! s->ctls[CTL_CAPTURE_ENUM].elem)
1041				return -EINVAL;
1042			return s->ctls[CTL_CAPTURE_ENUM].max;
1043		}
1044
1045	}
1046
1047	return 1;
1048}
1049
1050static int get_range_ops(snd_mixer_elem_t *elem, int dir,
1051			 long *min, long *max)
1052{
1053	selem_none_t *s = snd_mixer_elem_get_private(elem);
1054	*min = s->str[dir].min;
1055	*max = s->str[dir].max;
1056	return 0;
1057}
1058
1059static int set_range_ops(snd_mixer_elem_t *elem, int dir,
1060			 long min, long max)
1061{
1062	selem_none_t *s = snd_mixer_elem_get_private(elem);
1063	int err;
1064
1065	s->str[dir].range = 1;
1066	s->str[dir].min = min;
1067	s->str[dir].max = max;
1068	if ((err = selem_read(elem)) < 0)
1069		return err;
1070	return 0;
1071}
1072
1073static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
1074			  snd_mixer_selem_channel_id_t channel, long *value)
1075{
1076	selem_none_t *s = snd_mixer_elem_get_private(elem);
1077	if (s->selem.caps & SM_CAP_GVOLUME)
1078		dir = SM_PLAY;
1079	if ((unsigned int) channel >= s->str[dir].channels)
1080		return -EINVAL;
1081	*value = s->str[dir].vol[channel];
1082	return 0;
1083}
1084
1085static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec);
1086
1087static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
1088			 long volume, long *db_gain)
1089{
1090	if (init_db_range(ctl, rec) < 0)
1091		return -EINVAL;
1092	return snd_tlv_convert_to_dB(rec->db_info, rec->min, rec->max,
1093				     volume, db_gain);
1094}
1095
1096/* initialize dB range information, reading TLV via hcontrol
1097 */
1098static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec)
1099{
1100	snd_ctl_elem_info_t *info;
1101	unsigned int *tlv = NULL;
1102	const unsigned int tlv_size = 4096;
1103	unsigned int *dbrec;
1104	int db_size;
1105
1106	if (rec->db_init_error)
1107		return -EINVAL;
1108	if (rec->db_initialized)
1109		return 0;
1110
1111	snd_ctl_elem_info_alloca(&info);
1112	if (snd_hctl_elem_info(ctl, info) < 0)
1113		goto error;
1114	if (! snd_ctl_elem_info_is_tlv_readable(info))
1115		goto error;
1116	tlv = malloc(tlv_size);
1117	if (! tlv)
1118		return -ENOMEM;
1119	if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0)
1120		goto error;
1121	db_size = snd_tlv_parse_dB_info(tlv, tlv_size, &dbrec);
1122	if (db_size < 0)
1123		goto error;
1124	rec->db_info = malloc(db_size);
1125	if (!rec->db_info)
1126		goto error;
1127	memcpy(rec->db_info, dbrec, db_size);
1128	free(tlv);
1129	rec->db_initialized = 1;
1130	return 0;
1131
1132 error:
1133	free(tlv);
1134	rec->db_init_error = 1;
1135	return -EINVAL;
1136}
1137
1138/* get selem_ctl for TLV access */
1139static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir)
1140{
1141	selem_ctl_t *c;
1142	if (dir == SM_PLAY)
1143		c = &s->ctls[CTL_PLAYBACK_VOLUME];
1144	else if (dir == SM_CAPT)
1145		c = &s->ctls[CTL_CAPTURE_VOLUME];
1146	else
1147		return NULL;
1148	if (! c->elem) {
1149		c = &s->ctls[CTL_GLOBAL_VOLUME];
1150		if (! c->elem)
1151			return NULL;
1152	}
1153	if (c->type != SND_CTL_ELEM_TYPE_INTEGER)
1154		return NULL;
1155	return c;
1156}
1157
1158static int get_dB_range(snd_hctl_elem_t *ctl, struct selem_str *rec,
1159			long *min, long *max)
1160{
1161	if (init_db_range(ctl, rec) < 0)
1162		return -EINVAL;
1163
1164	return snd_tlv_get_dB_range(rec->db_info, rec->min, rec->max, min, max);
1165}
1166
1167static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir,
1168			    long *min, long *max)
1169{
1170	selem_none_t *s = snd_mixer_elem_get_private(elem);
1171	selem_ctl_t *c;
1172
1173	if (s->selem.caps & SM_CAP_GVOLUME)
1174		dir = SM_PLAY;
1175	c = get_selem_ctl(s, dir);
1176	if (! c)
1177		return -EINVAL;
1178	return get_dB_range(c->elem, &s->str[dir], min, max);
1179}
1180
1181static int convert_from_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
1182			   long db_gain, long *value, int xdir)
1183{
1184	if (init_db_range(ctl, rec) < 0)
1185		return -EINVAL;
1186
1187	return snd_tlv_convert_from_dB(rec->db_info, rec->min, rec->max,
1188				       db_gain, value, xdir);
1189}
1190
1191static int ask_vol_dB_ops(snd_mixer_elem_t *elem,
1192			  int dir,
1193			  long value,
1194			  long *dBvalue)
1195{
1196	selem_none_t *s = snd_mixer_elem_get_private(elem);
1197	selem_ctl_t *c;
1198
1199	c = get_selem_ctl(s, dir);
1200	if (! c)
1201		return -EINVAL;
1202	int res = convert_to_dB(c->elem, &s->str[dir], value, dBvalue);
1203	return res;
1204}
1205
1206static int get_dB_ops(snd_mixer_elem_t *elem,
1207                      int dir,
1208                      snd_mixer_selem_channel_id_t channel,
1209                      long *value)
1210{
1211	selem_none_t *s = snd_mixer_elem_get_private(elem);
1212	selem_ctl_t *c;
1213	int err;
1214	long volume, db_gain;
1215
1216	if (s->selem.caps & SM_CAP_GVOLUME)
1217		dir = SM_PLAY;
1218	c = get_selem_ctl(s, dir);
1219	if (! c)
1220		return -EINVAL;
1221	if ((err = get_volume_ops(elem, dir, channel, &volume)) < 0)
1222		goto _err;
1223	if ((err = convert_to_dB(c->elem, &s->str[dir], volume, &db_gain)) < 0)
1224		goto _err;
1225	err = 0;
1226	*value = db_gain;
1227 _err:
1228	return err;
1229}
1230
1231static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
1232			  snd_mixer_selem_channel_id_t channel, int *value)
1233{
1234	selem_none_t *s = snd_mixer_elem_get_private(elem);
1235	if (s->selem.caps & SM_CAP_GSWITCH)
1236		dir = SM_PLAY;
1237	if ((unsigned int) channel >= s->str[dir].channels)
1238		return -EINVAL;
1239	*value = !!(s->str[dir].sw & (1 << channel));
1240	return 0;
1241}
1242
1243static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
1244			  snd_mixer_selem_channel_id_t channel, long value)
1245{
1246	int changed;
1247	changed = _snd_mixer_selem_set_volume(elem, dir, channel, value);
1248	if (changed < 0)
1249		return changed;
1250	if (changed)
1251		return selem_write(elem);
1252	return 0;
1253}
1254
1255static int ask_dB_vol_ops(snd_mixer_elem_t *elem, int dir,
1256		          long dbValue, long *value, int xdir)
1257{
1258	selem_none_t *s = snd_mixer_elem_get_private(elem);
1259	selem_ctl_t *c;
1260
1261	if (s->selem.caps & SM_CAP_GVOLUME)
1262		dir = SM_PLAY;
1263	c = get_selem_ctl(s, dir);
1264	if (! c)
1265		return -EINVAL;
1266	return convert_from_dB(c->elem, &s->str[dir], dbValue, value, xdir);
1267}
1268
1269static int set_dB_ops(snd_mixer_elem_t *elem, int dir,
1270		      snd_mixer_selem_channel_id_t channel,
1271		      long db_gain, int xdir)
1272{
1273	selem_none_t *s = snd_mixer_elem_get_private(elem);
1274	selem_ctl_t *c;
1275	long value;
1276	int err;
1277
1278	if (s->selem.caps & SM_CAP_GVOLUME)
1279		dir = SM_PLAY;
1280	c = get_selem_ctl(s, dir);
1281	if (! c)
1282		return -EINVAL;
1283	err = convert_from_dB(c->elem, &s->str[dir], db_gain, &value, xdir);
1284	if (err < 0)
1285		return err;
1286	return set_volume_ops(elem, dir, channel, value);
1287}
1288
1289static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
1290			  snd_mixer_selem_channel_id_t channel, int value)
1291{
1292	int changed;
1293	selem_none_t *s = snd_mixer_elem_get_private(elem);
1294	if (s->selem.caps & SM_CAP_GSWITCH)
1295		dir = SM_PLAY;
1296	if (dir == SM_PLAY) {
1297		if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)))
1298			return -EINVAL;
1299	} else {
1300		if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)))
1301			return -EINVAL;
1302	}
1303	changed = _snd_mixer_selem_set_switch(elem, dir, channel, value);
1304	if (changed < 0)
1305		return changed;
1306	if (changed)
1307		return selem_write(elem);
1308	return 0;
1309}
1310
1311static int enum_item_name_ops(snd_mixer_elem_t *elem,
1312			      unsigned int item,
1313			      size_t maxlen, char *buf)
1314{
1315	selem_none_t *s = snd_mixer_elem_get_private(elem);
1316	snd_ctl_elem_info_t *info;
1317	snd_hctl_elem_t *helem;
1318	int type;
1319
1320	type = CTL_GLOBAL_ENUM;
1321	helem = s->ctls[type].elem;
1322	if (!helem) {
1323		type = CTL_PLAYBACK_ENUM;
1324		helem = s->ctls[type].elem;
1325	}
1326	if (!helem) {
1327		type = CTL_CAPTURE_ENUM;
1328		helem = s->ctls[type].elem;
1329	}
1330	assert(helem);
1331	if (item >= (unsigned int)s->ctls[type].max)
1332		return -EINVAL;
1333	snd_ctl_elem_info_alloca(&info);
1334	snd_hctl_elem_info(helem, info);
1335	snd_ctl_elem_info_set_item(info, item);
1336	snd_hctl_elem_info(helem, info);
1337	strncpy(buf, snd_ctl_elem_info_get_item_name(info), maxlen);
1338	return 0;
1339}
1340
1341static int get_enum_item_ops(snd_mixer_elem_t *elem,
1342			     snd_mixer_selem_channel_id_t channel,
1343			     unsigned int *itemp)
1344{
1345	selem_none_t *s = snd_mixer_elem_get_private(elem);
1346	snd_ctl_elem_value_t *ctl;
1347	snd_hctl_elem_t *helem;
1348	int err;
1349
1350	if ((unsigned int) channel >= s->str[0].channels)
1351		return -EINVAL;
1352	helem = s->ctls[CTL_GLOBAL_ENUM].elem;
1353	if (!helem) helem = s->ctls[CTL_PLAYBACK_ENUM].elem;
1354	if (!helem) helem = s->ctls[CTL_CAPTURE_ENUM].elem;
1355	assert(helem);
1356	snd_ctl_elem_value_alloca(&ctl);
1357	err = snd_hctl_elem_read(helem, ctl);
1358	if (! err)
1359		*itemp = snd_ctl_elem_value_get_enumerated(ctl, channel);
1360	return err;
1361}
1362
1363static int set_enum_item_ops(snd_mixer_elem_t *elem,
1364			     snd_mixer_selem_channel_id_t channel,
1365			     unsigned int item)
1366{
1367	selem_none_t *s = snd_mixer_elem_get_private(elem);
1368	snd_ctl_elem_value_t *ctl;
1369	snd_hctl_elem_t *helem;
1370	int err;
1371	int type;
1372
1373	if ((unsigned int) channel >= s->str[0].channels) {
1374		return -EINVAL;
1375	}
1376	type = CTL_GLOBAL_ENUM;
1377	helem = s->ctls[type].elem;
1378	if (!helem) {
1379		type = CTL_PLAYBACK_ENUM;
1380		helem = s->ctls[type].elem;
1381	}
1382	if (!helem) {
1383		type = CTL_CAPTURE_ENUM;
1384		helem = s->ctls[type].elem;
1385	}
1386	assert(helem);
1387	if (item >= (unsigned int)s->ctls[type].max) {
1388		return -EINVAL;
1389	}
1390	snd_ctl_elem_value_alloca(&ctl);
1391	err = snd_hctl_elem_read(helem, ctl);
1392	if (err < 0) {
1393		return err;
1394	}
1395	snd_ctl_elem_value_set_enumerated(ctl, channel, item);
1396	return snd_hctl_elem_write(helem, ctl);
1397}
1398
1399static struct sm_elem_ops simple_none_ops = {
1400	.is		= is_ops,
1401	.get_range	= get_range_ops,
1402	.get_dB_range	= get_dB_range_ops,
1403	.set_range	= set_range_ops,
1404	.ask_vol_dB	= ask_vol_dB_ops,
1405	.ask_dB_vol	= ask_dB_vol_ops,
1406	.get_volume	= get_volume_ops,
1407	.get_dB		= get_dB_ops,
1408	.set_volume	= set_volume_ops,
1409	.set_dB		= set_dB_ops,
1410	.get_switch	= get_switch_ops,
1411	.set_switch	= set_switch_ops,
1412	.enum_item_name	= enum_item_name_ops,
1413	.get_enum_item	= get_enum_item_ops,
1414	.set_enum_item	= set_enum_item_ops
1415};
1416
1417static int simple_add1(snd_mixer_class_t *class, const char *name,
1418		       snd_hctl_elem_t *helem, selem_ctl_type_t type,
1419		       unsigned int value)
1420{
1421	snd_mixer_elem_t *melem;
1422	snd_mixer_selem_id_t *id;
1423	int new = 0;
1424	int err;
1425	snd_ctl_elem_info_t *info;
1426	selem_none_t *simple;
1427	const char *name1;
1428	snd_ctl_elem_type_t ctype;
1429	unsigned long values;
1430
1431	snd_ctl_elem_info_alloca(&info);
1432	err = snd_hctl_elem_info(helem, info);
1433	if (err < 0)
1434		return err;
1435	ctype = snd_ctl_elem_info_get_type(info);
1436	values = snd_ctl_elem_info_get_count(info);
1437	switch (type) {
1438	case CTL_SINGLE:
1439		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED)
1440			type = CTL_GLOBAL_ENUM;
1441		else if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN &&
1442		         ctype != SND_CTL_ELEM_TYPE_INTEGER)
1443			return 0;
1444		break;
1445	case CTL_GLOBAL_ROUTE:
1446	case CTL_PLAYBACK_ROUTE:
1447	case CTL_CAPTURE_ROUTE:
1448	{
1449		unsigned int n;
1450		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
1451			if (type == CTL_PLAYBACK_ROUTE)
1452				type = CTL_PLAYBACK_ENUM;
1453			else if (type == CTL_CAPTURE_ROUTE)
1454				type = CTL_CAPTURE_ENUM;
1455			else
1456				type = CTL_GLOBAL_ENUM;
1457			break;
1458		}
1459		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
1460			return 0;
1461#ifdef HAVE_SOFT_FLOAT
1462		/* up to 256 channels */
1463		for (n = 1; n < 256; n++)
1464			if (n * n == values)
1465				break;
1466#else
1467		n = sqrt((double)values);
1468#endif
1469		if (n * n != values)
1470			return 0;
1471		values = n;
1472		break;
1473	}
1474	case CTL_GLOBAL_SWITCH:
1475	case CTL_PLAYBACK_SWITCH:
1476	case CTL_CAPTURE_SWITCH:
1477		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
1478			if (type == CTL_PLAYBACK_SWITCH)
1479				type = CTL_PLAYBACK_ENUM;
1480			else if (type == CTL_CAPTURE_SWITCH)
1481				type = CTL_CAPTURE_ENUM;
1482			else
1483				type = CTL_GLOBAL_ENUM;
1484			break;
1485		}
1486		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN)
1487			return 0;
1488		break;
1489	case CTL_GLOBAL_VOLUME:
1490	case CTL_PLAYBACK_VOLUME:
1491	case CTL_CAPTURE_VOLUME:
1492		if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) {
1493			if (type == CTL_PLAYBACK_VOLUME)
1494				type = CTL_PLAYBACK_ENUM;
1495			else if (type == CTL_CAPTURE_VOLUME)
1496				type = CTL_CAPTURE_ENUM;
1497			else
1498				type = CTL_GLOBAL_ENUM;
1499			break;
1500		}
1501		if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
1502			return 0;
1503		break;
1504	case CTL_CAPTURE_SOURCE:
1505		if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED)
1506			return 0;
1507		break;
1508	case CTL_GLOBAL_ENUM:
1509	case CTL_PLAYBACK_ENUM:
1510	case CTL_CAPTURE_ENUM:
1511		if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED)
1512			return 0;
1513		break;
1514	default:
1515		assert(0);
1516		break;
1517	}
1518	name1 = get_short_name(name);
1519	if (snd_mixer_selem_id_malloc(&id))
1520		return -ENOMEM;
1521	snd_mixer_selem_id_set_name(id, name1);
1522	snd_mixer_selem_id_set_index(id, snd_hctl_elem_get_index(helem));
1523	melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
1524	if (!melem) {
1525		simple = calloc(1, sizeof(*simple));
1526		if (!simple) {
1527			snd_mixer_selem_id_free(id);
1528			return -ENOMEM;
1529		}
1530		simple->selem.id = id;
1531		simple->selem.ops = &simple_none_ops;
1532		err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
1533					 get_compare_weight(snd_mixer_selem_id_get_name(simple->selem.id), snd_mixer_selem_id_get_index(simple->selem.id)),
1534					 simple, selem_free);
1535		if (err < 0) {
1536			snd_mixer_selem_id_free(id);
1537			free(simple);
1538			return err;
1539		}
1540		new = 1;
1541	} else {
1542		simple = snd_mixer_elem_get_private(melem);
1543		snd_mixer_selem_id_free(id);
1544	}
1545	if (simple->ctls[type].elem) {
1546		SNDERR("helem (%s,'%s',%u,%u,%u) appears twice or more",
1547				snd_ctl_elem_iface_name(snd_hctl_elem_get_interface(helem)),
1548				snd_hctl_elem_get_name(helem),
1549				snd_hctl_elem_get_index(helem),
1550				snd_hctl_elem_get_device(helem),
1551				snd_hctl_elem_get_subdevice(helem));
1552		err = -EINVAL;
1553		goto __error;
1554	}
1555	simple->ctls[type].elem = helem;
1556	simple->ctls[type].type = snd_ctl_elem_info_get_type(info);
1557	simple->ctls[type].inactive = snd_ctl_elem_info_is_inactive(info);
1558	simple->ctls[type].values = values;
1559	if ( (type == CTL_GLOBAL_ENUM) ||
1560	     (type == CTL_PLAYBACK_ENUM) ||
1561	     (type == CTL_CAPTURE_ENUM) ) {
1562		simple->ctls[type].min = 0;
1563		simple->ctls[type].max = snd_ctl_elem_info_get_items(info);
1564	} else {
1565		if (ctype == SND_CTL_ELEM_TYPE_INTEGER) {
1566			simple->ctls[type].min = snd_ctl_elem_info_get_min(info);
1567			simple->ctls[type].max = snd_ctl_elem_info_get_max(info);
1568		}
1569	}
1570	switch (type) {
1571	case CTL_CAPTURE_SOURCE:
1572		simple->capture_item = value;
1573		break;
1574	default:
1575		break;
1576	}
1577	err = snd_mixer_elem_attach(melem, helem);
1578	if (err < 0)
1579		goto __error;
1580	err = simple_update(melem);
1581	if (err < 0) {
1582		if (new)
1583			goto __error;
1584		return err;
1585	}
1586	if (new)
1587		err = snd_mixer_elem_add(melem, class);
1588	else
1589		err = snd_mixer_elem_info(melem);
1590	if (err < 0)
1591		return err;
1592	err = selem_read(melem);
1593	if (err < 0)
1594		return err;
1595	if (err)
1596		err = snd_mixer_elem_value(melem);
1597	return err;
1598      __error:
1599	if (new)
1600		snd_mixer_elem_free(melem);
1601	return -EINVAL;
1602}
1603
1604static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
1605{
1606	const char *name = snd_hctl_elem_get_name(helem);
1607	size_t len;
1608	selem_ctl_type_t type = CTL_SINGLE; /* to shut up warning */
1609	if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER)
1610		return 0;
1611	if (strcmp(name, "Capture Source") == 0) {
1612		snd_ctl_elem_info_t *info;
1613		unsigned int k, items;
1614		int err;
1615		snd_ctl_elem_info_alloca(&info);
1616		err = snd_hctl_elem_info(helem, info);
1617		assert(err >= 0);
1618		if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_ENUMERATED)
1619			return 0;
1620		items = snd_ctl_elem_info_get_items(info);
1621		for (k = 0; k < items; ++k) {
1622			const char *n;
1623			snd_ctl_elem_info_set_item(info, k);
1624			err = snd_hctl_elem_info(helem, info);
1625			if (err < 0)
1626				return err;
1627			n = snd_ctl_elem_info_get_item_name(info);
1628			err = simple_add1(class, n, helem, CTL_CAPTURE_SOURCE, k);
1629			if (err < 0)
1630				return err;
1631		}
1632		return 0;
1633	}
1634	len = base_len(name, &type);
1635	if (len == 0) {
1636		return simple_add1(class, name, helem, CTL_SINGLE, 0);
1637	} else {
1638		char ename[128];
1639		if (len >= sizeof(ename))
1640			len = sizeof(ename) - 1;
1641		memcpy(ename, name, len);
1642		ename[len] = 0;
1643		/* exception: Capture Volume and Capture Switch */
1644		if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture"))
1645			type = CTL_CAPTURE_VOLUME;
1646		else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture"))
1647			type = CTL_CAPTURE_SWITCH;
1648		return simple_add1(class, ename, helem, type, 0);
1649	}
1650}
1651
1652static int simple_event_remove(snd_hctl_elem_t *helem,
1653			       snd_mixer_elem_t *melem)
1654{
1655	selem_none_t *simple = snd_mixer_elem_get_private(melem);
1656	int err;
1657	int k;
1658	for (k = 0; k <= CTL_LAST; k++) {
1659		if (simple->ctls[k].elem == helem)
1660			break;
1661	}
1662	assert(k <= CTL_LAST);
1663	simple->ctls[k].elem = NULL;
1664	err = snd_mixer_elem_detach(melem, helem);
1665	if (err < 0)
1666		return err;
1667	if (snd_mixer_elem_empty(melem))
1668		return snd_mixer_elem_remove(melem);
1669	err = simple_update(melem);
1670	return snd_mixer_elem_info(melem);
1671}
1672
1673static int simple_event(snd_mixer_class_t *class, unsigned int mask,
1674			snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
1675{
1676	int err;
1677	if (mask == SND_CTL_EVENT_MASK_REMOVE)
1678		return simple_event_remove(helem, melem);
1679	if (mask & SND_CTL_EVENT_MASK_ADD) {
1680		err = simple_event_add(class, helem);
1681		if (err < 0)
1682			return err;
1683	}
1684	if (mask & SND_CTL_EVENT_MASK_INFO) {
1685		err = simple_event_remove(helem, melem);
1686		if (err < 0)
1687			return err;
1688		err = simple_event_add(class, helem);
1689		if (err < 0)
1690			return err;
1691		return 0;
1692	}
1693	if (mask & SND_CTL_EVENT_MASK_VALUE) {
1694		err = selem_read(melem);
1695		if (err < 0)
1696			return err;
1697		if (err) {
1698			err = snd_mixer_elem_value(melem);
1699			if (err < 0)
1700				return err;
1701		}
1702	}
1703	return 0;
1704}
1705
1706/**
1707 * \brief Register mixer simple element class - none abstraction
1708 * \param mixer Mixer handle
1709 * \param options Options container
1710 * \param classp Pointer to returned mixer simple element class handle (or NULL)
1711 * \return 0 on success otherwise a negative error code
1712 */
1713int snd_mixer_simple_none_register(snd_mixer_t *mixer,
1714				   struct snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED,
1715				   snd_mixer_class_t **classp)
1716{
1717	snd_mixer_class_t *class;
1718	int err;
1719
1720	if (snd_mixer_class_malloc(&class))
1721		return -ENOMEM;
1722	snd_mixer_class_set_event(class, simple_event);
1723	snd_mixer_class_set_compare(class, snd_mixer_selem_compare);
1724	err = snd_mixer_class_register(class, mixer);
1725	if (err < 0) {
1726		free(class);
1727		return err;
1728	}
1729	if (classp)
1730		*classp = class;
1731	return 0;
1732}
1733