]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
llvm-clang-analyzer: drop a few unnecessary assignments and other trivial fixes
[pulseaudio] / src / modules / alsa / alsa-mixer.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <limits.h>
29 #include <asoundlib.h>
30
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
34
35 #include <pulse/sample.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/i18n.h>
40 #include <pulse/utf8.h>
41
42 #include <pulsecore/log.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/atomic.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/once.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/conf-parser.h>
50 #include <pulsecore/strbuf.h>
51
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
54
55 struct description_map {
56 const char *name;
57 const char *description;
58 };
59
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 unsigned i;
62
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return dm[i].description;
66
67 return NULL;
68 }
69
70 struct pa_alsa_fdlist {
71 unsigned num_fds;
72 struct pollfd *fds;
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd *work_fds;
75
76 snd_mixer_t *mixer;
77
78 pa_mainloop_api *m;
79 pa_defer_event *defer;
80 pa_io_event **ios;
81
82 pa_bool_t polled;
83
84 void (*cb)(void *userdata);
85 void *userdata;
86 };
87
88 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
89
90 struct pa_alsa_fdlist *fdl = userdata;
91 int err;
92 unsigned i;
93 unsigned short revents;
94
95 pa_assert(a);
96 pa_assert(fdl);
97 pa_assert(fdl->mixer);
98 pa_assert(fdl->fds);
99 pa_assert(fdl->work_fds);
100
101 if (fdl->polled)
102 return;
103
104 fdl->polled = TRUE;
105
106 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
107
108 for (i = 0; i < fdl->num_fds; i++) {
109 if (e == fdl->ios[i]) {
110 if (events & PA_IO_EVENT_INPUT)
111 fdl->work_fds[i].revents |= POLLIN;
112 if (events & PA_IO_EVENT_OUTPUT)
113 fdl->work_fds[i].revents |= POLLOUT;
114 if (events & PA_IO_EVENT_ERROR)
115 fdl->work_fds[i].revents |= POLLERR;
116 if (events & PA_IO_EVENT_HANGUP)
117 fdl->work_fds[i].revents |= POLLHUP;
118 break;
119 }
120 }
121
122 pa_assert(i != fdl->num_fds);
123
124 if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) {
125 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
126 return;
127 }
128
129 a->defer_enable(fdl->defer, 1);
130
131 if (revents)
132 snd_mixer_handle_events(fdl->mixer);
133 }
134
135 static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {
136 struct pa_alsa_fdlist *fdl = userdata;
137 unsigned num_fds, i;
138 int err, n;
139 struct pollfd *temp;
140
141 pa_assert(a);
142 pa_assert(fdl);
143 pa_assert(fdl->mixer);
144
145 a->defer_enable(fdl->defer, 0);
146
147 if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) {
148 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
149 return;
150 }
151 num_fds = (unsigned) n;
152
153 if (num_fds != fdl->num_fds) {
154 if (fdl->fds)
155 pa_xfree(fdl->fds);
156 if (fdl->work_fds)
157 pa_xfree(fdl->work_fds);
158 fdl->fds = pa_xnew0(struct pollfd, num_fds);
159 fdl->work_fds = pa_xnew(struct pollfd, num_fds);
160 }
161
162 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
163
164 if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) {
165 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
166 return;
167 }
168
169 fdl->polled = FALSE;
170
171 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
172 return;
173
174 if (fdl->ios) {
175 for (i = 0; i < fdl->num_fds; i++)
176 a->io_free(fdl->ios[i]);
177
178 if (num_fds != fdl->num_fds) {
179 pa_xfree(fdl->ios);
180 fdl->ios = NULL;
181 }
182 }
183
184 if (!fdl->ios)
185 fdl->ios = pa_xnew(pa_io_event*, num_fds);
186
187 /* Swap pointers */
188 temp = fdl->work_fds;
189 fdl->work_fds = fdl->fds;
190 fdl->fds = temp;
191
192 fdl->num_fds = num_fds;
193
194 for (i = 0;i < num_fds;i++)
195 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
196 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
197 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
198 io_cb, fdl);
199 }
200
201 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist *fdl;
203
204 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
205
206 return fdl;
207 }
208
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
210 pa_assert(fdl);
211
212 if (fdl->defer) {
213 pa_assert(fdl->m);
214 fdl->m->defer_free(fdl->defer);
215 }
216
217 if (fdl->ios) {
218 unsigned i;
219 pa_assert(fdl->m);
220 for (i = 0; i < fdl->num_fds; i++)
221 fdl->m->io_free(fdl->ios[i]);
222 pa_xfree(fdl->ios);
223 }
224
225 if (fdl->fds)
226 pa_xfree(fdl->fds);
227 if (fdl->work_fds)
228 pa_xfree(fdl->work_fds);
229
230 pa_xfree(fdl);
231 }
232
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
234 pa_assert(fdl);
235 pa_assert(mixer_handle);
236 pa_assert(m);
237 pa_assert(!fdl->m);
238
239 fdl->mixer = mixer_handle;
240 fdl->m = m;
241 fdl->defer = m->defer_new(m, defer_cb, fdl);
242
243 return 0;
244 }
245
246 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
247 int err;
248
249 pa_assert(mixer);
250 pa_assert(dev);
251
252 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
253 pa_log_info("Unable to attach to mixer %s: %s", dev, pa_alsa_strerror(err));
254 return -1;
255 }
256
257 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
258 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err));
259 return -1;
260 }
261
262 if ((err = snd_mixer_load(mixer)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
264 return -1;
265 }
266
267 pa_log_info("Successfully attached to mixer '%s'", dev);
268 return 0;
269 }
270
271 snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
272 int err;
273 snd_mixer_t *m;
274 const char *dev;
275 snd_pcm_info_t* info;
276 snd_pcm_info_alloca(&info);
277
278 pa_assert(pcm);
279
280 if ((err = snd_mixer_open(&m, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
282 return NULL;
283 }
284
285 /* First, try by name */
286 if ((dev = snd_pcm_name(pcm)))
287 if (prepare_mixer(m, dev) >= 0) {
288 if (ctl_device)
289 *ctl_device = pa_xstrdup(dev);
290
291 return m;
292 }
293
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm, info) >= 0) {
296 char *md;
297 int card_idx;
298
299 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
300
301 md = pa_sprintf_malloc("hw:%i", card_idx);
302
303 if (!dev || !pa_streq(dev, md))
304 if (prepare_mixer(m, md) >= 0) {
305
306 if (ctl_device)
307 *ctl_device = md;
308 else
309 pa_xfree(md);
310
311 return m;
312 }
313
314 pa_xfree(md);
315 }
316 }
317
318 snd_mixer_close(m);
319 return NULL;
320 }
321
322 static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
323 [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
324
325 [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
326 [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
327 [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
328
329 [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
330 [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
331 [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
332
333 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
334
335 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
336 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
337
338 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
339 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
340
341 [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
342 [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
343 [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
344 [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
345 [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
346 [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
347 [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
348 [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
349 [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
350 [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN,
351 [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
352 [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
353 [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
354 [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
355 [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
356 [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
357 [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
358 [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
359 [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
360 [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
361 [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
362 [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
363 [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
364 [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
365 [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
366 [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
367 [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
368 [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
369 [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
370 [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
371 [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
372 [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
373
374 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
375
376 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
377 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
378 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
379
380 [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
381 [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
382 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
383 };
384
385 static void setting_free(pa_alsa_setting *s) {
386 pa_assert(s);
387
388 if (s->options)
389 pa_idxset_free(s->options, NULL, NULL);
390
391 pa_xfree(s->name);
392 pa_xfree(s->description);
393 pa_xfree(s);
394 }
395
396 static void option_free(pa_alsa_option *o) {
397 pa_assert(o);
398
399 pa_xfree(o->alsa_name);
400 pa_xfree(o->name);
401 pa_xfree(o->description);
402 pa_xfree(o);
403 }
404
405 static void element_free(pa_alsa_element *e) {
406 pa_alsa_option *o;
407 pa_assert(e);
408
409 while ((o = e->options)) {
410 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
411 option_free(o);
412 }
413
414 pa_xfree(e->alsa_name);
415 pa_xfree(e);
416 }
417
418 void pa_alsa_path_free(pa_alsa_path *p) {
419 pa_alsa_element *e;
420 pa_alsa_setting *s;
421
422 pa_assert(p);
423
424 while ((e = p->elements)) {
425 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
426 element_free(e);
427 }
428
429 while ((s = p->settings)) {
430 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
431 setting_free(s);
432 }
433
434 pa_xfree(p->name);
435 pa_xfree(p->description);
436 pa_xfree(p);
437 }
438
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
440 pa_alsa_path *p;
441 pa_assert(ps);
442
443 while ((p = ps->paths)) {
444 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445 pa_alsa_path_free(p);
446 }
447
448 pa_xfree(ps);
449 }
450
451 static long to_alsa_dB(pa_volume_t v) {
452 return (long) (pa_sw_volume_to_dB(v) * 100.0);
453 }
454
455 static pa_volume_t from_alsa_dB(long v) {
456 return pa_sw_volume_from_dB((double) v / 100.0);
457 }
458
459 static long to_alsa_volume(pa_volume_t v, long min, long max) {
460 long w;
461
462 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
463 return PA_CLAMP_UNLIKELY(w, min, max);
464 }
465
466 static pa_volume_t from_alsa_volume(long v, long min, long max) {
467 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
468 }
469
470 #define SELEM_INIT(sid, name) \
471 do { \
472 snd_mixer_selem_id_alloca(&(sid)); \
473 snd_mixer_selem_id_set_name((sid), (name)); \
474 snd_mixer_selem_id_set_index((sid), 0); \
475 } while(FALSE)
476
477 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
478 snd_mixer_selem_id_t *sid;
479 snd_mixer_elem_t *me;
480 snd_mixer_selem_channel_id_t c;
481 pa_channel_position_mask_t mask = 0;
482 pa_volume_t max_channel_volume = PA_VOLUME_MUTED;
483 unsigned k;
484
485 pa_assert(m);
486 pa_assert(e);
487 pa_assert(cm);
488 pa_assert(v);
489
490 SELEM_INIT(sid, e->alsa_name);
491 if (!(me = snd_mixer_find_selem(m, sid))) {
492 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
493 return -1;
494 }
495
496 pa_cvolume_mute(v, cm->channels);
497
498 /* We take the highest volume of all channels that match */
499
500 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
501 int r;
502 pa_volume_t f;
503
504 if (e->has_dB) {
505 long value = 0;
506
507 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
508 if (snd_mixer_selem_has_playback_channel(me, c))
509 r = snd_mixer_selem_get_playback_dB(me, c, &value);
510 else
511 r = -1;
512 } else {
513 if (snd_mixer_selem_has_capture_channel(me, c))
514 r = snd_mixer_selem_get_capture_dB(me, c, &value);
515 else
516 r = -1;
517 }
518
519 if (r < 0)
520 continue;
521
522 #ifdef HAVE_VALGRIND_MEMCHECK_H
523 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
524 #endif
525
526 f = from_alsa_dB(value);
527
528 } else {
529 long value = 0;
530
531 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
532 if (snd_mixer_selem_has_playback_channel(me, c))
533 r = snd_mixer_selem_get_playback_volume(me, c, &value);
534 else
535 r = -1;
536 } else {
537 if (snd_mixer_selem_has_capture_channel(me, c))
538 r = snd_mixer_selem_get_capture_volume(me, c, &value);
539 else
540 r = -1;
541 }
542
543 if (r < 0)
544 continue;
545
546 f = from_alsa_volume(value, e->min_volume, e->max_volume);
547 }
548
549 if (f > max_channel_volume)
550 max_channel_volume = f;
551
552 for (k = 0; k < cm->channels; k++)
553 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
554 if (v->values[k] < f)
555 v->values[k] = f;
556
557 mask |= e->masks[c][e->n_channels-1];
558 }
559
560 for (k = 0; k < cm->channels; k++)
561 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
562 v->values[k] = max_channel_volume;
563
564 return 0;
565 }
566
567 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
568 pa_alsa_element *e;
569
570 pa_assert(m);
571 pa_assert(p);
572 pa_assert(cm);
573 pa_assert(v);
574
575 if (!p->has_volume)
576 return -1;
577
578 pa_cvolume_reset(v, cm->channels);
579
580 PA_LLIST_FOREACH(e, p->elements) {
581 pa_cvolume ev;
582
583 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
584 continue;
585
586 pa_assert(!p->has_dB || e->has_dB);
587
588 if (element_get_volume(e, m, cm, &ev) < 0)
589 return -1;
590
591 /* If we have no dB information all we can do is take the first element and leave */
592 if (!p->has_dB) {
593 *v = ev;
594 return 0;
595 }
596
597 pa_sw_cvolume_multiply(v, v, &ev);
598 }
599
600 return 0;
601 }
602
603 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
604 snd_mixer_selem_id_t *sid;
605 snd_mixer_elem_t *me;
606 snd_mixer_selem_channel_id_t c;
607
608 pa_assert(m);
609 pa_assert(e);
610 pa_assert(b);
611
612 SELEM_INIT(sid, e->alsa_name);
613 if (!(me = snd_mixer_find_selem(m, sid))) {
614 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
615 return -1;
616 }
617
618 /* We return muted if at least one channel is muted */
619
620 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
621 int r;
622 int value = 0;
623
624 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
625 if (snd_mixer_selem_has_playback_channel(me, c))
626 r = snd_mixer_selem_get_playback_switch(me, c, &value);
627 else
628 r = -1;
629 } else {
630 if (snd_mixer_selem_has_capture_channel(me, c))
631 r = snd_mixer_selem_get_capture_switch(me, c, &value);
632 else
633 r = -1;
634 }
635
636 if (r < 0)
637 continue;
638
639 if (!value) {
640 *b = FALSE;
641 return 0;
642 }
643 }
644
645 *b = TRUE;
646 return 0;
647 }
648
649 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
650 pa_alsa_element *e;
651
652 pa_assert(m);
653 pa_assert(p);
654 pa_assert(muted);
655
656 if (!p->has_mute)
657 return -1;
658
659 PA_LLIST_FOREACH(e, p->elements) {
660 pa_bool_t b;
661
662 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
663 continue;
664
665 if (element_get_switch(e, m, &b) < 0)
666 return -1;
667
668 if (!b) {
669 *muted = TRUE;
670 return 0;
671 }
672 }
673
674 *muted = FALSE;
675 return 0;
676 }
677
678 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
679 snd_mixer_selem_id_t *sid;
680 pa_cvolume rv;
681 snd_mixer_elem_t *me;
682 snd_mixer_selem_channel_id_t c;
683 pa_channel_position_mask_t mask = 0;
684 pa_volume_t max_channel_volume = PA_VOLUME_MUTED;
685 unsigned k;
686
687 pa_assert(m);
688 pa_assert(e);
689 pa_assert(cm);
690 pa_assert(v);
691 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
692
693 SELEM_INIT(sid, e->alsa_name);
694 if (!(me = snd_mixer_find_selem(m, sid))) {
695 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
696 return -1;
697 }
698
699 pa_cvolume_mute(&rv, cm->channels);
700
701 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
702 int r;
703 pa_volume_t f = PA_VOLUME_MUTED;
704 pa_bool_t found = FALSE;
705
706 for (k = 0; k < cm->channels; k++)
707 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
708 found = TRUE;
709 if (v->values[k] > f)
710 f = v->values[k];
711 }
712
713 if (!found) {
714 /* Hmm, so this channel does not exist in the volume
715 * struct, so let's bind it to the overall max of the
716 * volume. */
717 f = pa_cvolume_max(v);
718 }
719
720 if (e->has_dB) {
721 long value = to_alsa_dB(f);
722
723 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
724 /* If we call set_play_volume() without checking first
725 * if the channel is available, ALSA behaves ver
726 * strangely and doesn't fail the call */
727 if (snd_mixer_selem_has_playback_channel(me, c)) {
728 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
729 r = snd_mixer_selem_get_playback_dB(me, c, &value);
730 } else
731 r = -1;
732 } else {
733 if (snd_mixer_selem_has_capture_channel(me, c)) {
734 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
735 r = snd_mixer_selem_get_capture_dB(me, c, &value);
736 } else
737 r = -1;
738 }
739
740 if (r < 0)
741 continue;
742
743 #ifdef HAVE_VALGRIND_MEMCHECK_H
744 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
745 #endif
746
747 f = from_alsa_dB(value);
748
749 } else {
750 long value;
751
752 value = to_alsa_volume(f, e->min_volume, e->max_volume);
753
754 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
755 if (snd_mixer_selem_has_playback_channel(me, c)) {
756 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
757 r = snd_mixer_selem_get_playback_volume(me, c, &value);
758 } else
759 r = -1;
760 } else {
761 if (snd_mixer_selem_has_capture_channel(me, c)) {
762 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
763 r = snd_mixer_selem_get_capture_volume(me, c, &value);
764 } else
765 r = -1;
766 }
767
768 if (r < 0)
769 continue;
770
771 f = from_alsa_volume(value, e->min_volume, e->max_volume);
772 }
773
774 if (f > max_channel_volume)
775 max_channel_volume = f;
776
777 for (k = 0; k < cm->channels; k++)
778 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
779 if (rv.values[k] < f)
780 rv.values[k] = f;
781
782 mask |= e->masks[c][e->n_channels-1];
783 }
784
785 for (k = 0; k < cm->channels; k++)
786 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
787 rv.values[k] = max_channel_volume;
788
789 *v = rv;
790 return 0;
791 }
792
793 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
794 pa_alsa_element *e;
795 pa_cvolume rv;
796
797 pa_assert(m);
798 pa_assert(p);
799 pa_assert(cm);
800 pa_assert(v);
801 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
802
803 if (!p->has_volume)
804 return -1;
805
806 rv = *v; /* Remaining adjustment */
807 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
808
809 PA_LLIST_FOREACH(e, p->elements) {
810 pa_cvolume ev;
811
812 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
813 continue;
814
815 pa_assert(!p->has_dB || e->has_dB);
816
817 ev = rv;
818 if (element_set_volume(e, m, cm, &ev) < 0)
819 return -1;
820
821 if (!p->has_dB) {
822 *v = ev;
823 return 0;
824 }
825
826 pa_sw_cvolume_multiply(v, v, &ev);
827 pa_sw_cvolume_divide(&rv, &rv, &ev);
828 }
829
830 return 0;
831 }
832
833 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
834 snd_mixer_elem_t *me;
835 snd_mixer_selem_id_t *sid;
836 int r;
837
838 pa_assert(m);
839 pa_assert(e);
840
841 SELEM_INIT(sid, e->alsa_name);
842 if (!(me = snd_mixer_find_selem(m, sid))) {
843 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
844 return -1;
845 }
846
847 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
848 r = snd_mixer_selem_set_playback_switch_all(me, b);
849 else
850 r = snd_mixer_selem_set_capture_switch_all(me, b);
851
852 if (r < 0)
853 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
854
855 return r;
856 }
857
858 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
859 pa_alsa_element *e;
860
861 pa_assert(m);
862 pa_assert(p);
863
864 if (!p->has_mute)
865 return -1;
866
867 PA_LLIST_FOREACH(e, p->elements) {
868
869 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
870 continue;
871
872 if (element_set_switch(e, m, !muted) < 0)
873 return -1;
874 }
875
876 return 0;
877 }
878
879 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
880 snd_mixer_elem_t *me;
881 snd_mixer_selem_id_t *sid;
882 int r;
883
884 pa_assert(m);
885 pa_assert(e);
886
887 SELEM_INIT(sid, e->alsa_name);
888 if (!(me = snd_mixer_find_selem(m, sid))) {
889 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
890 return -1;
891 }
892
893 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
894 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
895 else
896 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
897
898 if (r < 0)
899 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
900
901 return r;
902 }
903
904 /* The volume to 0dB */
905 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
906 snd_mixer_elem_t *me;
907 snd_mixer_selem_id_t *sid;
908 int r;
909
910 pa_assert(m);
911 pa_assert(e);
912
913 SELEM_INIT(sid, e->alsa_name);
914 if (!(me = snd_mixer_find_selem(m, sid))) {
915 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
916 return -1;
917 }
918
919 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
920 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
921 else
922 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
923
924 if (r < 0)
925 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
926
927 return r;
928 }
929
930 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
931 pa_alsa_element *e;
932 int r = 0;
933
934 pa_assert(m);
935 pa_assert(p);
936
937 pa_log_debug("Activating path %s", p->name);
938 pa_alsa_path_dump(p);
939
940 PA_LLIST_FOREACH(e, p->elements) {
941
942 switch (e->switch_use) {
943 case PA_ALSA_SWITCH_OFF:
944 r = element_set_switch(e, m, FALSE);
945 break;
946
947 case PA_ALSA_SWITCH_ON:
948 r = element_set_switch(e, m, TRUE);
949 break;
950
951 case PA_ALSA_SWITCH_MUTE:
952 case PA_ALSA_SWITCH_IGNORE:
953 case PA_ALSA_SWITCH_SELECT:
954 r = 0;
955 break;
956 }
957
958 if (r < 0)
959 return -1;
960
961 switch (e->volume_use) {
962 case PA_ALSA_VOLUME_OFF:
963 r = element_mute_volume(e, m);
964 break;
965
966 case PA_ALSA_VOLUME_ZERO:
967 r = element_zero_volume(e, m);
968 break;
969
970 case PA_ALSA_VOLUME_MERGE:
971 case PA_ALSA_VOLUME_IGNORE:
972 r = 0;
973 break;
974 }
975
976 if (r < 0)
977 return -1;
978 }
979
980 return 0;
981 }
982
983 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
984 pa_bool_t has_switch;
985 pa_bool_t has_enumeration;
986 pa_bool_t has_volume;
987
988 pa_assert(e);
989 pa_assert(me);
990
991 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
992 has_switch =
993 snd_mixer_selem_has_playback_switch(me) ||
994 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
995 } else {
996 has_switch =
997 snd_mixer_selem_has_capture_switch(me) ||
998 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
999 }
1000
1001 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1002 has_volume =
1003 snd_mixer_selem_has_playback_volume(me) ||
1004 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
1005 } else {
1006 has_volume =
1007 snd_mixer_selem_has_capture_volume(me) ||
1008 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1009 }
1010
1011 has_enumeration = snd_mixer_selem_is_enumerated(me);
1012
1013 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1014 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1015 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1016 return -1;
1017
1018 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1019 return -1;
1020
1021 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1022 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1023 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1024 return -1;
1025
1026 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1027 return -1;
1028
1029 return 0;
1030 }
1031
1032 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1033 snd_mixer_selem_id_t *sid;
1034 snd_mixer_elem_t *me;
1035
1036 pa_assert(m);
1037 pa_assert(e);
1038
1039 SELEM_INIT(sid, e->alsa_name);
1040
1041 if (!(me = snd_mixer_find_selem(m, sid))) {
1042
1043 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1044 return -1;
1045
1046 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1047 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1048 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1049
1050 return 0;
1051 }
1052
1053 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1054 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1055
1056 if (!snd_mixer_selem_has_playback_switch(me)) {
1057 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1058 e->direction = PA_ALSA_DIRECTION_INPUT;
1059 else
1060 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1061 }
1062
1063 } else {
1064
1065 if (!snd_mixer_selem_has_capture_switch(me)) {
1066 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1067 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1068 else
1069 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1070 }
1071 }
1072
1073 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1074 e->direction_try_other = FALSE;
1075 }
1076
1077 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1078
1079 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1080
1081 if (!snd_mixer_selem_has_playback_volume(me)) {
1082 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1083 e->direction = PA_ALSA_DIRECTION_INPUT;
1084 else
1085 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1086 }
1087
1088 } else {
1089
1090 if (!snd_mixer_selem_has_capture_volume(me)) {
1091 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1092 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1093 else
1094 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1095 }
1096 }
1097
1098 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1099 long min_dB = 0, max_dB = 0;
1100 int r;
1101
1102 e->direction_try_other = FALSE;
1103
1104 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1105 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1106 else
1107 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1108
1109 if (e->has_dB) {
1110 #ifdef HAVE_VALGRIND_MEMCHECK_H
1111 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1112 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1113 #endif
1114
1115 e->min_dB = ((double) min_dB) / 100.0;
1116 e->max_dB = ((double) max_dB) / 100.0;
1117
1118 if (min_dB >= max_dB) {
1119 pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB);
1120 e->has_dB = FALSE;
1121 }
1122 }
1123
1124 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1125 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1126 else
1127 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1128
1129 if (r < 0) {
1130 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1131 return -1;
1132 }
1133
1134
1135 if (e->min_volume >= e->max_volume) {
1136 pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e->min_volume, e->max_volume);
1137 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1138
1139 } else {
1140 pa_bool_t is_mono;
1141 pa_channel_position_t p;
1142
1143 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1144 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1145 else
1146 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1147
1148 if (is_mono) {
1149 e->n_channels = 1;
1150
1151 if (!e->override_map) {
1152 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1153 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1154 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1155 }
1156
1157 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1158 } else {
1159 e->n_channels = 0;
1160 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1161
1162 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1163 continue;
1164
1165 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1166 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1167 else
1168 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1169 }
1170
1171 if (e->n_channels <= 0) {
1172 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1173 return -1;
1174 }
1175
1176 if (!e->override_map) {
1177 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1178 pa_bool_t has_channel;
1179
1180 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1181 continue;
1182
1183 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1184 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1185 else
1186 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1187
1188 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1189 }
1190 }
1191
1192 e->merged_mask = 0;
1193 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1194 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1195 }
1196 }
1197 }
1198
1199 }
1200
1201 if (check_required(e, me) < 0)
1202 return -1;
1203
1204 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1205 pa_alsa_option *o;
1206
1207 PA_LLIST_FOREACH(o, e->options)
1208 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1209 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1210 int n;
1211 pa_alsa_option *o;
1212
1213 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1214 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1215 return -1;
1216 }
1217
1218 PA_LLIST_FOREACH(o, e->options) {
1219 int i;
1220
1221 for (i = 0; i < n; i++) {
1222 char buf[128];
1223
1224 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1225 continue;
1226
1227 if (!pa_streq(buf, o->alsa_name))
1228 continue;
1229
1230 o->alsa_idx = i;
1231 }
1232 }
1233 }
1234
1235 return 0;
1236 }
1237
1238 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1239 pa_alsa_element *e;
1240
1241 pa_assert(p);
1242 pa_assert(section);
1243
1244 if (prefixed) {
1245 if (!pa_startswith(section, "Element "))
1246 return NULL;
1247
1248 section += 8;
1249 }
1250
1251 /* This is not an element section, but an enum section? */
1252 if (strchr(section, ':'))
1253 return NULL;
1254
1255 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1256 return p->last_element;
1257
1258 PA_LLIST_FOREACH(e, p->elements)
1259 if (pa_streq(e->alsa_name, section))
1260 goto finish;
1261
1262 e = pa_xnew0(pa_alsa_element, 1);
1263 e->path = p;
1264 e->alsa_name = pa_xstrdup(section);
1265 e->direction = p->direction;
1266
1267 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1268
1269 finish:
1270 p->last_element = e;
1271 return e;
1272 }
1273
1274 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1275 char *en;
1276 const char *on;
1277 pa_alsa_option *o;
1278 pa_alsa_element *e;
1279
1280 if (!pa_startswith(section, "Option "))
1281 return NULL;
1282
1283 section += 7;
1284
1285 /* This is not an enum section, but an element section? */
1286 if (!(on = strchr(section, ':')))
1287 return NULL;
1288
1289 en = pa_xstrndup(section, on - section);
1290 on++;
1291
1292 if (p->last_option &&
1293 pa_streq(p->last_option->element->alsa_name, en) &&
1294 pa_streq(p->last_option->alsa_name, on)) {
1295 pa_xfree(en);
1296 return p->last_option;
1297 }
1298
1299 pa_assert_se(e = element_get(p, en, FALSE));
1300 pa_xfree(en);
1301
1302 PA_LLIST_FOREACH(o, e->options)
1303 if (pa_streq(o->alsa_name, on))
1304 goto finish;
1305
1306 o = pa_xnew0(pa_alsa_option, 1);
1307 o->element = e;
1308 o->alsa_name = pa_xstrdup(on);
1309 o->alsa_idx = -1;
1310
1311 if (p->last_option && p->last_option->element == e)
1312 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1313 else
1314 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1315
1316 finish:
1317 p->last_option = o;
1318 return o;
1319 }
1320
1321 static int element_parse_switch(
1322 const char *filename,
1323 unsigned line,
1324 const char *section,
1325 const char *lvalue,
1326 const char *rvalue,
1327 void *data,
1328 void *userdata) {
1329
1330 pa_alsa_path *p = userdata;
1331 pa_alsa_element *e;
1332
1333 pa_assert(p);
1334
1335 if (!(e = element_get(p, section, TRUE))) {
1336 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1337 return -1;
1338 }
1339
1340 if (pa_streq(rvalue, "ignore"))
1341 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1342 else if (pa_streq(rvalue, "mute"))
1343 e->switch_use = PA_ALSA_SWITCH_MUTE;
1344 else if (pa_streq(rvalue, "off"))
1345 e->switch_use = PA_ALSA_SWITCH_OFF;
1346 else if (pa_streq(rvalue, "on"))
1347 e->switch_use = PA_ALSA_SWITCH_ON;
1348 else if (pa_streq(rvalue, "select"))
1349 e->switch_use = PA_ALSA_SWITCH_SELECT;
1350 else {
1351 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1352 return -1;
1353 }
1354
1355 return 0;
1356 }
1357
1358 static int element_parse_volume(
1359 const char *filename,
1360 unsigned line,
1361 const char *section,
1362 const char *lvalue,
1363 const char *rvalue,
1364 void *data,
1365 void *userdata) {
1366
1367 pa_alsa_path *p = userdata;
1368 pa_alsa_element *e;
1369
1370 pa_assert(p);
1371
1372 if (!(e = element_get(p, section, TRUE))) {
1373 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1374 return -1;
1375 }
1376
1377 if (pa_streq(rvalue, "ignore"))
1378 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1379 else if (pa_streq(rvalue, "merge"))
1380 e->volume_use = PA_ALSA_VOLUME_MERGE;
1381 else if (pa_streq(rvalue, "off"))
1382 e->volume_use = PA_ALSA_VOLUME_OFF;
1383 else if (pa_streq(rvalue, "zero"))
1384 e->volume_use = PA_ALSA_VOLUME_ZERO;
1385 else {
1386 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1387 return -1;
1388 }
1389
1390 return 0;
1391 }
1392
1393 static int element_parse_enumeration(
1394 const char *filename,
1395 unsigned line,
1396 const char *section,
1397 const char *lvalue,
1398 const char *rvalue,
1399 void *data,
1400 void *userdata) {
1401
1402 pa_alsa_path *p = userdata;
1403 pa_alsa_element *e;
1404
1405 pa_assert(p);
1406
1407 if (!(e = element_get(p, section, TRUE))) {
1408 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1409 return -1;
1410 }
1411
1412 if (pa_streq(rvalue, "ignore"))
1413 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1414 else if (pa_streq(rvalue, "select"))
1415 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1416 else {
1417 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1418 return -1;
1419 }
1420
1421 return 0;
1422 }
1423
1424 static int option_parse_priority(
1425 const char *filename,
1426 unsigned line,
1427 const char *section,
1428 const char *lvalue,
1429 const char *rvalue,
1430 void *data,
1431 void *userdata) {
1432
1433 pa_alsa_path *p = userdata;
1434 pa_alsa_option *o;
1435 uint32_t prio;
1436
1437 pa_assert(p);
1438
1439 if (!(o = option_get(p, section))) {
1440 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1441 return -1;
1442 }
1443
1444 if (pa_atou(rvalue, &prio) < 0) {
1445 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1446 return -1;
1447 }
1448
1449 o->priority = prio;
1450 return 0;
1451 }
1452
1453 static int option_parse_name(
1454 const char *filename,
1455 unsigned line,
1456 const char *section,
1457 const char *lvalue,
1458 const char *rvalue,
1459 void *data,
1460 void *userdata) {
1461
1462 pa_alsa_path *p = userdata;
1463 pa_alsa_option *o;
1464
1465 pa_assert(p);
1466
1467 if (!(o = option_get(p, section))) {
1468 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1469 return -1;
1470 }
1471
1472 pa_xfree(o->name);
1473 o->name = pa_xstrdup(rvalue);
1474
1475 return 0;
1476 }
1477
1478 static int element_parse_required(
1479 const char *filename,
1480 unsigned line,
1481 const char *section,
1482 const char *lvalue,
1483 const char *rvalue,
1484 void *data,
1485 void *userdata) {
1486
1487 pa_alsa_path *p = userdata;
1488 pa_alsa_element *e;
1489 pa_alsa_required_t req;
1490
1491 pa_assert(p);
1492
1493 if (!(e = element_get(p, section, TRUE))) {
1494 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1495 return -1;
1496 }
1497
1498 if (pa_streq(rvalue, "ignore"))
1499 req = PA_ALSA_REQUIRED_IGNORE;
1500 else if (pa_streq(rvalue, "switch"))
1501 req = PA_ALSA_REQUIRED_SWITCH;
1502 else if (pa_streq(rvalue, "volume"))
1503 req = PA_ALSA_REQUIRED_VOLUME;
1504 else if (pa_streq(rvalue, "enumeration"))
1505 req = PA_ALSA_REQUIRED_ENUMERATION;
1506 else if (pa_streq(rvalue, "any"))
1507 req = PA_ALSA_REQUIRED_ANY;
1508 else {
1509 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1510 return -1;
1511 }
1512
1513 if (pa_streq(lvalue, "required-absent"))
1514 e->required_absent = req;
1515 else
1516 e->required = req;
1517
1518 return 0;
1519 }
1520
1521 static int element_parse_direction(
1522 const char *filename,
1523 unsigned line,
1524 const char *section,
1525 const char *lvalue,
1526 const char *rvalue,
1527 void *data,
1528 void *userdata) {
1529
1530 pa_alsa_path *p = userdata;
1531 pa_alsa_element *e;
1532
1533 pa_assert(p);
1534
1535 if (!(e = element_get(p, section, TRUE))) {
1536 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1537 return -1;
1538 }
1539
1540 if (pa_streq(rvalue, "playback"))
1541 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1542 else if (pa_streq(rvalue, "capture"))
1543 e->direction = PA_ALSA_DIRECTION_INPUT;
1544 else {
1545 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1546 return -1;
1547 }
1548
1549 return 0;
1550 }
1551
1552 static int element_parse_direction_try_other(
1553 const char *filename,
1554 unsigned line,
1555 const char *section,
1556 const char *lvalue,
1557 const char *rvalue,
1558 void *data,
1559 void *userdata) {
1560
1561 pa_alsa_path *p = userdata;
1562 pa_alsa_element *e;
1563 int yes;
1564
1565 if (!(e = element_get(p, section, TRUE))) {
1566 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1567 return -1;
1568 }
1569
1570 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1571 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1572 return -1;
1573 }
1574
1575 e->direction_try_other = !!yes;
1576 return 0;
1577 }
1578
1579 static pa_channel_position_mask_t parse_mask(const char *m) {
1580 pa_channel_position_mask_t v;
1581
1582 if (pa_streq(m, "all-left"))
1583 v = PA_CHANNEL_POSITION_MASK_LEFT;
1584 else if (pa_streq(m, "all-right"))
1585 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1586 else if (pa_streq(m, "all-center"))
1587 v = PA_CHANNEL_POSITION_MASK_CENTER;
1588 else if (pa_streq(m, "all-front"))
1589 v = PA_CHANNEL_POSITION_MASK_FRONT;
1590 else if (pa_streq(m, "all-rear"))
1591 v = PA_CHANNEL_POSITION_MASK_REAR;
1592 else if (pa_streq(m, "all-side"))
1593 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1594 else if (pa_streq(m, "all-top"))
1595 v = PA_CHANNEL_POSITION_MASK_TOP;
1596 else if (pa_streq(m, "all-no-lfe"))
1597 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1598 else if (pa_streq(m, "all"))
1599 v = PA_CHANNEL_POSITION_MASK_ALL;
1600 else {
1601 pa_channel_position_t p;
1602
1603 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1604 return 0;
1605
1606 v = PA_CHANNEL_POSITION_MASK(p);
1607 }
1608
1609 return v;
1610 }
1611
1612 static int element_parse_override_map(
1613 const char *filename,
1614 unsigned line,
1615 const char *section,
1616 const char *lvalue,
1617 const char *rvalue,
1618 void *data,
1619 void *userdata) {
1620
1621 pa_alsa_path *p = userdata;
1622 pa_alsa_element *e;
1623 const char *state = NULL;
1624 unsigned i = 0;
1625 char *n;
1626
1627 if (!(e = element_get(p, section, TRUE))) {
1628 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1629 return -1;
1630 }
1631
1632 while ((n = pa_split(rvalue, ",", &state))) {
1633 pa_channel_position_mask_t m;
1634
1635 if (!*n)
1636 m = 0;
1637 else {
1638 if ((m = parse_mask(n)) == 0) {
1639 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1640 pa_xfree(n);
1641 return -1;
1642 }
1643 }
1644
1645 if (pa_streq(lvalue, "override-map.1"))
1646 e->masks[i++][0] = m;
1647 else
1648 e->masks[i++][1] = m;
1649
1650 /* Later on we might add override-map.3 and so on here ... */
1651
1652 pa_xfree(n);
1653 }
1654
1655 e->override_map = TRUE;
1656
1657 return 0;
1658 }
1659
1660 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1661 snd_mixer_selem_id_t *sid;
1662 snd_mixer_elem_t *me;
1663 int r;
1664
1665 pa_assert(e);
1666 pa_assert(m);
1667
1668 SELEM_INIT(sid, e->alsa_name);
1669 if (!(me = snd_mixer_find_selem(m, sid))) {
1670 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1671 return -1;
1672 }
1673
1674 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1675
1676 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1677 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1678 else
1679 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1680
1681 if (r < 0)
1682 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1683
1684 } else {
1685 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1686
1687 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1688 pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1689 }
1690
1691 return r;
1692 }
1693
1694 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1695 pa_alsa_option *o;
1696 uint32_t idx;
1697
1698 pa_assert(s);
1699 pa_assert(m);
1700
1701 PA_IDXSET_FOREACH(o, s->options, idx)
1702 element_set_option(o->element, m, o->alsa_idx);
1703
1704 return 0;
1705 }
1706
1707 static int option_verify(pa_alsa_option *o) {
1708 static const struct description_map well_known_descriptions[] = {
1709 { "input", N_("Input") },
1710 { "input-docking", N_("Docking Station Input") },
1711 { "input-docking-microphone", N_("Docking Station Microphone") },
1712 { "input-linein", N_("Line-In") },
1713 { "input-microphone", N_("Microphone") },
1714 { "input-microphone-external", N_("External Microphone") },
1715 { "input-microphone-internal", N_("Internal Microphone") },
1716 { "input-radio", N_("Radio") },
1717 { "input-video", N_("Video") },
1718 { "input-agc-on", N_("Automatic Gain Control") },
1719 { "input-agc-off", "" },
1720 { "input-boost-on", N_("Boost") },
1721 { "input-boost-off", "" },
1722 { "output-amplifier-on", N_("Amplifier") },
1723 { "output-amplifier-off", "" }
1724 };
1725
1726 pa_assert(o);
1727
1728 if (!o->name) {
1729 pa_log("No name set for option %s", o->alsa_name);
1730 return -1;
1731 }
1732
1733 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1734 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1735 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1736 return -1;
1737 }
1738
1739 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1740 !pa_streq(o->alsa_name, "on") &&
1741 !pa_streq(o->alsa_name, "off")) {
1742 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1743 return -1;
1744 }
1745
1746 if (!o->description)
1747 o->description = pa_xstrdup(lookup_description(o->name,
1748 well_known_descriptions,
1749 PA_ELEMENTSOF(well_known_descriptions)));
1750 if (!o->description)
1751 o->description = pa_xstrdup(o->name);
1752
1753 return 0;
1754 }
1755
1756 static int element_verify(pa_alsa_element *e) {
1757 pa_alsa_option *o;
1758
1759 pa_assert(e);
1760
1761 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1762 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1763 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1764 return -1;
1765 }
1766
1767 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1768 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1769 return -1;
1770 }
1771
1772 PA_LLIST_FOREACH(o, e->options)
1773 if (option_verify(o) < 0)
1774 return -1;
1775
1776 return 0;
1777 }
1778
1779 static int path_verify(pa_alsa_path *p) {
1780 static const struct description_map well_known_descriptions[] = {
1781 { "analog-input", N_("Analog Input") },
1782 { "analog-input-microphone", N_("Analog Microphone") },
1783 { "analog-input-linein", N_("Analog Line-In") },
1784 { "analog-input-radio", N_("Analog Radio") },
1785 { "analog-input-video", N_("Analog Video") },
1786 { "analog-output", N_("Analog Output") },
1787 { "analog-output-headphones", N_("Analog Headphones") },
1788 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1789 { "analog-output-mono", N_("Analog Mono Output") }
1790 };
1791
1792 pa_alsa_element *e;
1793
1794 pa_assert(p);
1795
1796 PA_LLIST_FOREACH(e, p->elements)
1797 if (element_verify(e) < 0)
1798 return -1;
1799
1800 if (!p->description)
1801 p->description = pa_xstrdup(lookup_description(p->name,
1802 well_known_descriptions,
1803 PA_ELEMENTSOF(well_known_descriptions)));
1804
1805 if (!p->description)
1806 p->description = pa_xstrdup(p->name);
1807
1808 return 0;
1809 }
1810
1811 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1812 pa_alsa_path *p;
1813 char *fn;
1814 int r;
1815 const char *n;
1816
1817 pa_config_item items[] = {
1818 /* [General] */
1819 { "priority", pa_config_parse_unsigned, NULL, "General" },
1820 { "description", pa_config_parse_string, NULL, "General" },
1821 { "name", pa_config_parse_string, NULL, "General" },
1822
1823 /* [Option ...] */
1824 { "priority", option_parse_priority, NULL, NULL },
1825 { "name", option_parse_name, NULL, NULL },
1826
1827 /* [Element ...] */
1828 { "switch", element_parse_switch, NULL, NULL },
1829 { "volume", element_parse_volume, NULL, NULL },
1830 { "enumeration", element_parse_enumeration, NULL, NULL },
1831 { "override-map.1", element_parse_override_map, NULL, NULL },
1832 { "override-map.2", element_parse_override_map, NULL, NULL },
1833 /* ... later on we might add override-map.3 and so on here ... */
1834 { "required", element_parse_required, NULL, NULL },
1835 { "required-absent", element_parse_required, NULL, NULL },
1836 { "direction", element_parse_direction, NULL, NULL },
1837 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1838 { NULL, NULL, NULL, NULL }
1839 };
1840
1841 pa_assert(fname);
1842
1843 p = pa_xnew0(pa_alsa_path, 1);
1844 n = pa_path_get_filename(fname);
1845 p->name = pa_xstrndup(n, strcspn(n, "."));
1846 p->direction = direction;
1847
1848 items[0].data = &p->priority;
1849 items[1].data = &p->description;
1850 items[2].data = &p->name;
1851
1852 fn = pa_maybe_prefix_path(fname,
1853 #if defined(__linux__) && !defined(__OPTIMIZE__)
1854 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
1855 #endif
1856 PA_ALSA_PATHS_DIR);
1857
1858 r = pa_config_parse(fn, NULL, items, p);
1859 pa_xfree(fn);
1860
1861 if (r < 0)
1862 goto fail;
1863
1864 if (path_verify(p) < 0)
1865 goto fail;
1866
1867 return p;
1868
1869 fail:
1870 pa_alsa_path_free(p);
1871 return NULL;
1872 }
1873
1874 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1875 pa_alsa_path *p;
1876 pa_alsa_element *e;
1877
1878 pa_assert(element);
1879
1880 p = pa_xnew0(pa_alsa_path, 1);
1881 p->name = pa_xstrdup(element);
1882 p->direction = direction;
1883
1884 e = pa_xnew0(pa_alsa_element, 1);
1885 e->path = p;
1886 e->alsa_name = pa_xstrdup(element);
1887 e->direction = direction;
1888
1889 e->switch_use = PA_ALSA_SWITCH_MUTE;
1890 e->volume_use = PA_ALSA_VOLUME_MERGE;
1891
1892 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1893 return p;
1894 }
1895
1896 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1897 pa_alsa_option *o, *n;
1898
1899 pa_assert(e);
1900
1901 for (o = e->options; o; o = n) {
1902 n = o->next;
1903
1904 if (o->alsa_idx < 0) {
1905 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1906 option_free(o);
1907 }
1908 }
1909
1910 return
1911 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1912 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1913 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1914 }
1915
1916 static void path_drop_unsupported(pa_alsa_path *p) {
1917 pa_alsa_element *e, *n;
1918
1919 pa_assert(p);
1920
1921 for (e = p->elements; e; e = n) {
1922 n = e->next;
1923
1924 if (!element_drop_unsupported(e)) {
1925 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1926 element_free(e);
1927 }
1928 }
1929 }
1930
1931 static void path_make_options_unique(pa_alsa_path *p) {
1932 pa_alsa_element *e;
1933 pa_alsa_option *o, *u;
1934
1935 PA_LLIST_FOREACH(e, p->elements) {
1936 PA_LLIST_FOREACH(o, e->options) {
1937 unsigned i;
1938 char *m;
1939
1940 for (u = o->next; u; u = u->next)
1941 if (pa_streq(u->name, o->name))
1942 break;
1943
1944 if (!u)
1945 continue;
1946
1947 m = pa_xstrdup(o->name);
1948
1949 /* OK, this name is not unique, hence let's rename */
1950 for (i = 1, u = o; u; u = u->next) {
1951 char *nn, *nd;
1952
1953 if (!pa_streq(u->name, m))
1954 continue;
1955
1956 nn = pa_sprintf_malloc("%s-%u", m, i);
1957 pa_xfree(u->name);
1958 u->name = nn;
1959
1960 nd = pa_sprintf_malloc("%s %u", u->description, i);
1961 pa_xfree(u->description);
1962 u->description = nd;
1963
1964 i++;
1965 }
1966
1967 pa_xfree(m);
1968 }
1969 }
1970 }
1971
1972 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1973 pa_alsa_option *o;
1974
1975 for (; e; e = e->next)
1976 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1977 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1978 break;
1979
1980 if (!e)
1981 return FALSE;
1982
1983 for (o = e->options; o; o = o->next) {
1984 pa_alsa_setting *s;
1985
1986 if (template) {
1987 s = pa_xnewdup(pa_alsa_setting, template, 1);
1988 s->options = pa_idxset_copy(template->options);
1989 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1990 s->description =
1991 (template->description[0] && o->description[0])
1992 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1993 : (template->description[0]
1994 ? pa_xstrdup(template->description)
1995 : pa_xstrdup(o->description));
1996
1997 s->priority = PA_MAX(template->priority, o->priority);
1998 } else {
1999 s = pa_xnew0(pa_alsa_setting, 1);
2000 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2001 s->name = pa_xstrdup(o->name);
2002 s->description = pa_xstrdup(o->description);
2003 s->priority = o->priority;
2004 }
2005
2006 pa_idxset_put(s->options, o, NULL);
2007
2008 if (element_create_settings(e->next, s))
2009 /* This is not a leaf, so let's get rid of it */
2010 setting_free(s);
2011 else {
2012 /* This is a leaf, so let's add it */
2013 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2014
2015 e->path->last_setting = s;
2016 }
2017 }
2018
2019 return TRUE;
2020 }
2021
2022 static void path_create_settings(pa_alsa_path *p) {
2023 pa_assert(p);
2024
2025 element_create_settings(p->elements, NULL);
2026 }
2027
2028 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2029 pa_alsa_element *e;
2030 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2031 pa_channel_position_t t;
2032
2033 pa_assert(p);
2034 pa_assert(m);
2035
2036 if (p->probed)
2037 return 0;
2038
2039 pa_zero(min_dB);
2040 pa_zero(max_dB);
2041
2042 pa_log_debug("Probing path '%s'", p->name);
2043
2044 PA_LLIST_FOREACH(e, p->elements) {
2045 if (element_probe(e, m) < 0) {
2046 p->supported = FALSE;
2047 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2048 return -1;
2049 }
2050
2051 if (ignore_dB)
2052 e->has_dB = FALSE;
2053
2054 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2055
2056 if (!p->has_volume) {
2057 p->min_volume = e->min_volume;
2058 p->max_volume = e->max_volume;
2059 }
2060
2061 if (e->has_dB) {
2062 if (!p->has_volume) {
2063 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2064 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2065 min_dB[t] = e->min_dB;
2066 max_dB[t] = e->max_dB;
2067 }
2068
2069 p->has_dB = TRUE;
2070 } else {
2071
2072 if (p->has_dB) {
2073 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2074 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2075 min_dB[t] += e->min_dB;
2076 max_dB[t] += e->max_dB;
2077 }
2078 } else
2079 /* Hmm, there's another element before us
2080 * which cannot do dB volumes, so we we need
2081 * to 'neutralize' this slider */
2082 e->volume_use = PA_ALSA_VOLUME_ZERO;
2083 }
2084 } else if (p->has_volume)
2085 /* We can't use this volume, so let's ignore it */
2086 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2087
2088 p->has_volume = TRUE;
2089 }
2090
2091 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2092 p->has_mute = TRUE;
2093 }
2094
2095 path_drop_unsupported(p);
2096 path_make_options_unique(p);
2097 path_create_settings(p);
2098
2099 p->supported = TRUE;
2100 p->probed = TRUE;
2101
2102 p->min_dB = INFINITY;
2103 p->max_dB = -INFINITY;
2104
2105 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2106 if (p->min_dB > min_dB[t])
2107 p->min_dB = min_dB[t];
2108
2109 if (p->max_dB < max_dB[t])
2110 p->max_dB = max_dB[t];
2111 }
2112
2113 return 0;
2114 }
2115
2116 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2117 pa_assert(s);
2118
2119 pa_log_debug("Setting %s (%s) priority=%u",
2120 s->name,
2121 pa_strnull(s->description),
2122 s->priority);
2123 }
2124
2125 void pa_alsa_option_dump(pa_alsa_option *o) {
2126 pa_assert(o);
2127
2128 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2129 o->alsa_name,
2130 pa_strnull(o->name),
2131 pa_strnull(o->description),
2132 o->alsa_idx,
2133 o->priority);
2134 }
2135
2136 void pa_alsa_element_dump(pa_alsa_element *e) {
2137 pa_alsa_option *o;
2138 pa_assert(e);
2139
2140 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2141 e->alsa_name,
2142 e->direction,
2143 e->switch_use,
2144 e->volume_use,
2145 e->enumeration_use,
2146 e->required,
2147 e->required_absent,
2148 (long long unsigned) e->merged_mask,
2149 e->n_channels,
2150 pa_yes_no(e->override_map));
2151
2152 PA_LLIST_FOREACH(o, e->options)
2153 pa_alsa_option_dump(o);
2154 }
2155
2156 void pa_alsa_path_dump(pa_alsa_path *p) {
2157 pa_alsa_element *e;
2158 pa_alsa_setting *s;
2159 pa_assert(p);
2160
2161 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2162 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2163 p->name,
2164 pa_strnull(p->description),
2165 p->direction,
2166 p->priority,
2167 pa_yes_no(p->probed),
2168 pa_yes_no(p->supported),
2169 pa_yes_no(p->has_mute),
2170 pa_yes_no(p->has_volume),
2171 pa_yes_no(p->has_dB),
2172 p->min_volume, p->max_volume,
2173 p->min_dB, p->max_dB);
2174
2175 PA_LLIST_FOREACH(e, p->elements)
2176 pa_alsa_element_dump(e);
2177
2178 PA_LLIST_FOREACH(s, p->settings)
2179 pa_alsa_setting_dump(s);
2180 }
2181
2182 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2183 snd_mixer_selem_id_t *sid;
2184 snd_mixer_elem_t *me;
2185
2186 pa_assert(e);
2187 pa_assert(m);
2188 pa_assert(cb);
2189
2190 SELEM_INIT(sid, e->alsa_name);
2191 if (!(me = snd_mixer_find_selem(m, sid))) {
2192 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2193 return;
2194 }
2195
2196 snd_mixer_elem_set_callback(me, cb);
2197 snd_mixer_elem_set_callback_private(me, userdata);
2198 }
2199
2200 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2201 pa_alsa_element *e;
2202
2203 pa_assert(p);
2204 pa_assert(m);
2205 pa_assert(cb);
2206
2207 PA_LLIST_FOREACH(e, p->elements)
2208 element_set_callback(e, m, cb, userdata);
2209 }
2210
2211 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2212 pa_alsa_path *p;
2213
2214 pa_assert(ps);
2215 pa_assert(m);
2216 pa_assert(cb);
2217
2218 PA_LLIST_FOREACH(p, ps->paths)
2219 pa_alsa_path_set_callback(p, m, cb, userdata);
2220 }
2221
2222 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2223 pa_alsa_path_set *ps;
2224 char **pn = NULL, **en = NULL, **ie;
2225
2226 pa_assert(m);
2227 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2228
2229 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2230 return NULL;
2231
2232 ps = pa_xnew0(pa_alsa_path_set, 1);
2233 ps->direction = direction;
2234
2235 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2236 pn = m->output_path_names;
2237 else if (direction == PA_ALSA_DIRECTION_INPUT)
2238 pn = m->input_path_names;
2239
2240 if (pn) {
2241 char **in;
2242
2243 for (in = pn; *in; in++) {
2244 pa_alsa_path *p;
2245 pa_bool_t duplicate = FALSE;
2246 char **kn, *fn;
2247
2248 for (kn = pn; kn != in; kn++)
2249 if (pa_streq(*kn, *in)) {
2250 duplicate = TRUE;
2251 break;
2252 }
2253
2254 if (duplicate)
2255 continue;
2256
2257 fn = pa_sprintf_malloc("%s.conf", *in);
2258
2259 if ((p = pa_alsa_path_new(fn, direction))) {
2260 p->path_set = ps;
2261 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2262 ps->last_path = p;
2263 }
2264
2265 pa_xfree(fn);
2266 }
2267
2268 return ps;
2269 }
2270
2271 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2272 en = m->output_element;
2273 else if (direction == PA_ALSA_DIRECTION_INPUT)
2274 en = m->input_element;
2275
2276 if (!en) {
2277 pa_alsa_path_set_free(ps);
2278 return NULL;
2279 }
2280
2281 for (ie = en; *ie; ie++) {
2282 char **je;
2283 pa_alsa_path *p;
2284
2285 p = pa_alsa_path_synthesize(*ie, direction);
2286 p->path_set = ps;
2287
2288 /* Mark all other passed elements for require-absent */
2289 for (je = en; *je; je++) {
2290 pa_alsa_element *e;
2291 e = pa_xnew0(pa_alsa_element, 1);
2292 e->path = p;
2293 e->alsa_name = pa_xstrdup(*je);
2294 e->direction = direction;
2295 e->required_absent = PA_ALSA_REQUIRED_ANY;
2296
2297 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2298 p->last_element = e;
2299 }
2300
2301 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2302 ps->last_path = p;
2303 }
2304
2305 return ps;
2306 }
2307
2308 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2309 pa_alsa_path *p;
2310 pa_assert(ps);
2311
2312 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2313 (void*) ps,
2314 ps->direction,
2315 pa_yes_no(ps->probed));
2316
2317 PA_LLIST_FOREACH(p, ps->paths)
2318 pa_alsa_path_dump(p);
2319 }
2320
2321 static void path_set_unify(pa_alsa_path_set *ps) {
2322 pa_alsa_path *p;
2323 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2324 pa_assert(ps);
2325
2326 /* We have issues dealing with paths that vary too wildly. That
2327 * means for now we have to have all paths support volume/mute/dB
2328 * or none. */
2329
2330 PA_LLIST_FOREACH(p, ps->paths) {
2331 pa_assert(p->probed);
2332
2333 if (!p->has_volume)
2334 has_volume = FALSE;
2335 else if (!p->has_dB)
2336 has_dB = FALSE;
2337
2338 if (!p->has_mute)
2339 has_mute = FALSE;
2340 }
2341
2342 if (!has_volume || !has_dB || !has_mute) {
2343
2344 if (!has_volume)
2345 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2346 else if (!has_dB)
2347 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2348
2349 if (!has_mute)
2350 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2351
2352 PA_LLIST_FOREACH(p, ps->paths) {
2353 if (!has_volume)
2354 p->has_volume = FALSE;
2355 else if (!has_dB)
2356 p->has_dB = FALSE;
2357
2358 if (!has_mute)
2359 p->has_mute = FALSE;
2360 }
2361 }
2362 }
2363
2364 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2365 pa_alsa_path *p, *q;
2366
2367 PA_LLIST_FOREACH(p, ps->paths) {
2368 unsigned i;
2369 char *m;
2370
2371 for (q = p->next; q; q = q->next)
2372 if (pa_streq(q->name, p->name))
2373 break;
2374
2375 if (!q)
2376 continue;
2377
2378 m = pa_xstrdup(p->name);
2379
2380 /* OK, this name is not unique, hence let's rename */
2381 for (i = 1, q = p; q; q = q->next) {
2382 char *nn, *nd;
2383
2384 if (!pa_streq(q->name, m))
2385 continue;
2386
2387 nn = pa_sprintf_malloc("%s-%u", m, i);
2388 pa_xfree(q->name);
2389 q->name = nn;
2390
2391 nd = pa_sprintf_malloc("%s %u", q->description, i);
2392 pa_xfree(q->description);
2393 q->description = nd;
2394
2395 i++;
2396 }
2397
2398 pa_xfree(m);
2399 }
2400 }
2401
2402 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2403 pa_alsa_path *p, *n;
2404
2405 pa_assert(ps);
2406
2407 if (ps->probed)
2408 return;
2409
2410 for (p = ps->paths; p; p = n) {
2411 n = p->next;
2412
2413 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2414 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2415 pa_alsa_path_free(p);
2416 }
2417 }
2418
2419 path_set_unify(ps);
2420 path_set_make_paths_unique(ps);
2421 ps->probed = TRUE;
2422 }
2423
2424 static void mapping_free(pa_alsa_mapping *m) {
2425 pa_assert(m);
2426
2427 pa_xfree(m->name);
2428 pa_xfree(m->description);
2429
2430 pa_xstrfreev(m->device_strings);
2431 pa_xstrfreev(m->input_path_names);
2432 pa_xstrfreev(m->output_path_names);
2433 pa_xstrfreev(m->input_element);
2434 pa_xstrfreev(m->output_element);
2435
2436 pa_assert(!m->input_pcm);
2437 pa_assert(!m->output_pcm);
2438
2439 pa_xfree(m);
2440 }
2441
2442 static void profile_free(pa_alsa_profile *p) {
2443 pa_assert(p);
2444
2445 pa_xfree(p->name);
2446 pa_xfree(p->description);
2447
2448 pa_xstrfreev(p->input_mapping_names);
2449 pa_xstrfreev(p->output_mapping_names);
2450
2451 if (p->input_mappings)
2452 pa_idxset_free(p->input_mappings, NULL, NULL);
2453
2454 if (p->output_mappings)
2455 pa_idxset_free(p->output_mappings, NULL, NULL);
2456
2457 pa_xfree(p);
2458 }
2459
2460 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2461 pa_assert(ps);
2462
2463 if (ps->profiles) {
2464 pa_alsa_profile *p;
2465
2466 while ((p = pa_hashmap_steal_first(ps->profiles)))
2467 profile_free(p);
2468
2469 pa_hashmap_free(ps->profiles, NULL, NULL);
2470 }
2471
2472 if (ps->mappings) {
2473 pa_alsa_mapping *m;
2474
2475 while ((m = pa_hashmap_steal_first(ps->mappings)))
2476 mapping_free(m);
2477
2478 pa_hashmap_free(ps->mappings, NULL, NULL);
2479 }
2480
2481 pa_xfree(ps);
2482 }
2483
2484 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2485 pa_alsa_mapping *m;
2486
2487 if (!pa_startswith(name, "Mapping "))
2488 return NULL;
2489
2490 name += 8;
2491
2492 if ((m = pa_hashmap_get(ps->mappings, name)))
2493 return m;
2494
2495 m = pa_xnew0(pa_alsa_mapping, 1);
2496 m->profile_set = ps;
2497 m->name = pa_xstrdup(name);
2498 pa_channel_map_init(&m->channel_map);
2499
2500 pa_hashmap_put(ps->mappings, m->name, m);
2501
2502 return m;
2503 }
2504
2505 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2506 pa_alsa_profile *p;
2507
2508 if (!pa_startswith(name, "Profile "))
2509 return NULL;
2510
2511 name += 8;
2512
2513 if ((p = pa_hashmap_get(ps->profiles, name)))
2514 return p;
2515
2516 p = pa_xnew0(pa_alsa_profile, 1);
2517 p->profile_set = ps;
2518 p->name = pa_xstrdup(name);
2519
2520 pa_hashmap_put(ps->profiles, p->name, p);
2521
2522 return p;
2523 }
2524
2525 static int mapping_parse_device_strings(
2526 const char *filename,
2527 unsigned line,
2528 const char *section,
2529 const char *lvalue,
2530 const char *rvalue,
2531 void *data,
2532 void *userdata) {
2533
2534 pa_alsa_profile_set *ps = userdata;
2535 pa_alsa_mapping *m;
2536
2537 pa_assert(ps);
2538
2539 if (!(m = mapping_get(ps, section))) {
2540 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2541 return -1;
2542 }
2543
2544 pa_xstrfreev(m->device_strings);
2545 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2546 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2547 return -1;
2548 }
2549
2550 return 0;
2551 }
2552
2553 static int mapping_parse_channel_map(
2554 const char *filename,
2555 unsigned line,
2556 const char *section,
2557 const char *lvalue,
2558 const char *rvalue,
2559 void *data,
2560 void *userdata) {
2561
2562 pa_alsa_profile_set *ps = userdata;
2563 pa_alsa_mapping *m;
2564
2565 pa_assert(ps);
2566
2567 if (!(m = mapping_get(ps, section))) {
2568 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2569 return -1;
2570 }
2571
2572 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2573 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2574 return -1;
2575 }
2576
2577 return 0;
2578 }
2579
2580 static int mapping_parse_paths(
2581 const char *filename,
2582 unsigned line,
2583 const char *section,
2584 const char *lvalue,
2585 const char *rvalue,
2586 void *data,
2587 void *userdata) {
2588
2589 pa_alsa_profile_set *ps = userdata;
2590 pa_alsa_mapping *m;
2591
2592 pa_assert(ps);
2593
2594 if (!(m = mapping_get(ps, section))) {
2595 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2596 return -1;
2597 }
2598
2599 if (pa_streq(lvalue, "paths-input")) {
2600 pa_xstrfreev(m->input_path_names);
2601 m->input_path_names = pa_split_spaces_strv(rvalue);
2602 } else {
2603 pa_xstrfreev(m->output_path_names);
2604 m->output_path_names = pa_split_spaces_strv(rvalue);
2605 }
2606
2607 return 0;
2608 }
2609
2610 static int mapping_parse_element(
2611 const char *filename,
2612 unsigned line,
2613 const char *section,
2614 const char *lvalue,
2615 const char *rvalue,
2616 void *data,
2617 void *userdata) {
2618
2619 pa_alsa_profile_set *ps = userdata;
2620 pa_alsa_mapping *m;
2621
2622 pa_assert(ps);
2623
2624 if (!(m = mapping_get(ps, section))) {
2625 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2626 return -1;
2627 }
2628
2629 if (pa_streq(lvalue, "element-input")) {
2630 pa_xstrfreev(m->input_element);
2631 m->input_element = pa_split_spaces_strv(rvalue);
2632 } else {
2633 pa_xstrfreev(m->output_element);
2634 m->output_element = pa_split_spaces_strv(rvalue);
2635 }
2636
2637 return 0;
2638 }
2639
2640 static int mapping_parse_direction(
2641 const char *filename,
2642 unsigned line,
2643 const char *section,
2644 const char *lvalue,
2645 const char *rvalue,
2646 void *data,
2647 void *userdata) {
2648
2649 pa_alsa_profile_set *ps = userdata;
2650 pa_alsa_mapping *m;
2651
2652 pa_assert(ps);
2653
2654 if (!(m = mapping_get(ps, section))) {
2655 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2656 return -1;
2657 }
2658
2659 if (pa_streq(rvalue, "input"))
2660 m->direction = PA_ALSA_DIRECTION_INPUT;
2661 else if (pa_streq(rvalue, "output"))
2662 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2663 else if (pa_streq(rvalue, "any"))
2664 m->direction = PA_ALSA_DIRECTION_ANY;
2665 else {
2666 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2667 return -1;
2668 }
2669
2670 return 0;
2671 }
2672
2673 static int mapping_parse_description(
2674 const char *filename,
2675 unsigned line,
2676 const char *section,
2677 const char *lvalue,
2678 const char *rvalue,
2679 void *data,
2680 void *userdata) {
2681
2682 pa_alsa_profile_set *ps = userdata;
2683 pa_alsa_profile *p;
2684 pa_alsa_mapping *m;
2685
2686 pa_assert(ps);
2687
2688 if ((m = mapping_get(ps, section))) {
2689 pa_xstrdup(m->description);
2690 m->description = pa_xstrdup(rvalue);
2691 } else if ((p = profile_get(ps, section))) {
2692 pa_xfree(p->description);
2693 p->description = pa_xstrdup(rvalue);
2694 } else {
2695 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2696 return -1;
2697 }
2698
2699 return 0;
2700 }
2701
2702 static int mapping_parse_priority(
2703 const char *filename,
2704 unsigned line,
2705 const char *section,
2706 const char *lvalue,
2707 const char *rvalue,
2708 void *data,
2709 void *userdata) {
2710
2711 pa_alsa_profile_set *ps = userdata;
2712 pa_alsa_profile *p;
2713 pa_alsa_mapping *m;
2714 uint32_t prio;
2715
2716 pa_assert(ps);
2717
2718 if (pa_atou(rvalue, &prio) < 0) {
2719 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2720 return -1;
2721 }
2722
2723 if ((m = mapping_get(ps, section)))
2724 m->priority = prio;
2725 else if ((p = profile_get(ps, section)))
2726 p->priority = prio;
2727 else {
2728 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2729 return -1;
2730 }
2731
2732 return 0;
2733 }
2734
2735 static int profile_parse_mappings(
2736 const char *filename,
2737 unsigned line,
2738 const char *section,
2739 const char *lvalue,
2740 const char *rvalue,
2741 void *data,
2742 void *userdata) {
2743
2744 pa_alsa_profile_set *ps = userdata;
2745 pa_alsa_profile *p;
2746
2747 pa_assert(ps);
2748
2749 if (!(p = profile_get(ps, section))) {
2750 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2751 return -1;
2752 }
2753
2754 if (pa_streq(lvalue, "input-mappings")) {
2755 pa_xstrfreev(p->input_mapping_names);
2756 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2757 } else {
2758 pa_xstrfreev(p->output_mapping_names);
2759 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2760 }
2761
2762 return 0;
2763 }
2764
2765 static int profile_parse_skip_probe(
2766 const char *filename,
2767 unsigned line,
2768 const char *section,
2769 const char *lvalue,
2770 const char *rvalue,
2771 void *data,
2772 void *userdata) {
2773
2774 pa_alsa_profile_set *ps = userdata;
2775 pa_alsa_profile *p;
2776 int b;
2777
2778 pa_assert(ps);
2779
2780 if (!(p = profile_get(ps, section))) {
2781 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2782 return -1;
2783 }
2784
2785 if ((b = pa_parse_boolean(rvalue)) < 0) {
2786 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2787 return -1;
2788 }
2789
2790 p->supported = b;
2791
2792 return 0;
2793 }
2794
2795 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2796
2797 static const struct description_map well_known_descriptions[] = {
2798 { "analog-mono", N_("Analog Mono") },
2799 { "analog-stereo", N_("Analog Stereo") },
2800 { "analog-surround-21", N_("Analog Surround 2.1") },
2801 { "analog-surround-30", N_("Analog Surround 3.0") },
2802 { "analog-surround-31", N_("Analog Surround 3.1") },
2803 { "analog-surround-40", N_("Analog Surround 4.0") },
2804 { "analog-surround-41", N_("Analog Surround 4.1") },
2805 { "analog-surround-50", N_("Analog Surround 5.0") },
2806 { "analog-surround-51", N_("Analog Surround 5.1") },
2807 { "analog-surround-61", N_("Analog Surround 6.0") },
2808 { "analog-surround-61", N_("Analog Surround 6.1") },
2809 { "analog-surround-70", N_("Analog Surround 7.0") },
2810 { "analog-surround-71", N_("Analog Surround 7.1") },
2811 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2812 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2813 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2814 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2815 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2816 };
2817
2818 pa_assert(m);
2819
2820 if (!pa_channel_map_valid(&m->channel_map)) {
2821 pa_log("Mapping %s is missing channel map.", m->name);
2822 return -1;
2823 }
2824
2825 if (!m->device_strings) {
2826 pa_log("Mapping %s is missing device strings.", m->name);
2827 return -1;
2828 }
2829
2830 if ((m->input_path_names && m->input_element) ||
2831 (m->output_path_names && m->output_element)) {
2832 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2833 return -1;
2834 }
2835
2836 if (!m->description)
2837 m->description = pa_xstrdup(lookup_description(m->name,
2838 well_known_descriptions,
2839 PA_ELEMENTSOF(well_known_descriptions)));
2840
2841 if (!m->description)
2842 m->description = pa_xstrdup(m->name);
2843
2844 if (bonus) {
2845 if (pa_channel_map_equal(&m->channel_map, bonus))
2846 m->priority += 50;
2847 else if (m->channel_map.channels == bonus->channels)
2848 m->priority += 30;
2849 }
2850
2851 return 0;
2852 }
2853
2854 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2855 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2856
2857 pa_assert(m);
2858
2859 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2860 m->name,
2861 pa_strnull(m->description),
2862 m->priority,
2863 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2864 pa_yes_no(m->supported),
2865 m->direction);
2866 }
2867
2868 static void profile_set_add_auto_pair(
2869 pa_alsa_profile_set *ps,
2870 pa_alsa_mapping *m, /* output */
2871 pa_alsa_mapping *n /* input */) {
2872
2873 char *name;
2874 pa_alsa_profile *p;
2875
2876 pa_assert(ps);
2877 pa_assert(m || n);
2878
2879 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2880 return;
2881
2882 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2883 return;
2884
2885 if (m && n)
2886 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2887 else if (m)
2888 name = pa_sprintf_malloc("output:%s", m->name);
2889 else
2890 name = pa_sprintf_malloc("input:%s", n->name);
2891
2892 if (pa_hashmap_get(ps->profiles, name)) {
2893 pa_xfree(name);
2894 return;
2895 }
2896
2897 p = pa_xnew0(pa_alsa_profile, 1);
2898 p->profile_set = ps;
2899 p->name = name;
2900
2901 if (m) {
2902 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2903 pa_idxset_put(p->output_mappings, m, NULL);
2904 p->priority += m->priority * 100;
2905 }
2906
2907 if (n) {
2908 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2909 pa_idxset_put(p->input_mappings, n, NULL);
2910 p->priority += n->priority;
2911 }
2912
2913 pa_hashmap_put(ps->profiles, p->name, p);
2914 }
2915
2916 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2917 pa_alsa_mapping *m, *n;
2918 void *m_state, *n_state;
2919
2920 pa_assert(ps);
2921
2922 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2923 profile_set_add_auto_pair(ps, m, NULL);
2924
2925 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2926 profile_set_add_auto_pair(ps, m, n);
2927 }
2928
2929 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2930 profile_set_add_auto_pair(ps, NULL, n);
2931 }
2932
2933 static int profile_verify(pa_alsa_profile *p) {
2934
2935 static const struct description_map well_known_descriptions[] = {
2936 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2937 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2938 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2939 { "off", N_("Off") }
2940 };
2941
2942 pa_assert(p);
2943
2944 /* Replace the output mapping names by the actual mappings */
2945 if (p->output_mapping_names) {
2946 char **name;
2947
2948 pa_assert(!p->output_mappings);
2949 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2950
2951 for (name = p->output_mapping_names; *name; name++) {
2952 pa_alsa_mapping *m;
2953 char **in;
2954 pa_bool_t duplicate = FALSE;
2955
2956 for (in = name + 1; *in; in++)
2957 if (pa_streq(*name, *in)) {
2958 duplicate = TRUE;
2959 break;
2960 }
2961
2962 if (duplicate)
2963 continue;
2964
2965 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2966 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2967 return -1;
2968 }
2969
2970 pa_idxset_put(p->output_mappings, m, NULL);
2971
2972 if (p->supported)
2973 m->supported++;
2974 }
2975
2976 pa_xstrfreev(p->output_mapping_names);
2977 p->output_mapping_names = NULL;
2978 }
2979
2980 /* Replace the input mapping names by the actual mappings */
2981 if (p->input_mapping_names) {
2982 char **name;
2983
2984 pa_assert(!p->input_mappings);
2985 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2986
2987 for (name = p->input_mapping_names; *name; name++) {
2988 pa_alsa_mapping *m;
2989 char **in;
2990 pa_bool_t duplicate = FALSE;
2991
2992 for (in = name + 1; *in; in++)
2993 if (pa_streq(*name, *in)) {
2994 duplicate = TRUE;
2995 break;
2996 }
2997
2998 if (duplicate)
2999 continue;
3000
3001 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3002 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3003 return -1;
3004 }
3005
3006 pa_idxset_put(p->input_mappings, m, NULL);
3007
3008 if (p->supported)
3009 m->supported++;
3010 }
3011
3012 pa_xstrfreev(p->input_mapping_names);
3013 p->input_mapping_names = NULL;
3014 }
3015
3016 if (!p->input_mappings && !p->output_mappings) {
3017 pa_log("Profile '%s' lacks mappings.", p->name);
3018 return -1;
3019 }
3020
3021 if (!p->description)
3022 p->description = pa_xstrdup(lookup_description(p->name,
3023 well_known_descriptions,
3024 PA_ELEMENTSOF(well_known_descriptions)));
3025
3026 if (!p->description) {
3027 pa_strbuf *sb;
3028 uint32_t idx;
3029 pa_alsa_mapping *m;
3030
3031 sb = pa_strbuf_new();
3032
3033 if (p->output_mappings)
3034 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3035 if (!pa_strbuf_isempty(sb))
3036 pa_strbuf_puts(sb, " + ");
3037
3038 pa_strbuf_printf(sb, "%s Output", m->description);
3039 }
3040
3041 if (p->input_mappings)
3042 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3043 if (!pa_strbuf_isempty(sb))
3044 pa_strbuf_puts(sb, " + ");
3045
3046 pa_strbuf_printf(sb, "%s Input", m->description);
3047 }
3048
3049 p->description = pa_strbuf_tostring_free(sb);
3050 }
3051
3052 return 0;
3053 }
3054
3055 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3056 uint32_t idx;
3057 pa_alsa_mapping *m;
3058 pa_assert(p);
3059
3060 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3061 p->name,
3062 pa_strnull(p->description),
3063 p->priority,
3064 pa_yes_no(p->supported),
3065 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3066 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3067
3068 if (p->input_mappings)
3069 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3070 pa_log_debug("Input %s", m->name);
3071
3072 if (p->output_mappings)
3073 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3074 pa_log_debug("Output %s", m->name);
3075 }
3076
3077 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3078 pa_alsa_profile_set *ps;
3079 pa_alsa_profile *p;
3080 pa_alsa_mapping *m;
3081 char *fn;
3082 int r;
3083 void *state;
3084
3085 static pa_config_item items[] = {
3086 /* [General] */
3087 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3088
3089 /* [Mapping ...] */
3090 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3091 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3092 { "paths-input", mapping_parse_paths, NULL, NULL },
3093 { "paths-output", mapping_parse_paths, NULL, NULL },
3094 { "element-input", mapping_parse_element, NULL, NULL },
3095 { "element-output", mapping_parse_element, NULL, NULL },
3096 { "direction", mapping_parse_direction, NULL, NULL },
3097
3098 /* Shared by [Mapping ...] and [Profile ...] */
3099 { "description", mapping_parse_description, NULL, NULL },
3100 { "priority", mapping_parse_priority, NULL, NULL },
3101
3102 /* [Profile ...] */
3103 { "input-mappings", profile_parse_mappings, NULL, NULL },
3104 { "output-mappings", profile_parse_mappings, NULL, NULL },
3105 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3106 { NULL, NULL, NULL, NULL }
3107 };
3108
3109 ps = pa_xnew0(pa_alsa_profile_set, 1);
3110 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3111 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3112
3113 items[0].data = &ps->auto_profiles;
3114
3115 if (!fname)
3116 fname = "default.conf";
3117
3118 fn = pa_maybe_prefix_path(fname,
3119 #if defined(__linux__) && !defined(__OPTIMIZE__)
3120 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3121 #endif
3122 PA_ALSA_PROFILE_SETS_DIR);
3123
3124 r = pa_config_parse(fn, NULL, items, ps);
3125 pa_xfree(fn);
3126
3127 if (r < 0)
3128 goto fail;
3129
3130 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3131 if (mapping_verify(m, bonus) < 0)
3132 goto fail;
3133
3134 if (ps->auto_profiles)
3135 profile_set_add_auto(ps);
3136
3137 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3138 if (profile_verify(p) < 0)
3139 goto fail;
3140
3141 return ps;
3142
3143 fail:
3144 pa_alsa_profile_set_free(ps);
3145 return NULL;
3146 }
3147
3148 void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
3149 void *state;
3150 pa_alsa_profile *p, *last = NULL;
3151 pa_alsa_mapping *m;
3152
3153 pa_assert(ps);
3154 pa_assert(dev_id);
3155 pa_assert(ss);
3156
3157 if (ps->probed)
3158 return;
3159
3160 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3161 pa_sample_spec try_ss;
3162 pa_channel_map try_map;
3163 uint32_t idx;
3164
3165 /* Is this already marked that it is supported? (i.e. from the config file) */
3166 if (p->supported)
3167 continue;
3168
3169 pa_log_debug("Looking at profile %s", p->name);
3170
3171 /* Close PCMs from the last iteration we don't need anymore */
3172 if (last && last->output_mappings)
3173 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3174
3175 if (!m->output_pcm)
3176 break;
3177
3178 if (last->supported)
3179 m->supported++;
3180
3181 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3182 snd_pcm_close(m->output_pcm);
3183 m->output_pcm = NULL;
3184 }
3185 }
3186
3187 if (last && last->input_mappings)
3188 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3189
3190 if (!m->input_pcm)
3191 break;
3192
3193 if (last->supported)
3194 m->supported++;
3195
3196 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3197 snd_pcm_close(m->input_pcm);
3198 m->input_pcm = NULL;
3199 }
3200 }
3201
3202 p->supported = TRUE;
3203
3204 /* Check if we can open all new ones */
3205 if (p->output_mappings)
3206 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3207
3208 if (m->output_pcm)
3209 continue;
3210
3211 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3212 try_map = m->channel_map;
3213 try_ss = *ss;
3214 try_ss.channels = try_map.channels;
3215
3216 if (!(m ->output_pcm = pa_alsa_open_by_template(
3217 m->device_strings,
3218 dev_id,
3219 NULL,
3220 &try_ss, &try_map,
3221 SND_PCM_STREAM_PLAYBACK,
3222 NULL, NULL, 0, NULL, NULL,
3223 TRUE))) {
3224 p->supported = FALSE;
3225 break;
3226 }
3227 }
3228
3229 if (p->input_mappings && p->supported)
3230 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3231
3232 if (m->input_pcm)
3233 continue;
3234
3235 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3236 try_map = m->channel_map;
3237 try_ss = *ss;
3238 try_ss.channels = try_map.channels;
3239
3240 if (!(m ->input_pcm = pa_alsa_open_by_template(
3241 m->device_strings,
3242 dev_id,
3243 NULL,
3244 &try_ss, &try_map,
3245 SND_PCM_STREAM_CAPTURE,
3246 NULL, NULL, 0, NULL, NULL,
3247 TRUE))) {
3248 p->supported = FALSE;
3249 break;
3250 }
3251 }
3252
3253 last = p;
3254
3255 if (p->supported)
3256 pa_log_debug("Profile %s supported.", p->name);
3257 }
3258
3259 /* Clean up */
3260 if (last) {
3261 uint32_t idx;
3262
3263 if (last->output_mappings)
3264 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3265 if (m->output_pcm) {
3266
3267 if (last->supported)
3268 m->supported++;
3269
3270 snd_pcm_close(m->output_pcm);
3271 m->output_pcm = NULL;
3272 }
3273
3274 if (last->input_mappings)
3275 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3276 if (m->input_pcm) {
3277
3278 if (last->supported)
3279 m->supported++;
3280
3281 snd_pcm_close(m->input_pcm);
3282 m->input_pcm = NULL;
3283 }
3284 }
3285
3286 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3287 if (!p->supported) {
3288 pa_hashmap_remove(ps->profiles, p->name);
3289 profile_free(p);
3290 }
3291
3292 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3293 if (m->supported <= 0) {
3294 pa_hashmap_remove(ps->mappings, m->name);
3295 mapping_free(m);
3296 }
3297
3298 ps->probed = TRUE;
3299 }
3300
3301 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3302 pa_alsa_profile *p;
3303 pa_alsa_mapping *m;
3304 void *state;
3305
3306 pa_assert(ps);
3307
3308 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3309 (void*)
3310 ps,
3311 pa_yes_no(ps->auto_profiles),
3312 pa_yes_no(ps->probed),
3313 pa_hashmap_size(ps->mappings),
3314 pa_hashmap_size(ps->profiles));
3315
3316 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3317 pa_alsa_mapping_dump(m);
3318
3319 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3320 pa_alsa_profile_dump(p);
3321 }
3322
3323 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3324 pa_alsa_path *path;
3325
3326 pa_assert(p);
3327 pa_assert(!*p);
3328 pa_assert(ps);
3329
3330 /* if there is no path, we don't want a port list */
3331 if (!ps->paths)
3332 return;
3333
3334 if (!ps->paths->next){
3335 pa_alsa_setting *s;
3336
3337 /* If there is only one path, but no or only one setting, then
3338 * we want a port list either */
3339 if (!ps->paths->settings || !ps->paths->settings->next)
3340 return;
3341
3342 /* Ok, there is only one path, however with multiple settings,
3343 * so let's create a port for each setting */
3344 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3345
3346 PA_LLIST_FOREACH(s, ps->paths->settings) {
3347 pa_device_port *port;
3348 pa_alsa_port_data *data;
3349
3350 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3351 port->priority = s->priority;
3352
3353 data = PA_DEVICE_PORT_DATA(port);
3354 data->path = ps->paths;
3355 data->setting = s;
3356
3357 pa_hashmap_put(*p, port->name, port);
3358 }
3359
3360 } else {
3361
3362 /* We have multiple paths, so let's create a port for each
3363 * one, and each of each settings */
3364 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3365
3366 PA_LLIST_FOREACH(path, ps->paths) {
3367
3368 if (!path->settings || !path->settings->next) {
3369 pa_device_port *port;
3370 pa_alsa_port_data *data;
3371
3372 /* If there is no or just one setting we only need a
3373 * single entry */
3374
3375 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3376 port->priority = path->priority * 100;
3377
3378
3379 data = PA_DEVICE_PORT_DATA(port);
3380 data->path = path;
3381 data->setting = path->settings;
3382
3383 pa_hashmap_put(*p, port->name, port);
3384 } else {
3385 pa_alsa_setting *s;
3386
3387 PA_LLIST_FOREACH(s, path->settings) {
3388 pa_device_port *port;
3389 pa_alsa_port_data *data;
3390 char *n, *d;
3391
3392 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3393
3394 if (s->description[0])
3395 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3396 else
3397 d = pa_xstrdup(path->description);
3398
3399 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3400 port->priority = path->priority * 100 + s->priority;
3401
3402 pa_xfree(n);
3403 pa_xfree(d);
3404
3405 data = PA_DEVICE_PORT_DATA(port);
3406 data->path = path;
3407 data->setting = s;
3408
3409 pa_hashmap_put(*p, port->name, port);
3410 }
3411 }
3412 }
3413 }
3414
3415 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3416 }