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