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