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