]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
[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;
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, PA_ALSA_PATHS_DIR);
1853 r = pa_config_parse(fn, NULL, items, p);
1854 pa_xfree(fn);
1855
1856 if (r < 0)
1857 goto fail;
1858
1859 if (path_verify(p) < 0)
1860 goto fail;
1861
1862 return p;
1863
1864 fail:
1865 pa_alsa_path_free(p);
1866 return NULL;
1867 }
1868
1869 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1870 pa_alsa_path *p;
1871 pa_alsa_element *e;
1872
1873 pa_assert(element);
1874
1875 p = pa_xnew0(pa_alsa_path, 1);
1876 p->name = pa_xstrdup(element);
1877 p->direction = direction;
1878
1879 e = pa_xnew0(pa_alsa_element, 1);
1880 e->path = p;
1881 e->alsa_name = pa_xstrdup(element);
1882 e->direction = direction;
1883
1884 e->switch_use = PA_ALSA_SWITCH_MUTE;
1885 e->volume_use = PA_ALSA_VOLUME_MERGE;
1886
1887 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1888 return p;
1889 }
1890
1891 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1892 pa_alsa_option *o, *n;
1893
1894 pa_assert(e);
1895
1896 for (o = e->options; o; o = n) {
1897 n = o->next;
1898
1899 if (o->alsa_idx < 0) {
1900 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1901 option_free(o);
1902 }
1903 }
1904
1905 return
1906 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1907 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1908 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1909 }
1910
1911 static void path_drop_unsupported(pa_alsa_path *p) {
1912 pa_alsa_element *e, *n;
1913
1914 pa_assert(p);
1915
1916 for (e = p->elements; e; e = n) {
1917 n = e->next;
1918
1919 if (!element_drop_unsupported(e)) {
1920 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1921 element_free(e);
1922 }
1923 }
1924 }
1925
1926 static void path_make_options_unique(pa_alsa_path *p) {
1927 pa_alsa_element *e;
1928 pa_alsa_option *o, *u;
1929
1930 PA_LLIST_FOREACH(e, p->elements) {
1931 PA_LLIST_FOREACH(o, e->options) {
1932 unsigned i;
1933 char *m;
1934
1935 for (u = o->next; u; u = u->next)
1936 if (pa_streq(u->name, o->name))
1937 break;
1938
1939 if (!u)
1940 continue;
1941
1942 m = pa_xstrdup(o->name);
1943
1944 /* OK, this name is not unique, hence let's rename */
1945 for (i = 1, u = o; u; u = u->next) {
1946 char *nn, *nd;
1947
1948 if (!pa_streq(u->name, m))
1949 continue;
1950
1951 nn = pa_sprintf_malloc("%s-%u", m, i);
1952 pa_xfree(u->name);
1953 u->name = nn;
1954
1955 nd = pa_sprintf_malloc("%s %u", u->description, i);
1956 pa_xfree(u->description);
1957 u->description = nd;
1958
1959 i++;
1960 }
1961
1962 pa_xfree(m);
1963 }
1964 }
1965 }
1966
1967 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1968 pa_alsa_option *o;
1969
1970 for (; e; e = e->next)
1971 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1972 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1973 break;
1974
1975 if (!e)
1976 return FALSE;
1977
1978 for (o = e->options; o; o = o->next) {
1979 pa_alsa_setting *s;
1980
1981 if (template) {
1982 s = pa_xnewdup(pa_alsa_setting, template, 1);
1983 s->options = pa_idxset_copy(template->options);
1984 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1985 s->description =
1986 (template->description[0] && o->description[0])
1987 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1988 : (template->description[0]
1989 ? pa_xstrdup(template->description)
1990 : pa_xstrdup(o->description));
1991
1992 s->priority = PA_MAX(template->priority, o->priority);
1993 } else {
1994 s = pa_xnew0(pa_alsa_setting, 1);
1995 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1996 s->name = pa_xstrdup(o->name);
1997 s->description = pa_xstrdup(o->description);
1998 s->priority = o->priority;
1999 }
2000
2001 pa_idxset_put(s->options, o, NULL);
2002
2003 if (element_create_settings(e->next, s))
2004 /* This is not a leaf, so let's get rid of it */
2005 setting_free(s);
2006 else {
2007 /* This is a leaf, so let's add it */
2008 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2009
2010 e->path->last_setting = s;
2011 }
2012 }
2013
2014 return TRUE;
2015 }
2016
2017 static void path_create_settings(pa_alsa_path *p) {
2018 pa_assert(p);
2019
2020 element_create_settings(p->elements, NULL);
2021 }
2022
2023 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2024 pa_alsa_element *e;
2025 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2026 pa_channel_position_t t;
2027
2028 pa_assert(p);
2029 pa_assert(m);
2030
2031 if (p->probed)
2032 return 0;
2033
2034 pa_zero(min_dB);
2035 pa_zero(max_dB);
2036
2037 pa_log_debug("Probing path '%s'", p->name);
2038
2039 PA_LLIST_FOREACH(e, p->elements) {
2040 if (element_probe(e, m) < 0) {
2041 p->supported = FALSE;
2042 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2043 return -1;
2044 }
2045
2046 if (ignore_dB)
2047 e->has_dB = FALSE;
2048
2049 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2050
2051 if (!p->has_volume) {
2052 p->min_volume = e->min_volume;
2053 p->max_volume = e->max_volume;
2054 }
2055
2056 if (e->has_dB) {
2057 if (!p->has_volume) {
2058 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2059 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2060 min_dB[t] = e->min_dB;
2061 max_dB[t] = e->max_dB;
2062 }
2063
2064 p->has_dB = TRUE;
2065 } else {
2066
2067 if (p->has_dB) {
2068 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2069 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2070 min_dB[t] += e->min_dB;
2071 max_dB[t] += e->max_dB;
2072 }
2073 } else
2074 /* Hmm, there's another element before us
2075 * which cannot do dB volumes, so we we need
2076 * to 'neutralize' this slider */
2077 e->volume_use = PA_ALSA_VOLUME_ZERO;
2078 }
2079 } else if (p->has_volume)
2080 /* We can't use this volume, so let's ignore it */
2081 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2082
2083 p->has_volume = TRUE;
2084 }
2085
2086 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2087 p->has_mute = TRUE;
2088 }
2089
2090 path_drop_unsupported(p);
2091 path_make_options_unique(p);
2092 path_create_settings(p);
2093
2094 p->supported = TRUE;
2095 p->probed = TRUE;
2096
2097 p->min_dB = INFINITY;
2098 p->max_dB = -INFINITY;
2099
2100 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2101 if (p->min_dB > min_dB[t])
2102 p->min_dB = min_dB[t];
2103
2104 if (p->max_dB < max_dB[t])
2105 p->max_dB = max_dB[t];
2106 }
2107
2108 return 0;
2109 }
2110
2111 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2112 pa_assert(s);
2113
2114 pa_log_debug("Setting %s (%s) priority=%u",
2115 s->name,
2116 pa_strnull(s->description),
2117 s->priority);
2118 }
2119
2120 void pa_alsa_option_dump(pa_alsa_option *o) {
2121 pa_assert(o);
2122
2123 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2124 o->alsa_name,
2125 pa_strnull(o->name),
2126 pa_strnull(o->description),
2127 o->alsa_idx,
2128 o->priority);
2129 }
2130
2131 void pa_alsa_element_dump(pa_alsa_element *e) {
2132 pa_alsa_option *o;
2133 pa_assert(e);
2134
2135 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",
2136 e->alsa_name,
2137 e->direction,
2138 e->switch_use,
2139 e->volume_use,
2140 e->enumeration_use,
2141 e->required,
2142 e->required_absent,
2143 (long long unsigned) e->merged_mask,
2144 e->n_channels,
2145 pa_yes_no(e->override_map));
2146
2147 PA_LLIST_FOREACH(o, e->options)
2148 pa_alsa_option_dump(o);
2149 }
2150
2151 void pa_alsa_path_dump(pa_alsa_path *p) {
2152 pa_alsa_element *e;
2153 pa_alsa_setting *s;
2154 pa_assert(p);
2155
2156 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2157 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2158 p->name,
2159 pa_strnull(p->description),
2160 p->direction,
2161 p->priority,
2162 pa_yes_no(p->probed),
2163 pa_yes_no(p->supported),
2164 pa_yes_no(p->has_mute),
2165 pa_yes_no(p->has_volume),
2166 pa_yes_no(p->has_dB),
2167 p->min_volume, p->max_volume,
2168 p->min_dB, p->max_dB);
2169
2170 PA_LLIST_FOREACH(e, p->elements)
2171 pa_alsa_element_dump(e);
2172
2173 PA_LLIST_FOREACH(s, p->settings)
2174 pa_alsa_setting_dump(s);
2175 }
2176
2177 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2178 snd_mixer_selem_id_t *sid;
2179 snd_mixer_elem_t *me;
2180
2181 pa_assert(e);
2182 pa_assert(m);
2183 pa_assert(cb);
2184
2185 SELEM_INIT(sid, e->alsa_name);
2186 if (!(me = snd_mixer_find_selem(m, sid))) {
2187 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2188 return;
2189 }
2190
2191 snd_mixer_elem_set_callback(me, cb);
2192 snd_mixer_elem_set_callback_private(me, userdata);
2193 }
2194
2195 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2196 pa_alsa_element *e;
2197
2198 pa_assert(p);
2199 pa_assert(m);
2200 pa_assert(cb);
2201
2202 PA_LLIST_FOREACH(e, p->elements)
2203 element_set_callback(e, m, cb, userdata);
2204 }
2205
2206 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2207 pa_alsa_path *p;
2208
2209 pa_assert(ps);
2210 pa_assert(m);
2211 pa_assert(cb);
2212
2213 PA_LLIST_FOREACH(p, ps->paths)
2214 pa_alsa_path_set_callback(p, m, cb, userdata);
2215 }
2216
2217 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2218 pa_alsa_path_set *ps;
2219 char **pn = NULL, **en = NULL, **ie;
2220
2221 pa_assert(m);
2222 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2223
2224 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2225 return NULL;
2226
2227 ps = pa_xnew0(pa_alsa_path_set, 1);
2228 ps->direction = direction;
2229
2230 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2231 pn = m->output_path_names;
2232 else if (direction == PA_ALSA_DIRECTION_INPUT)
2233 pn = m->input_path_names;
2234
2235 if (pn) {
2236 char **in;
2237
2238 for (in = pn; *in; in++) {
2239 pa_alsa_path *p;
2240 pa_bool_t duplicate = FALSE;
2241 char **kn, *fn;
2242
2243 for (kn = pn; kn != in; kn++)
2244 if (pa_streq(*kn, *in)) {
2245 duplicate = TRUE;
2246 break;
2247 }
2248
2249 if (duplicate)
2250 continue;
2251
2252 fn = pa_sprintf_malloc("%s.conf", *in);
2253
2254 if ((p = pa_alsa_path_new(fn, direction))) {
2255 p->path_set = ps;
2256 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2257 ps->last_path = p;
2258 }
2259
2260 pa_xfree(fn);
2261 }
2262
2263 return ps;
2264 }
2265
2266 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2267 en = m->output_element;
2268 else if (direction == PA_ALSA_DIRECTION_INPUT)
2269 en = m->input_element;
2270
2271 if (!en) {
2272 pa_alsa_path_set_free(ps);
2273 return NULL;
2274 }
2275
2276 for (ie = en; *ie; ie++) {
2277 char **je;
2278 pa_alsa_path *p;
2279
2280 p = pa_alsa_path_synthesize(*ie, direction);
2281 p->path_set = ps;
2282
2283 /* Mark all other passed elements for require-absent */
2284 for (je = en; *je; je++) {
2285 pa_alsa_element *e;
2286 e = pa_xnew0(pa_alsa_element, 1);
2287 e->path = p;
2288 e->alsa_name = pa_xstrdup(*je);
2289 e->direction = direction;
2290 e->required_absent = PA_ALSA_REQUIRED_ANY;
2291
2292 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2293 p->last_element = e;
2294 }
2295
2296 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2297 ps->last_path = p;
2298 }
2299
2300 return ps;
2301 }
2302
2303 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2304 pa_alsa_path *p;
2305 pa_assert(ps);
2306
2307 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2308 (void*) ps,
2309 ps->direction,
2310 pa_yes_no(ps->probed));
2311
2312 PA_LLIST_FOREACH(p, ps->paths)
2313 pa_alsa_path_dump(p);
2314 }
2315
2316 static void path_set_unify(pa_alsa_path_set *ps) {
2317 pa_alsa_path *p;
2318 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2319 pa_assert(ps);
2320
2321 /* We have issues dealing with paths that vary too wildly. That
2322 * means for now we have to have all paths support volume/mute/dB
2323 * or none. */
2324
2325 PA_LLIST_FOREACH(p, ps->paths) {
2326 pa_assert(p->probed);
2327
2328 if (!p->has_volume)
2329 has_volume = FALSE;
2330 else if (!p->has_dB)
2331 has_dB = FALSE;
2332
2333 if (!p->has_mute)
2334 has_mute = FALSE;
2335 }
2336
2337 if (!has_volume || !has_dB || !has_mute) {
2338
2339 if (!has_volume)
2340 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2341 else if (!has_dB)
2342 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2343
2344 if (!has_mute)
2345 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2346
2347 PA_LLIST_FOREACH(p, ps->paths) {
2348 if (!has_volume)
2349 p->has_volume = FALSE;
2350 else if (!has_dB)
2351 p->has_dB = FALSE;
2352
2353 if (!has_mute)
2354 p->has_mute = FALSE;
2355 }
2356 }
2357 }
2358
2359 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2360 pa_alsa_path *p, *q;
2361
2362 PA_LLIST_FOREACH(p, ps->paths) {
2363 unsigned i;
2364 char *m;
2365
2366 for (q = p->next; q; q = q->next)
2367 if (pa_streq(q->name, p->name))
2368 break;
2369
2370 if (!q)
2371 continue;
2372
2373 m = pa_xstrdup(p->name);
2374
2375 /* OK, this name is not unique, hence let's rename */
2376 for (i = 1, q = p; q; q = q->next) {
2377 char *nn, *nd;
2378
2379 if (!pa_streq(q->name, m))
2380 continue;
2381
2382 nn = pa_sprintf_malloc("%s-%u", m, i);
2383 pa_xfree(q->name);
2384 q->name = nn;
2385
2386 nd = pa_sprintf_malloc("%s %u", q->description, i);
2387 pa_xfree(q->description);
2388 q->description = nd;
2389
2390 i++;
2391 }
2392
2393 pa_xfree(m);
2394 }
2395 }
2396
2397 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2398 pa_alsa_path *p, *n;
2399
2400 pa_assert(ps);
2401
2402 if (ps->probed)
2403 return;
2404
2405 for (p = ps->paths; p; p = n) {
2406 n = p->next;
2407
2408 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2409 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2410 pa_alsa_path_free(p);
2411 }
2412 }
2413
2414 path_set_unify(ps);
2415 path_set_make_paths_unique(ps);
2416 ps->probed = TRUE;
2417 }
2418
2419 static void mapping_free(pa_alsa_mapping *m) {
2420 pa_assert(m);
2421
2422 pa_xfree(m->name);
2423 pa_xfree(m->description);
2424
2425 pa_xstrfreev(m->device_strings);
2426 pa_xstrfreev(m->input_path_names);
2427 pa_xstrfreev(m->output_path_names);
2428 pa_xstrfreev(m->input_element);
2429 pa_xstrfreev(m->output_element);
2430
2431 pa_assert(!m->input_pcm);
2432 pa_assert(!m->output_pcm);
2433
2434 pa_xfree(m);
2435 }
2436
2437 static void profile_free(pa_alsa_profile *p) {
2438 pa_assert(p);
2439
2440 pa_xfree(p->name);
2441 pa_xfree(p->description);
2442
2443 pa_xstrfreev(p->input_mapping_names);
2444 pa_xstrfreev(p->output_mapping_names);
2445
2446 if (p->input_mappings)
2447 pa_idxset_free(p->input_mappings, NULL, NULL);
2448
2449 if (p->output_mappings)
2450 pa_idxset_free(p->output_mappings, NULL, NULL);
2451
2452 pa_xfree(p);
2453 }
2454
2455 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2456 pa_assert(ps);
2457
2458 if (ps->profiles) {
2459 pa_alsa_profile *p;
2460
2461 while ((p = pa_hashmap_steal_first(ps->profiles)))
2462 profile_free(p);
2463
2464 pa_hashmap_free(ps->profiles, NULL, NULL);
2465 }
2466
2467 if (ps->mappings) {
2468 pa_alsa_mapping *m;
2469
2470 while ((m = pa_hashmap_steal_first(ps->mappings)))
2471 mapping_free(m);
2472
2473 pa_hashmap_free(ps->mappings, NULL, NULL);
2474 }
2475
2476 pa_xfree(ps);
2477 }
2478
2479 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2480 pa_alsa_mapping *m;
2481
2482 if (!pa_startswith(name, "Mapping "))
2483 return NULL;
2484
2485 name += 8;
2486
2487 if ((m = pa_hashmap_get(ps->mappings, name)))
2488 return m;
2489
2490 m = pa_xnew0(pa_alsa_mapping, 1);
2491 m->profile_set = ps;
2492 m->name = pa_xstrdup(name);
2493 pa_channel_map_init(&m->channel_map);
2494
2495 pa_hashmap_put(ps->mappings, m->name, m);
2496
2497 return m;
2498 }
2499
2500 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2501 pa_alsa_profile *p;
2502
2503 if (!pa_startswith(name, "Profile "))
2504 return NULL;
2505
2506 name += 8;
2507
2508 if ((p = pa_hashmap_get(ps->profiles, name)))
2509 return p;
2510
2511 p = pa_xnew0(pa_alsa_profile, 1);
2512 p->profile_set = ps;
2513 p->name = pa_xstrdup(name);
2514
2515 pa_hashmap_put(ps->profiles, p->name, p);
2516
2517 return p;
2518 }
2519
2520 static int mapping_parse_device_strings(
2521 const char *filename,
2522 unsigned line,
2523 const char *section,
2524 const char *lvalue,
2525 const char *rvalue,
2526 void *data,
2527 void *userdata) {
2528
2529 pa_alsa_profile_set *ps = userdata;
2530 pa_alsa_mapping *m;
2531
2532 pa_assert(ps);
2533
2534 if (!(m = mapping_get(ps, section))) {
2535 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2536 return -1;
2537 }
2538
2539 pa_xstrfreev(m->device_strings);
2540 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2541 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2542 return -1;
2543 }
2544
2545 return 0;
2546 }
2547
2548 static int mapping_parse_channel_map(
2549 const char *filename,
2550 unsigned line,
2551 const char *section,
2552 const char *lvalue,
2553 const char *rvalue,
2554 void *data,
2555 void *userdata) {
2556
2557 pa_alsa_profile_set *ps = userdata;
2558 pa_alsa_mapping *m;
2559
2560 pa_assert(ps);
2561
2562 if (!(m = mapping_get(ps, section))) {
2563 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2564 return -1;
2565 }
2566
2567 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2568 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2569 return -1;
2570 }
2571
2572 return 0;
2573 }
2574
2575 static int mapping_parse_paths(
2576 const char *filename,
2577 unsigned line,
2578 const char *section,
2579 const char *lvalue,
2580 const char *rvalue,
2581 void *data,
2582 void *userdata) {
2583
2584 pa_alsa_profile_set *ps = userdata;
2585 pa_alsa_mapping *m;
2586
2587 pa_assert(ps);
2588
2589 if (!(m = mapping_get(ps, section))) {
2590 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2591 return -1;
2592 }
2593
2594 if (pa_streq(lvalue, "paths-input")) {
2595 pa_xstrfreev(m->input_path_names);
2596 m->input_path_names = pa_split_spaces_strv(rvalue);
2597 } else {
2598 pa_xstrfreev(m->output_path_names);
2599 m->output_path_names = pa_split_spaces_strv(rvalue);
2600 }
2601
2602 return 0;
2603 }
2604
2605 static int mapping_parse_element(
2606 const char *filename,
2607 unsigned line,
2608 const char *section,
2609 const char *lvalue,
2610 const char *rvalue,
2611 void *data,
2612 void *userdata) {
2613
2614 pa_alsa_profile_set *ps = userdata;
2615 pa_alsa_mapping *m;
2616
2617 pa_assert(ps);
2618
2619 if (!(m = mapping_get(ps, section))) {
2620 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2621 return -1;
2622 }
2623
2624 if (pa_streq(lvalue, "element-input")) {
2625 pa_xstrfreev(m->input_element);
2626 m->input_element = pa_split_spaces_strv(rvalue);
2627 } else {
2628 pa_xstrfreev(m->output_element);
2629 m->output_element = pa_split_spaces_strv(rvalue);
2630 }
2631
2632 return 0;
2633 }
2634
2635 static int mapping_parse_direction(
2636 const char *filename,
2637 unsigned line,
2638 const char *section,
2639 const char *lvalue,
2640 const char *rvalue,
2641 void *data,
2642 void *userdata) {
2643
2644 pa_alsa_profile_set *ps = userdata;
2645 pa_alsa_mapping *m;
2646
2647 pa_assert(ps);
2648
2649 if (!(m = mapping_get(ps, section))) {
2650 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2651 return -1;
2652 }
2653
2654 if (pa_streq(rvalue, "input"))
2655 m->direction = PA_ALSA_DIRECTION_INPUT;
2656 else if (pa_streq(rvalue, "output"))
2657 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2658 else if (pa_streq(rvalue, "any"))
2659 m->direction = PA_ALSA_DIRECTION_ANY;
2660 else {
2661 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2662 return -1;
2663 }
2664
2665 return 0;
2666 }
2667
2668 static int mapping_parse_description(
2669 const char *filename,
2670 unsigned line,
2671 const char *section,
2672 const char *lvalue,
2673 const char *rvalue,
2674 void *data,
2675 void *userdata) {
2676
2677 pa_alsa_profile_set *ps = userdata;
2678 pa_alsa_profile *p;
2679 pa_alsa_mapping *m;
2680
2681 pa_assert(ps);
2682
2683 if ((m = mapping_get(ps, section))) {
2684 pa_xstrdup(m->description);
2685 m->description = pa_xstrdup(rvalue);
2686 } else if ((p = profile_get(ps, section))) {
2687 pa_xfree(p->description);
2688 p->description = pa_xstrdup(rvalue);
2689 } else {
2690 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2691 return -1;
2692 }
2693
2694 return 0;
2695 }
2696
2697 static int mapping_parse_priority(
2698 const char *filename,
2699 unsigned line,
2700 const char *section,
2701 const char *lvalue,
2702 const char *rvalue,
2703 void *data,
2704 void *userdata) {
2705
2706 pa_alsa_profile_set *ps = userdata;
2707 pa_alsa_profile *p;
2708 pa_alsa_mapping *m;
2709 uint32_t prio;
2710
2711 pa_assert(ps);
2712
2713 if (pa_atou(rvalue, &prio) < 0) {
2714 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2715 return -1;
2716 }
2717
2718 if ((m = mapping_get(ps, section)))
2719 m->priority = prio;
2720 else if ((p = profile_get(ps, section)))
2721 p->priority = prio;
2722 else {
2723 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2724 return -1;
2725 }
2726
2727 return 0;
2728 }
2729
2730 static int profile_parse_mappings(
2731 const char *filename,
2732 unsigned line,
2733 const char *section,
2734 const char *lvalue,
2735 const char *rvalue,
2736 void *data,
2737 void *userdata) {
2738
2739 pa_alsa_profile_set *ps = userdata;
2740 pa_alsa_profile *p;
2741
2742 pa_assert(ps);
2743
2744 if (!(p = profile_get(ps, section))) {
2745 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2746 return -1;
2747 }
2748
2749 if (pa_streq(lvalue, "input-mappings")) {
2750 pa_xstrfreev(p->input_mapping_names);
2751 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2752 } else {
2753 pa_xstrfreev(p->output_mapping_names);
2754 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2755 }
2756
2757 return 0;
2758 }
2759
2760 static int profile_parse_skip_probe(
2761 const char *filename,
2762 unsigned line,
2763 const char *section,
2764 const char *lvalue,
2765 const char *rvalue,
2766 void *data,
2767 void *userdata) {
2768
2769 pa_alsa_profile_set *ps = userdata;
2770 pa_alsa_profile *p;
2771 int b;
2772
2773 pa_assert(ps);
2774
2775 if (!(p = profile_get(ps, section))) {
2776 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2777 return -1;
2778 }
2779
2780 if ((b = pa_parse_boolean(rvalue)) < 0) {
2781 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2782 return -1;
2783 }
2784
2785 p->supported = b;
2786
2787 return 0;
2788 }
2789
2790 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2791
2792 static const struct description_map well_known_descriptions[] = {
2793 { "analog-mono", N_("Analog Mono") },
2794 { "analog-stereo", N_("Analog Stereo") },
2795 { "analog-surround-21", N_("Analog Surround 2.1") },
2796 { "analog-surround-30", N_("Analog Surround 3.0") },
2797 { "analog-surround-31", N_("Analog Surround 3.1") },
2798 { "analog-surround-40", N_("Analog Surround 4.0") },
2799 { "analog-surround-41", N_("Analog Surround 4.1") },
2800 { "analog-surround-50", N_("Analog Surround 5.0") },
2801 { "analog-surround-51", N_("Analog Surround 5.1") },
2802 { "analog-surround-61", N_("Analog Surround 6.0") },
2803 { "analog-surround-61", N_("Analog Surround 6.1") },
2804 { "analog-surround-70", N_("Analog Surround 7.0") },
2805 { "analog-surround-71", N_("Analog Surround 7.1") },
2806 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2807 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2808 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2809 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2810 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2811 };
2812
2813 pa_assert(m);
2814
2815 if (!pa_channel_map_valid(&m->channel_map)) {
2816 pa_log("Mapping %s is missing channel map.", m->name);
2817 return -1;
2818 }
2819
2820 if (!m->device_strings) {
2821 pa_log("Mapping %s is missing device strings.", m->name);
2822 return -1;
2823 }
2824
2825 if ((m->input_path_names && m->input_element) ||
2826 (m->output_path_names && m->output_element)) {
2827 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2828 return -1;
2829 }
2830
2831 if (!m->description)
2832 m->description = pa_xstrdup(lookup_description(m->name,
2833 well_known_descriptions,
2834 PA_ELEMENTSOF(well_known_descriptions)));
2835
2836 if (!m->description)
2837 m->description = pa_xstrdup(m->name);
2838
2839 if (bonus) {
2840 if (pa_channel_map_equal(&m->channel_map, bonus))
2841 m->priority += 50;
2842 else if (m->channel_map.channels == bonus->channels)
2843 m->priority += 30;
2844 }
2845
2846 return 0;
2847 }
2848
2849 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2850 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2851
2852 pa_assert(m);
2853
2854 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2855 m->name,
2856 pa_strnull(m->description),
2857 m->priority,
2858 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2859 pa_yes_no(m->supported),
2860 m->direction);
2861 }
2862
2863 static void profile_set_add_auto_pair(
2864 pa_alsa_profile_set *ps,
2865 pa_alsa_mapping *m, /* output */
2866 pa_alsa_mapping *n /* input */) {
2867
2868 char *name;
2869 pa_alsa_profile *p;
2870
2871 pa_assert(ps);
2872 pa_assert(m || n);
2873
2874 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2875 return;
2876
2877 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2878 return;
2879
2880 if (m && n)
2881 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2882 else if (m)
2883 name = pa_sprintf_malloc("output:%s", m->name);
2884 else
2885 name = pa_sprintf_malloc("input:%s", n->name);
2886
2887 if ((p = pa_hashmap_get(ps->profiles, name))) {
2888 pa_xfree(name);
2889 return;
2890 }
2891
2892 p = pa_xnew0(pa_alsa_profile, 1);
2893 p->profile_set = ps;
2894 p->name = name;
2895
2896 if (m) {
2897 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2898 pa_idxset_put(p->output_mappings, m, NULL);
2899 p->priority += m->priority * 100;
2900 }
2901
2902 if (n) {
2903 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2904 pa_idxset_put(p->input_mappings, n, NULL);
2905 p->priority += n->priority;
2906 }
2907
2908 pa_hashmap_put(ps->profiles, p->name, p);
2909 }
2910
2911 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2912 pa_alsa_mapping *m, *n;
2913 void *m_state, *n_state;
2914
2915 pa_assert(ps);
2916
2917 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2918 profile_set_add_auto_pair(ps, m, NULL);
2919
2920 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2921 profile_set_add_auto_pair(ps, m, n);
2922 }
2923
2924 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2925 profile_set_add_auto_pair(ps, NULL, n);
2926 }
2927
2928 static int profile_verify(pa_alsa_profile *p) {
2929
2930 static const struct description_map well_known_descriptions[] = {
2931 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2932 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2933 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2934 { "off", N_("Off") }
2935 };
2936
2937 pa_assert(p);
2938
2939 /* Replace the output mapping names by the actual mappings */
2940 if (p->output_mapping_names) {
2941 char **name;
2942
2943 pa_assert(!p->output_mappings);
2944 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2945
2946 for (name = p->output_mapping_names; *name; name++) {
2947 pa_alsa_mapping *m;
2948 char **in;
2949 pa_bool_t duplicate = FALSE;
2950
2951 for (in = name + 1; *in; in++)
2952 if (pa_streq(*name, *in)) {
2953 duplicate = TRUE;
2954 break;
2955 }
2956
2957 if (duplicate)
2958 continue;
2959
2960 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2961 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2962 return -1;
2963 }
2964
2965 pa_idxset_put(p->output_mappings, m, NULL);
2966
2967 if (p->supported)
2968 m->supported++;
2969 }
2970
2971 pa_xstrfreev(p->output_mapping_names);
2972 p->output_mapping_names = NULL;
2973 }
2974
2975 /* Replace the input mapping names by the actual mappings */
2976 if (p->input_mapping_names) {
2977 char **name;
2978
2979 pa_assert(!p->input_mappings);
2980 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2981
2982 for (name = p->input_mapping_names; *name; name++) {
2983 pa_alsa_mapping *m;
2984 char **in;
2985 pa_bool_t duplicate = FALSE;
2986
2987 for (in = name + 1; *in; in++)
2988 if (pa_streq(*name, *in)) {
2989 duplicate = TRUE;
2990 break;
2991 }
2992
2993 if (duplicate)
2994 continue;
2995
2996 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
2997 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2998 return -1;
2999 }
3000
3001 pa_idxset_put(p->input_mappings, m, NULL);
3002
3003 if (p->supported)
3004 m->supported++;
3005 }
3006
3007 pa_xstrfreev(p->input_mapping_names);
3008 p->input_mapping_names = NULL;
3009 }
3010
3011 if (!p->input_mappings && !p->output_mappings) {
3012 pa_log("Profile '%s' lacks mappings.", p->name);
3013 return -1;
3014 }
3015
3016 if (!p->description)
3017 p->description = pa_xstrdup(lookup_description(p->name,
3018 well_known_descriptions,
3019 PA_ELEMENTSOF(well_known_descriptions)));
3020
3021 if (!p->description) {
3022 pa_strbuf *sb;
3023 uint32_t idx;
3024 pa_alsa_mapping *m;
3025
3026 sb = pa_strbuf_new();
3027
3028 if (p->output_mappings)
3029 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3030 if (!pa_strbuf_isempty(sb))
3031 pa_strbuf_puts(sb, " + ");
3032
3033 pa_strbuf_printf(sb, "%s Output", m->description);
3034 }
3035
3036 if (p->input_mappings)
3037 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3038 if (!pa_strbuf_isempty(sb))
3039 pa_strbuf_puts(sb, " + ");
3040
3041 pa_strbuf_printf(sb, "%s Input", m->description);
3042 }
3043
3044 p->description = pa_strbuf_tostring_free(sb);
3045 }
3046
3047 return 0;
3048 }
3049
3050 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3051 uint32_t idx;
3052 pa_alsa_mapping *m;
3053 pa_assert(p);
3054
3055 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3056 p->name,
3057 pa_strnull(p->description),
3058 p->priority,
3059 pa_yes_no(p->supported),
3060 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3061 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3062
3063 if (p->input_mappings)
3064 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3065 pa_log_debug("Input %s", m->name);
3066
3067 if (p->output_mappings)
3068 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3069 pa_log_debug("Output %s", m->name);
3070 }
3071
3072 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3073 pa_alsa_profile_set *ps;
3074 pa_alsa_profile *p;
3075 pa_alsa_mapping *m;
3076 char *fn;
3077 int r;
3078 void *state;
3079
3080 static pa_config_item items[] = {
3081 /* [General] */
3082 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3083
3084 /* [Mapping ...] */
3085 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3086 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3087 { "paths-input", mapping_parse_paths, NULL, NULL },
3088 { "paths-output", mapping_parse_paths, NULL, NULL },
3089 { "element-input", mapping_parse_element, NULL, NULL },
3090 { "element-output", mapping_parse_element, NULL, NULL },
3091 { "direction", mapping_parse_direction, NULL, NULL },
3092
3093 /* Shared by [Mapping ...] and [Profile ...] */
3094 { "description", mapping_parse_description, NULL, NULL },
3095 { "priority", mapping_parse_priority, NULL, NULL },
3096
3097 /* [Profile ...] */
3098 { "input-mappings", profile_parse_mappings, NULL, NULL },
3099 { "output-mappings", profile_parse_mappings, NULL, NULL },
3100 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3101 { NULL, NULL, NULL, NULL }
3102 };
3103
3104 ps = pa_xnew0(pa_alsa_profile_set, 1);
3105 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3106 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3107
3108 items[0].data = &ps->auto_profiles;
3109
3110 if (!fname)
3111 fname = "default.conf";
3112
3113 fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
3114 r = pa_config_parse(fn, NULL, items, ps);
3115 pa_xfree(fn);
3116
3117 if (r < 0)
3118 goto fail;
3119
3120 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3121 if (mapping_verify(m, bonus) < 0)
3122 goto fail;
3123
3124 if (ps->auto_profiles)
3125 profile_set_add_auto(ps);
3126
3127 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3128 if (profile_verify(p) < 0)
3129 goto fail;
3130
3131 return ps;
3132
3133 fail:
3134 pa_alsa_profile_set_free(ps);
3135 return NULL;
3136 }
3137
3138 void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
3139 void *state;
3140 pa_alsa_profile *p, *last = NULL;
3141 pa_alsa_mapping *m;
3142
3143 pa_assert(ps);
3144 pa_assert(dev_id);
3145 pa_assert(ss);
3146
3147 if (ps->probed)
3148 return;
3149
3150 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3151 pa_sample_spec try_ss;
3152 pa_channel_map try_map;
3153 uint32_t idx;
3154
3155 /* Is this already marked that it is supported? (i.e. from the config file) */
3156 if (p->supported)
3157 continue;
3158
3159 pa_log_debug("Looking at profile %s", p->name);
3160
3161 /* Close PCMs from the last iteration we don't need anymore */
3162 if (last && last->output_mappings)
3163 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3164
3165 if (!m->output_pcm)
3166 break;
3167
3168 if (last->supported)
3169 m->supported++;
3170
3171 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3172 snd_pcm_close(m->output_pcm);
3173 m->output_pcm = NULL;
3174 }
3175 }
3176
3177 if (last && last->input_mappings)
3178 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3179
3180 if (!m->input_pcm)
3181 break;
3182
3183 if (last->supported)
3184 m->supported++;
3185
3186 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3187 snd_pcm_close(m->input_pcm);
3188 m->input_pcm = NULL;
3189 }
3190 }
3191
3192 p->supported = TRUE;
3193
3194 /* Check if we can open all new ones */
3195 if (p->output_mappings)
3196 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3197
3198 if (m->output_pcm)
3199 continue;
3200
3201 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3202 try_map = m->channel_map;
3203 try_ss = *ss;
3204 try_ss.channels = try_map.channels;
3205
3206 if (!(m ->output_pcm = pa_alsa_open_by_template(
3207 m->device_strings,
3208 dev_id,
3209 NULL,
3210 &try_ss, &try_map,
3211 SND_PCM_STREAM_PLAYBACK,
3212 NULL, NULL, 0, NULL, NULL,
3213 TRUE))) {
3214 p->supported = FALSE;
3215 break;
3216 }
3217 }
3218
3219 if (p->input_mappings && p->supported)
3220 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3221
3222 if (m->input_pcm)
3223 continue;
3224
3225 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3226 try_map = m->channel_map;
3227 try_ss = *ss;
3228 try_ss.channels = try_map.channels;
3229
3230 if (!(m ->input_pcm = pa_alsa_open_by_template(
3231 m->device_strings,
3232 dev_id,
3233 NULL,
3234 &try_ss, &try_map,
3235 SND_PCM_STREAM_CAPTURE,
3236 NULL, NULL, 0, NULL, NULL,
3237 TRUE))) {
3238 p->supported = FALSE;
3239 break;
3240 }
3241 }
3242
3243 last = p;
3244
3245 if (p->supported)
3246 pa_log_debug("Profile %s supported.", p->name);
3247 }
3248
3249 /* Clean up */
3250 if (last) {
3251 uint32_t idx;
3252
3253 if (last->output_mappings)
3254 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3255 if (m->output_pcm) {
3256
3257 if (last->supported)
3258 m->supported++;
3259
3260 snd_pcm_close(m->output_pcm);
3261 m->output_pcm = NULL;
3262 }
3263
3264 if (last->input_mappings)
3265 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3266 if (m->input_pcm) {
3267
3268 if (last->supported)
3269 m->supported++;
3270
3271 snd_pcm_close(m->input_pcm);
3272 m->input_pcm = NULL;
3273 }
3274 }
3275
3276 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3277 if (!p->supported) {
3278 pa_hashmap_remove(ps->profiles, p->name);
3279 profile_free(p);
3280 }
3281
3282 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3283 if (m->supported <= 0) {
3284 pa_hashmap_remove(ps->mappings, m->name);
3285 mapping_free(m);
3286 }
3287
3288 ps->probed = TRUE;
3289 }
3290
3291 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3292 pa_alsa_profile *p;
3293 pa_alsa_mapping *m;
3294 void *state;
3295
3296 pa_assert(ps);
3297
3298 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3299 (void*)
3300 ps,
3301 pa_yes_no(ps->auto_profiles),
3302 pa_yes_no(ps->probed),
3303 pa_hashmap_size(ps->mappings),
3304 pa_hashmap_size(ps->profiles));
3305
3306 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3307 pa_alsa_mapping_dump(m);
3308
3309 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3310 pa_alsa_profile_dump(p);
3311 }
3312
3313 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3314 pa_alsa_path *path;
3315
3316 pa_assert(p);
3317 pa_assert(!*p);
3318 pa_assert(ps);
3319
3320 /* if there is no path, we don't want a port list */
3321 if (!ps->paths)
3322 return;
3323
3324 if (!ps->paths->next){
3325 pa_alsa_setting *s;
3326
3327 /* If there is only one path, but no or only one setting, then
3328 * we want a port list either */
3329 if (!ps->paths->settings || !ps->paths->settings->next)
3330 return;
3331
3332 /* Ok, there is only one path, however with multiple settings,
3333 * so let's create a port for each setting */
3334 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3335
3336 PA_LLIST_FOREACH(s, ps->paths->settings) {
3337 pa_device_port *port;
3338 pa_alsa_port_data *data;
3339
3340 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3341 port->priority = s->priority;
3342
3343 data = PA_DEVICE_PORT_DATA(port);
3344 data->path = ps->paths;
3345 data->setting = s;
3346
3347 pa_hashmap_put(*p, port->name, port);
3348 }
3349
3350 } else {
3351
3352 /* We have multiple paths, so let's create a port for each
3353 * one, and each of each settings */
3354 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3355
3356 PA_LLIST_FOREACH(path, ps->paths) {
3357
3358 if (!path->settings || !path->settings->next) {
3359 pa_device_port *port;
3360 pa_alsa_port_data *data;
3361
3362 /* If there is no or just one setting we only need a
3363 * single entry */
3364
3365 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3366 port->priority = path->priority * 100;
3367
3368
3369 data = PA_DEVICE_PORT_DATA(port);
3370 data->path = path;
3371 data->setting = path->settings;
3372
3373 pa_hashmap_put(*p, port->name, port);
3374 } else {
3375 pa_alsa_setting *s;
3376
3377 PA_LLIST_FOREACH(s, path->settings) {
3378 pa_device_port *port;
3379 pa_alsa_port_data *data;
3380 char *n, *d;
3381
3382 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3383
3384 if (s->description[0])
3385 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3386 else
3387 d = pa_xstrdup(path->description);
3388
3389 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3390 port->priority = path->priority * 100 + s->priority;
3391
3392 pa_xfree(n);
3393 pa_xfree(d);
3394
3395 data = PA_DEVICE_PORT_DATA(port);
3396 data->path = path;
3397 data->setting = s;
3398
3399 pa_hashmap_put(*p, port->name, port);
3400 }
3401 }
3402 }
3403 }
3404
3405 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3406 }