• Home
  • History
  • Annotate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/iserver/alsa-lib-1.0.26/src/mixer/

Lines Matching defs:*

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
8 * Mixer simple element class interface.
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>
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.
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.
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
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"
45 #ifndef DOC_HIDDEN
47 #define MIXER_COMPARE_WEIGHT_SIMPLE_BASE 0
48 #define MIXER_COMPARE_WEIGHT_NEXT_BASE 10000000
49 #define MIXER_COMPARE_WEIGHT_NOT_FOUND 1000000000
51 typedef enum _selem_ctl_type {
52 CTL_SINGLE,
53 CTL_GLOBAL_ENUM,
54 CTL_GLOBAL_SWITCH,
55 CTL_GLOBAL_VOLUME,
56 CTL_GLOBAL_ROUTE,
57 CTL_PLAYBACK_ENUM,
58 CTL_PLAYBACK_SWITCH,
59 CTL_PLAYBACK_VOLUME,
60 CTL_PLAYBACK_ROUTE,
61 CTL_CAPTURE_ENUM,
62 CTL_CAPTURE_SWITCH,
63 CTL_CAPTURE_VOLUME,
64 CTL_CAPTURE_ROUTE,
65 CTL_CAPTURE_SOURCE,
66 CTL_LAST = CTL_CAPTURE_SOURCE,
67 } selem_ctl_type_t;
69 typedef struct _selem_ctl {
70 snd_hctl_elem_t *elem;
71 snd_ctl_elem_type_t type;
72 unsigned int inactive: 1;
73 unsigned int values;
74 long min, max;
75 } selem_ctl_t;
77 typedef struct _selem_none {
78 sm_selem_t selem;
79 selem_ctl_t ctls[CTL_LAST + 1];
80 unsigned int capture_item;
81 struct selem_str {
82 unsigned int range: 1; /* Forced range */
83 unsigned int db_initialized: 1;
84 unsigned int db_init_error: 1;
85 long min, max;
86 unsigned int channels;
87 long vol[32];
88 unsigned int sw;
89 unsigned int *db_info;
90 } str[2];
91 } selem_none_t;
93 static const struct mixer_name_table {
94 const char *longname;
95 const char *shortname;
96 } name_table[] = {
97 {"Tone Control - Switch", "Tone"},
98 {"Tone Control - Bass", "Bass"},
99 {"Tone Control - Treble", "Treble"},
100 {"Synth Tone Control - Switch", "Synth Tone"},
101 {"Synth Tone Control - Bass", "Synth Bass"},
102 {"Synth Tone Control - Treble", "Synth Treble"},
103 {0, 0},
106 #endif /* !DOC_HIDDEN */
108 static const char *get_short_name(const char *lname)
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;
115 return lname;
118 static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef)
120 int res;
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;
130 return MIXER_COMPARE_WEIGHT_NOT_FOUND;
133 static int get_compare_weight(const char *name, unsigned int idx)
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
171 static const char *const names1[] = {
172 "-",
173 NULL,
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,
191 const char *name1;
192 int res, res1;
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;
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;
217 static long to_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
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);
226 static long from_user(selem_none_t *s, int dir, selem_ctl_t *c, long value)
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);
235 static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
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));
250 return 0;
253 static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
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);
269 return 0;
272 static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type)
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);
288 return 0;
291 static int elem_read_enum(selem_none_t *s)
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);
315 return 0;
318 static int selem_read(snd_mixer_elem_t *elem)
320 selem_none_t *s;
321 unsigned int idx;
322 int err = 0;
323 long pvol[32], cvol[32];
324 unsigned int psw, csw;
326 assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
327 s = snd_mixer_elem_get_private(elem);
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;
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;
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;
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;
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;
370 if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) {
371 s->str[SM_PLAY].sw = 0;
372 goto __skip_pswitch;
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;
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;
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;
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;
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;
400 __skip_pswitch:
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;
412 if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) {
413 s->str[SM_CAPT].sw = 0;
414 goto __skip_cswitch;
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;
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;
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;
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;
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;
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);
457 __skip_cswitch:
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;
467 static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type)
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;
483 static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type)
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;
499 static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val)
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;
515 static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type)
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;
533 static int elem_write_enum(selem_none_t *s)
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;
558 static int selem_write_main(snd_mixer_elem_t *elem)
560 selem_none_t *s;
561 unsigned int idx;
562 int err;
564 assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
565 s = snd_mixer_elem_get_private(elem);
567 if (s->ctls[CTL_GLOBAL_ENUM].elem)
568 return elem_write_enum(s);
570 if (s->ctls[CTL_PLAYBACK_ENUM].elem)
571 return elem_write_enum(s);
573 if (s->ctls[CTL_CAPTURE_ENUM].elem)
574 return elem_write_enum(s);
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;
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;
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;
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;
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;
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;
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;
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;
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;
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);
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;
644 return 0;
647 static int selem_write(snd_mixer_elem_t *elem)
649 int err;
651 err = selem_write_main(elem);
652 if (err < 0)
653 selem_read(elem);
654 return err;
657 static void selem_free(snd_mixer_elem_t *elem)
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);
669 static int simple_update(snd_mixer_elem_t *melem)
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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);
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);
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);
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;
840 if (simple->ctls[CTL_PLAYBACK_VOLUME].elem &&
841 simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) {
842 caps &= ~SM_CAP_PVOLUME_JOIN;
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;
853 if (simple->ctls[CTL_CAPTURE_VOLUME].elem &&
854 simple->ctls[CTL_CAPTURE_VOLUME].values > 1) {
855 caps &= ~SM_CAP_CVOLUME_JOIN;
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;
866 if ((caps & SM_CAP_GSWITCH) &&
867 (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
868 caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;
870 if ((caps & SM_CAP_GVOLUME) &&
871 (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
872 caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;
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;
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;
885 return 0;
888 #ifndef DOC_HIDDEN
889 static const struct suf {
890 const char *suffix;
891 selem_ctl_type_t type;
892 } suffixes[] = {
893 {" Playback Enum", CTL_PLAYBACK_ENUM},
894 {" Playback Switch", CTL_PLAYBACK_SWITCH},
895 {" Playback Route", CTL_PLAYBACK_ROUTE},
896 {" Playback Volume", CTL_PLAYBACK_VOLUME},
897 {" Capture Enum", CTL_CAPTURE_ENUM},
898 {" Capture Switch", CTL_CAPTURE_SWITCH},
899 {" Capture Route", CTL_CAPTURE_ROUTE},
900 {" Capture Volume", CTL_CAPTURE_VOLUME},
901 {" Enum", CTL_GLOBAL_ENUM},
902 {" Switch", CTL_GLOBAL_SWITCH},
903 {" Route", CTL_GLOBAL_ROUTE},
904 {" Volume", CTL_GLOBAL_VOLUME},
905 {NULL, 0}
907 #endif
909 /* Return base length or 0 on failure */
910 static int base_len(const char *name, selem_ctl_type_t *type)
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;
926 p++;
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.
936 if (!strcmp(name, "Input Source")) {
937 *type = CTL_CAPTURE_ROUTE;
938 return strlen(name);
940 if (strstr(name, "3D Control")) {
941 if (strstr(name, "Depth")) {
942 *type = CTL_PLAYBACK_VOLUME;
943 return strlen(name);
946 return 0;
951 * Simple Mixer Operations
954 static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value)
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;
970 return 0;
973 static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value)
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;
986 } else {
987 if (s->str[dir].sw & (1 << channel)) {
988 s->str[dir].sw &= ~(1 << channel);
989 return 1;
992 return 0;
995 static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
997 selem_none_t *s = snd_mixer_elem_get_private(elem);
999 switch (cmd) {
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;
1009 case SM_OPS_IS_MONO:
1010 return s->str[dir].channels == 1;
1012 case SM_OPS_IS_CHANNEL:
1013 return (unsigned int) val < s->str[dir].channels;
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;
1023 if (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) )
1024 return 1;
1025 return 0;
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;
1047 return 1;
1050 static int get_range_ops(snd_mixer_elem_t *elem, int dir,
1051 long *min, long *max)
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;
1059 static int set_range_ops(snd_mixer_elem_t *elem, int dir,
1060 long min, long max)
1062 selem_none_t *s = snd_mixer_elem_get_private(elem);
1063 int err;
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;
1073 static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
1074 snd_mixer_selem_channel_id_t channel, long *value)
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;
1085 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec);
1087 static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
1088 long volume, long *db_gain)
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);
1096 /* initialize dB range information, reading TLV via hcontrol
1098 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec)
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;
1106 if (rec->db_init_error)
1107 return -EINVAL;
1108 if (rec->db_initialized)
1109 return 0;
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;
1132 error:
1133 free(tlv);
1134 rec->db_init_error = 1;
1135 return -EINVAL;
1138 /* get selem_ctl for TLV access */
1139 static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir)
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;
1153 if (c->type != SND_CTL_ELEM_TYPE_INTEGER)
1154 return NULL;
1155 return c;
1158 static int get_dB_range(snd_hctl_elem_t *ctl, struct selem_str *rec,
1159 long *min, long *max)
1161 if (init_db_range(ctl, rec) < 0)
1162 return -EINVAL;
1164 return snd_tlv_get_dB_range(rec->db_info, rec->min, rec->max, min, max);
1167 static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir,
1168 long *min, long *max)
1170 selem_none_t *s = snd_mixer_elem_get_private(elem);
1171 selem_ctl_t *c;
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);
1181 static int convert_from_dB(snd_hctl_elem_t *ctl, struct selem_str *rec,
1182 long db_gain, long *value, int xdir)
1184 if (init_db_range(ctl, rec) < 0)
1185 return -EINVAL;
1187 return snd_tlv_convert_from_dB(rec->db_info, rec->min, rec->max,
1188 db_gain, value, xdir);
1191 static int ask_vol_dB_ops(snd_mixer_elem_t *elem,
1192 int dir,
1193 long value,
1194 long *dBvalue)
1196 selem_none_t *s = snd_mixer_elem_get_private(elem);
1197 selem_ctl_t *c;
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;
1206 static int get_dB_ops(snd_mixer_elem_t *elem,
1207 int dir,
1208 snd_mixer_selem_channel_id_t channel,
1209 long *value)
1211 selem_none_t *s = snd_mixer_elem_get_private(elem);
1212 selem_ctl_t *c;
1213 int err;
1214 long volume, db_gain;
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;
1231 static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
1232 snd_mixer_selem_channel_id_t channel, int *value)
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;
1243 static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
1244 snd_mixer_selem_channel_id_t channel, long value)
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;
1255 static int ask_dB_vol_ops(snd_mixer_elem_t *elem, int dir,
1256 long dbValue, long *value, int xdir)
1258 selem_none_t *s = snd_mixer_elem_get_private(elem);
1259 selem_ctl_t *c;
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);
1269 static 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)
1273 selem_none_t *s = snd_mixer_elem_get_private(elem);
1274 selem_ctl_t *c;
1275 long value;
1276 int err;
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);
1289 static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
1290 snd_mixer_selem_channel_id_t channel, int value)
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;
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;
1311 static int enum_item_name_ops(snd_mixer_elem_t *elem,
1312 unsigned int item,
1313 size_t maxlen, char *buf)
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;
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;
1326 if (!helem) {
1327 type = CTL_CAPTURE_ENUM;
1328 helem = s->ctls[type].elem;
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;
1341 static int get_enum_item_ops(snd_mixer_elem_t *elem,
1342 snd_mixer_selem_channel_id_t channel,
1343 unsigned int *itemp)
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;
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;
1363 static int set_enum_item_ops(snd_mixer_elem_t *elem,
1364 snd_mixer_selem_channel_id_t channel,
1365 unsigned int item)
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;
1373 if ((unsigned int) channel >= s->str[0].channels) {
1374 return -EINVAL;
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;
1382 if (!helem) {
1383 type = CTL_CAPTURE_ENUM;
1384 helem = s->ctls[type].elem;
1386 assert(helem);
1387 if (item >= (unsigned int)s->ctls[type].max) {
1388 return -EINVAL;
1390 snd_ctl_elem_value_alloca(&ctl);
1391 err = snd_hctl_elem_read(helem, ctl);
1392 if (err < 0) {
1393 return err;
1395 snd_ctl_elem_value_set_enumerated(ctl, channel, item);
1396 return snd_hctl_elem_write(helem, ctl);
1399 static 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
1417 static 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)
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;
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:
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;
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;
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;
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;
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;
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;
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;
1540 new = 1;
1541 } else {
1542 simple = snd_mixer_elem_get_private(melem);
1543 snd_mixer_selem_id_free(id);
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;
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);
1570 switch (type) {
1571 case CTL_CAPTURE_SOURCE:
1572 simple->capture_item = value;
1573 break;
1574 default:
1575 break;
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;
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;
1604 static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
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;
1632 return 0;
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);
1652 static int simple_event_remove(snd_hctl_elem_t *helem,
1653 snd_mixer_elem_t *melem)
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;
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);
1673 static int simple_event(snd_mixer_class_t *class, unsigned int mask,
1674 snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
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;
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;
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;
1703 return 0;
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
1713 int snd_mixer_simple_none_register(snd_mixer_t *mixer,
1714 struct snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED,
1715 snd_mixer_class_t **classp)
1717 snd_mixer_class_t *class;
1718 int err;
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;
1729 if (classp)
1730 *classp = class;
1731 return 0;