alsa-mixer: Use pa_xfree() instead of pa_xstrdup() for freeing a string.
[pulseaudio-raopUDP/pulseaudio-raop-alac.git] / src / modules / alsa / alsa-mixer.c
blob3ccd88711c8646bce951e1edc66b978d5d6fbb60
1 /***
2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <sys/types.h>
28 #include <limits.h>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
33 #endif
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>
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>
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
55 struct description_map {
56 const char *name;
57 const char *description;
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 unsigned i;
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return dm[i].description;
67 return NULL;
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;
76 snd_mixer_t *mixer;
78 pa_mainloop_api *m;
79 pa_defer_event *defer;
80 pa_io_event **ios;
82 pa_bool_t polled;
84 void (*cb)(void *userdata);
85 void *userdata;
88 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
90 struct pa_alsa_fdlist *fdl = userdata;
91 int err;
92 unsigned i;
93 unsigned short revents;
95 pa_assert(a);
96 pa_assert(fdl);
97 pa_assert(fdl->mixer);
98 pa_assert(fdl->fds);
99 pa_assert(fdl->work_fds);
101 if (fdl->polled)
102 return;
104 fdl->polled = TRUE;
106 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
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;
122 pa_assert(i != fdl->num_fds);
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;
129 a->defer_enable(fdl->defer, 1);
131 if (revents)
132 snd_mixer_handle_events(fdl->mixer);
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;
141 pa_assert(a);
142 pa_assert(fdl);
143 pa_assert(fdl->mixer);
145 a->defer_enable(fdl->defer, 0);
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;
151 num_fds = (unsigned) n;
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);
162 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
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;
169 fdl->polled = FALSE;
171 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
172 return;
174 if (fdl->ios) {
175 for (i = 0; i < fdl->num_fds; i++)
176 a->io_free(fdl->ios[i]);
178 if (num_fds != fdl->num_fds) {
179 pa_xfree(fdl->ios);
180 fdl->ios = NULL;
184 if (!fdl->ios)
185 fdl->ios = pa_xnew(pa_io_event*, num_fds);
187 /* Swap pointers */
188 temp = fdl->work_fds;
189 fdl->work_fds = fdl->fds;
190 fdl->fds = temp;
192 fdl->num_fds = num_fds;
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);
201 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist *fdl;
204 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
206 return fdl;
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
210 pa_assert(fdl);
212 if (fdl->defer) {
213 pa_assert(fdl->m);
214 fdl->m->defer_free(fdl->defer);
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);
225 if (fdl->fds)
226 pa_xfree(fdl->fds);
227 if (fdl->work_fds)
228 pa_xfree(fdl->work_fds);
230 pa_xfree(fdl);
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);
239 fdl->mixer = mixer_handle;
240 fdl->m = m;
241 fdl->defer = m->defer_new(m, defer_cb, fdl);
243 return 0;
246 static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
247 int err;
249 pa_assert(mixer);
250 pa_assert(dev);
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;
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;
262 if ((err = snd_mixer_load(mixer)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err));
264 return -1;
267 pa_log_info("Successfully attached to mixer '%s'", dev);
268 return 0;
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);
278 pa_assert(pcm);
280 if ((err = snd_mixer_open(&m, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
282 return NULL;
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);
291 return m;
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm, info) >= 0) {
296 char *md;
297 int card_idx;
299 if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
301 md = pa_sprintf_malloc("hw:%i", card_idx);
303 if (!dev || !pa_streq(dev, md))
304 if (prepare_mixer(m, md) >= 0) {
306 if (ctl_device)
307 *ctl_device = md;
308 else
309 pa_xfree(md);
311 return m;
314 pa_xfree(md);
318 snd_mixer_close(m);
319 return NULL;
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! */
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,
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,
333 [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
335 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
336 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
338 [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
339 [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
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,
374 [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
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,
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
385 static void setting_free(pa_alsa_setting *s) {
386 pa_assert(s);
388 if (s->options)
389 pa_idxset_free(s->options, NULL, NULL);
391 pa_xfree(s->name);
392 pa_xfree(s->description);
393 pa_xfree(s);
396 static void option_free(pa_alsa_option *o) {
397 pa_assert(o);
399 pa_xfree(o->alsa_name);
400 pa_xfree(o->name);
401 pa_xfree(o->description);
402 pa_xfree(o);
405 static void element_free(pa_alsa_element *e) {
406 pa_alsa_option *o;
407 pa_assert(e);
409 while ((o = e->options)) {
410 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
411 option_free(o);
414 pa_xfree(e->alsa_name);
415 pa_xfree(e);
418 void pa_alsa_path_free(pa_alsa_path *p) {
419 pa_alsa_element *e;
420 pa_alsa_setting *s;
422 pa_assert(p);
424 while ((e = p->elements)) {
425 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
426 element_free(e);
429 while ((s = p->settings)) {
430 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
431 setting_free(s);
434 pa_xfree(p->name);
435 pa_xfree(p->description);
436 pa_xfree(p);
439 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
440 pa_alsa_path *p;
441 pa_assert(ps);
443 while ((p = ps->paths)) {
444 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
445 pa_alsa_path_free(p);
448 pa_xfree(ps);
451 static long to_alsa_dB(pa_volume_t v) {
452 return (long) (pa_sw_volume_to_dB(v) * 100.0);
455 static pa_volume_t from_alsa_dB(long v) {
456 return pa_sw_volume_from_dB((double) v / 100.0);
459 static long to_alsa_volume(pa_volume_t v, long min, long max) {
460 long w;
462 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
463 return PA_CLAMP_UNLIKELY(w, min, max);
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));
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)
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 unsigned k;
484 pa_assert(m);
485 pa_assert(e);
486 pa_assert(cm);
487 pa_assert(v);
489 SELEM_INIT(sid, e->alsa_name);
490 if (!(me = snd_mixer_find_selem(m, sid))) {
491 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
492 return -1;
495 pa_cvolume_mute(v, cm->channels);
497 /* We take the highest volume of all channels that match */
499 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
500 int r;
501 pa_volume_t f;
503 if (e->has_dB) {
504 long value = 0;
506 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
507 if (snd_mixer_selem_has_playback_channel(me, c))
508 r = snd_mixer_selem_get_playback_dB(me, c, &value);
509 else
510 r = -1;
511 } else {
512 if (snd_mixer_selem_has_capture_channel(me, c))
513 r = snd_mixer_selem_get_capture_dB(me, c, &value);
514 else
515 r = -1;
518 if (r < 0)
519 continue;
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
523 #endif
525 f = from_alsa_dB(value);
527 } else {
528 long value = 0;
530 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
531 if (snd_mixer_selem_has_playback_channel(me, c))
532 r = snd_mixer_selem_get_playback_volume(me, c, &value);
533 else
534 r = -1;
535 } else {
536 if (snd_mixer_selem_has_capture_channel(me, c))
537 r = snd_mixer_selem_get_capture_volume(me, c, &value);
538 else
539 r = -1;
542 if (r < 0)
543 continue;
545 f = from_alsa_volume(value, e->min_volume, e->max_volume);
548 for (k = 0; k < cm->channels; k++)
549 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
550 if (v->values[k] < f)
551 v->values[k] = f;
553 mask |= e->masks[c][e->n_channels-1];
556 for (k = 0; k < cm->channels; k++)
557 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
558 v->values[k] = PA_VOLUME_NORM;
560 return 0;
563 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
564 pa_alsa_element *e;
566 pa_assert(m);
567 pa_assert(p);
568 pa_assert(cm);
569 pa_assert(v);
571 if (!p->has_volume)
572 return -1;
574 pa_cvolume_reset(v, cm->channels);
576 PA_LLIST_FOREACH(e, p->elements) {
577 pa_cvolume ev;
579 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
580 continue;
582 pa_assert(!p->has_dB || e->has_dB);
584 if (element_get_volume(e, m, cm, &ev) < 0)
585 return -1;
587 /* If we have no dB information all we can do is take the first element and leave */
588 if (!p->has_dB) {
589 *v = ev;
590 return 0;
593 pa_sw_cvolume_multiply(v, v, &ev);
596 return 0;
599 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t *b) {
600 snd_mixer_selem_id_t *sid;
601 snd_mixer_elem_t *me;
602 snd_mixer_selem_channel_id_t c;
604 pa_assert(m);
605 pa_assert(e);
606 pa_assert(b);
608 SELEM_INIT(sid, e->alsa_name);
609 if (!(me = snd_mixer_find_selem(m, sid))) {
610 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
611 return -1;
614 /* We return muted if at least one channel is muted */
616 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
617 int r;
618 int value = 0;
620 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
621 if (snd_mixer_selem_has_playback_channel(me, c))
622 r = snd_mixer_selem_get_playback_switch(me, c, &value);
623 else
624 r = -1;
625 } else {
626 if (snd_mixer_selem_has_capture_channel(me, c))
627 r = snd_mixer_selem_get_capture_switch(me, c, &value);
628 else
629 r = -1;
632 if (r < 0)
633 continue;
635 if (!value) {
636 *b = FALSE;
637 return 0;
641 *b = TRUE;
642 return 0;
645 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
646 pa_alsa_element *e;
648 pa_assert(m);
649 pa_assert(p);
650 pa_assert(muted);
652 if (!p->has_mute)
653 return -1;
655 PA_LLIST_FOREACH(e, p->elements) {
656 pa_bool_t b;
658 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
659 continue;
661 if (element_get_switch(e, m, &b) < 0)
662 return -1;
664 if (!b) {
665 *muted = TRUE;
666 return 0;
670 *muted = FALSE;
671 return 0;
674 static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
675 snd_mixer_selem_id_t *sid;
676 pa_cvolume rv;
677 snd_mixer_elem_t *me;
678 snd_mixer_selem_channel_id_t c;
679 pa_channel_position_mask_t mask = 0;
680 unsigned k;
682 pa_assert(m);
683 pa_assert(e);
684 pa_assert(cm);
685 pa_assert(v);
686 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
688 SELEM_INIT(sid, e->alsa_name);
689 if (!(me = snd_mixer_find_selem(m, sid))) {
690 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
691 return -1;
694 pa_cvolume_mute(&rv, cm->channels);
696 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
697 int r;
698 pa_volume_t f = PA_VOLUME_MUTED;
699 pa_bool_t found = FALSE;
701 for (k = 0; k < cm->channels; k++)
702 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
703 found = TRUE;
704 if (v->values[k] > f)
705 f = v->values[k];
708 if (!found) {
709 /* Hmm, so this channel does not exist in the volume
710 * struct, so let's bind it to the overall max of the
711 * volume. */
712 f = pa_cvolume_max(v);
715 if (e->has_dB) {
716 long value = to_alsa_dB(f);
718 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
719 /* If we call set_play_volume() without checking first
720 * if the channel is available, ALSA behaves ver
721 * strangely and doesn't fail the call */
722 if (snd_mixer_selem_has_playback_channel(me, c)) {
723 if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
724 r = snd_mixer_selem_get_playback_dB(me, c, &value);
725 } else
726 r = -1;
727 } else {
728 if (snd_mixer_selem_has_capture_channel(me, c)) {
729 if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
730 r = snd_mixer_selem_get_capture_dB(me, c, &value);
731 } else
732 r = -1;
735 if (r < 0)
736 continue;
738 #ifdef HAVE_VALGRIND_MEMCHECK_H
739 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
740 #endif
742 f = from_alsa_dB(value);
744 } else {
745 long value;
747 value = to_alsa_volume(f, e->min_volume, e->max_volume);
749 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
750 if (snd_mixer_selem_has_playback_channel(me, c)) {
751 if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
752 r = snd_mixer_selem_get_playback_volume(me, c, &value);
753 } else
754 r = -1;
755 } else {
756 if (snd_mixer_selem_has_capture_channel(me, c)) {
757 if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
758 r = snd_mixer_selem_get_capture_volume(me, c, &value);
759 } else
760 r = -1;
763 if (r < 0)
764 continue;
766 f = from_alsa_volume(value, e->min_volume, e->max_volume);
769 for (k = 0; k < cm->channels; k++)
770 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
771 if (rv.values[k] < f)
772 rv.values[k] = f;
774 mask |= e->masks[c][e->n_channels-1];
777 for (k = 0; k < cm->channels; k++)
778 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
779 rv.values[k] = PA_VOLUME_NORM;
781 *v = rv;
782 return 0;
785 int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
786 pa_alsa_element *e;
787 pa_cvolume rv;
789 pa_assert(m);
790 pa_assert(p);
791 pa_assert(cm);
792 pa_assert(v);
793 pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
795 if (!p->has_volume)
796 return -1;
798 rv = *v; /* Remaining adjustment */
799 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
801 PA_LLIST_FOREACH(e, p->elements) {
802 pa_cvolume ev;
804 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
805 continue;
807 pa_assert(!p->has_dB || e->has_dB);
809 ev = rv;
810 if (element_set_volume(e, m, cm, &ev) < 0)
811 return -1;
813 if (!p->has_dB) {
814 *v = ev;
815 return 0;
818 pa_sw_cvolume_multiply(v, v, &ev);
819 pa_sw_cvolume_divide(&rv, &rv, &ev);
822 return 0;
825 static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, pa_bool_t b) {
826 snd_mixer_elem_t *me;
827 snd_mixer_selem_id_t *sid;
828 int r;
830 pa_assert(m);
831 pa_assert(e);
833 SELEM_INIT(sid, e->alsa_name);
834 if (!(me = snd_mixer_find_selem(m, sid))) {
835 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
836 return -1;
839 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
840 r = snd_mixer_selem_set_playback_switch_all(me, b);
841 else
842 r = snd_mixer_selem_set_capture_switch_all(me, b);
844 if (r < 0)
845 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
847 return r;
850 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
851 pa_alsa_element *e;
853 pa_assert(m);
854 pa_assert(p);
856 if (!p->has_mute)
857 return -1;
859 PA_LLIST_FOREACH(e, p->elements) {
861 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
862 continue;
864 if (element_set_switch(e, m, !muted) < 0)
865 return -1;
868 return 0;
871 static int element_mute_volume(pa_alsa_element *e, snd_mixer_t *m) {
872 snd_mixer_elem_t *me;
873 snd_mixer_selem_id_t *sid;
874 int r;
876 pa_assert(m);
877 pa_assert(e);
879 SELEM_INIT(sid, e->alsa_name);
880 if (!(me = snd_mixer_find_selem(m, sid))) {
881 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
882 return -1;
885 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
886 r = snd_mixer_selem_set_playback_volume_all(me, e->min_volume);
887 else
888 r = snd_mixer_selem_set_capture_volume_all(me, e->min_volume);
890 if (r < 0)
891 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
893 return r;
896 /* The volume to 0dB */
897 static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
898 snd_mixer_elem_t *me;
899 snd_mixer_selem_id_t *sid;
900 int r;
902 pa_assert(m);
903 pa_assert(e);
905 SELEM_INIT(sid, e->alsa_name);
906 if (!(me = snd_mixer_find_selem(m, sid))) {
907 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
908 return -1;
911 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
912 r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
913 else
914 r = snd_mixer_selem_set_capture_dB_all(me, 0, +1);
916 if (r < 0)
917 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
919 return r;
922 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
923 pa_alsa_element *e;
924 int r = 0;
926 pa_assert(m);
927 pa_assert(p);
929 pa_log_debug("Activating path %s", p->name);
930 pa_alsa_path_dump(p);
932 PA_LLIST_FOREACH(e, p->elements) {
934 switch (e->switch_use) {
935 case PA_ALSA_SWITCH_OFF:
936 r = element_set_switch(e, m, FALSE);
937 break;
939 case PA_ALSA_SWITCH_ON:
940 r = element_set_switch(e, m, TRUE);
941 break;
943 case PA_ALSA_SWITCH_MUTE:
944 case PA_ALSA_SWITCH_IGNORE:
945 case PA_ALSA_SWITCH_SELECT:
946 r = 0;
947 break;
950 if (r < 0)
951 return -1;
953 switch (e->volume_use) {
954 case PA_ALSA_VOLUME_OFF:
955 r = element_mute_volume(e, m);
956 break;
958 case PA_ALSA_VOLUME_ZERO:
959 r = element_zero_volume(e, m);
960 break;
962 case PA_ALSA_VOLUME_MERGE:
963 case PA_ALSA_VOLUME_IGNORE:
964 r = 0;
965 break;
968 if (r < 0)
969 return -1;
972 return 0;
975 static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
976 pa_bool_t has_switch;
977 pa_bool_t has_enumeration;
978 pa_bool_t has_volume;
980 pa_assert(e);
981 pa_assert(me);
983 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
984 has_switch =
985 snd_mixer_selem_has_playback_switch(me) ||
986 (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
987 } else {
988 has_switch =
989 snd_mixer_selem_has_capture_switch(me) ||
990 (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
993 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
994 has_volume =
995 snd_mixer_selem_has_playback_volume(me) ||
996 (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
997 } else {
998 has_volume =
999 snd_mixer_selem_has_capture_volume(me) ||
1000 (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
1003 has_enumeration = snd_mixer_selem_is_enumerated(me);
1005 if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
1006 (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
1007 (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
1008 return -1;
1010 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1011 return -1;
1013 if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
1014 (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
1015 (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
1016 return -1;
1018 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1019 return -1;
1021 return 0;
1024 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1025 snd_mixer_selem_id_t *sid;
1026 snd_mixer_elem_t *me;
1028 pa_assert(m);
1029 pa_assert(e);
1031 SELEM_INIT(sid, e->alsa_name);
1033 if (!(me = snd_mixer_find_selem(m, sid))) {
1035 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1036 return -1;
1038 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1039 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1040 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1042 return 0;
1045 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1046 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1048 if (!snd_mixer_selem_has_playback_switch(me)) {
1049 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1050 e->direction = PA_ALSA_DIRECTION_INPUT;
1051 else
1052 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1055 } else {
1057 if (!snd_mixer_selem_has_capture_switch(me)) {
1058 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1059 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1060 else
1061 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1065 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1066 e->direction_try_other = FALSE;
1069 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1071 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1073 if (!snd_mixer_selem_has_playback_volume(me)) {
1074 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1075 e->direction = PA_ALSA_DIRECTION_INPUT;
1076 else
1077 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1080 } else {
1082 if (!snd_mixer_selem_has_capture_volume(me)) {
1083 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1084 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1085 else
1086 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1090 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1091 long min_dB = 0, max_dB = 0;
1092 int r;
1094 e->direction_try_other = FALSE;
1096 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1097 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1098 else
1099 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1101 if (e->has_dB) {
1102 #ifdef HAVE_VALGRIND_MEMCHECK_H
1103 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1104 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1105 #endif
1107 e->min_dB = ((double) min_dB) / 100.0;
1108 e->max_dB = ((double) max_dB) / 100.0;
1110 if (min_dB >= max_dB) {
1111 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);
1112 e->has_dB = FALSE;
1116 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1117 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1118 else
1119 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1121 if (r < 0) {
1122 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1123 return -1;
1127 if (e->min_volume >= e->max_volume) {
1128 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);
1129 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1131 } else {
1132 pa_bool_t is_mono;
1133 pa_channel_position_t p;
1135 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1136 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1137 else
1138 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1140 if (is_mono) {
1141 e->n_channels = 1;
1143 if (!e->override_map) {
1144 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1145 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1146 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1149 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1150 } else {
1151 e->n_channels = 0;
1152 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1154 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1155 continue;
1157 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1158 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1159 else
1160 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1163 if (e->n_channels <= 0) {
1164 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1165 return -1;
1168 if (!e->override_map) {
1169 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1170 pa_bool_t has_channel;
1172 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1173 continue;
1175 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1176 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1177 else
1178 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1180 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1184 e->merged_mask = 0;
1185 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1186 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1193 if (check_required(e, me) < 0)
1194 return -1;
1196 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1197 pa_alsa_option *o;
1199 PA_LLIST_FOREACH(o, e->options)
1200 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1201 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1202 int n;
1203 pa_alsa_option *o;
1205 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1206 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1207 return -1;
1210 PA_LLIST_FOREACH(o, e->options) {
1211 int i;
1213 for (i = 0; i < n; i++) {
1214 char buf[128];
1216 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1217 continue;
1219 if (!pa_streq(buf, o->alsa_name))
1220 continue;
1222 o->alsa_idx = i;
1227 return 0;
1230 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1231 pa_alsa_element *e;
1233 pa_assert(p);
1234 pa_assert(section);
1236 if (prefixed) {
1237 if (!pa_startswith(section, "Element "))
1238 return NULL;
1240 section += 8;
1243 /* This is not an element section, but an enum section? */
1244 if (strchr(section, ':'))
1245 return NULL;
1247 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1248 return p->last_element;
1250 PA_LLIST_FOREACH(e, p->elements)
1251 if (pa_streq(e->alsa_name, section))
1252 goto finish;
1254 e = pa_xnew0(pa_alsa_element, 1);
1255 e->path = p;
1256 e->alsa_name = pa_xstrdup(section);
1257 e->direction = p->direction;
1259 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1261 finish:
1262 p->last_element = e;
1263 return e;
1266 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1267 char *en;
1268 const char *on;
1269 pa_alsa_option *o;
1270 pa_alsa_element *e;
1272 if (!pa_startswith(section, "Option "))
1273 return NULL;
1275 section += 7;
1277 /* This is not an enum section, but an element section? */
1278 if (!(on = strchr(section, ':')))
1279 return NULL;
1281 en = pa_xstrndup(section, on - section);
1282 on++;
1284 if (p->last_option &&
1285 pa_streq(p->last_option->element->alsa_name, en) &&
1286 pa_streq(p->last_option->alsa_name, on)) {
1287 pa_xfree(en);
1288 return p->last_option;
1291 pa_assert_se(e = element_get(p, en, FALSE));
1292 pa_xfree(en);
1294 PA_LLIST_FOREACH(o, e->options)
1295 if (pa_streq(o->alsa_name, on))
1296 goto finish;
1298 o = pa_xnew0(pa_alsa_option, 1);
1299 o->element = e;
1300 o->alsa_name = pa_xstrdup(on);
1301 o->alsa_idx = -1;
1303 if (p->last_option && p->last_option->element == e)
1304 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1305 else
1306 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1308 finish:
1309 p->last_option = o;
1310 return o;
1313 static int element_parse_switch(
1314 const char *filename,
1315 unsigned line,
1316 const char *section,
1317 const char *lvalue,
1318 const char *rvalue,
1319 void *data,
1320 void *userdata) {
1322 pa_alsa_path *p = userdata;
1323 pa_alsa_element *e;
1325 pa_assert(p);
1327 if (!(e = element_get(p, section, TRUE))) {
1328 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1329 return -1;
1332 if (pa_streq(rvalue, "ignore"))
1333 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1334 else if (pa_streq(rvalue, "mute"))
1335 e->switch_use = PA_ALSA_SWITCH_MUTE;
1336 else if (pa_streq(rvalue, "off"))
1337 e->switch_use = PA_ALSA_SWITCH_OFF;
1338 else if (pa_streq(rvalue, "on"))
1339 e->switch_use = PA_ALSA_SWITCH_ON;
1340 else if (pa_streq(rvalue, "select"))
1341 e->switch_use = PA_ALSA_SWITCH_SELECT;
1342 else {
1343 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1344 return -1;
1347 return 0;
1350 static int element_parse_volume(
1351 const char *filename,
1352 unsigned line,
1353 const char *section,
1354 const char *lvalue,
1355 const char *rvalue,
1356 void *data,
1357 void *userdata) {
1359 pa_alsa_path *p = userdata;
1360 pa_alsa_element *e;
1362 pa_assert(p);
1364 if (!(e = element_get(p, section, TRUE))) {
1365 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1366 return -1;
1369 if (pa_streq(rvalue, "ignore"))
1370 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1371 else if (pa_streq(rvalue, "merge"))
1372 e->volume_use = PA_ALSA_VOLUME_MERGE;
1373 else if (pa_streq(rvalue, "off"))
1374 e->volume_use = PA_ALSA_VOLUME_OFF;
1375 else if (pa_streq(rvalue, "zero"))
1376 e->volume_use = PA_ALSA_VOLUME_ZERO;
1377 else {
1378 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1379 return -1;
1382 return 0;
1385 static int element_parse_enumeration(
1386 const char *filename,
1387 unsigned line,
1388 const char *section,
1389 const char *lvalue,
1390 const char *rvalue,
1391 void *data,
1392 void *userdata) {
1394 pa_alsa_path *p = userdata;
1395 pa_alsa_element *e;
1397 pa_assert(p);
1399 if (!(e = element_get(p, section, TRUE))) {
1400 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1401 return -1;
1404 if (pa_streq(rvalue, "ignore"))
1405 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1406 else if (pa_streq(rvalue, "select"))
1407 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1408 else {
1409 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1410 return -1;
1413 return 0;
1416 static int option_parse_priority(
1417 const char *filename,
1418 unsigned line,
1419 const char *section,
1420 const char *lvalue,
1421 const char *rvalue,
1422 void *data,
1423 void *userdata) {
1425 pa_alsa_path *p = userdata;
1426 pa_alsa_option *o;
1427 uint32_t prio;
1429 pa_assert(p);
1431 if (!(o = option_get(p, section))) {
1432 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1433 return -1;
1436 if (pa_atou(rvalue, &prio) < 0) {
1437 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1438 return -1;
1441 o->priority = prio;
1442 return 0;
1445 static int option_parse_name(
1446 const char *filename,
1447 unsigned line,
1448 const char *section,
1449 const char *lvalue,
1450 const char *rvalue,
1451 void *data,
1452 void *userdata) {
1454 pa_alsa_path *p = userdata;
1455 pa_alsa_option *o;
1457 pa_assert(p);
1459 if (!(o = option_get(p, section))) {
1460 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1461 return -1;
1464 pa_xfree(o->name);
1465 o->name = pa_xstrdup(rvalue);
1467 return 0;
1470 static int element_parse_required(
1471 const char *filename,
1472 unsigned line,
1473 const char *section,
1474 const char *lvalue,
1475 const char *rvalue,
1476 void *data,
1477 void *userdata) {
1479 pa_alsa_path *p = userdata;
1480 pa_alsa_element *e;
1481 pa_alsa_required_t req;
1483 pa_assert(p);
1485 if (!(e = element_get(p, section, TRUE))) {
1486 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1487 return -1;
1490 if (pa_streq(rvalue, "ignore"))
1491 req = PA_ALSA_REQUIRED_IGNORE;
1492 else if (pa_streq(rvalue, "switch"))
1493 req = PA_ALSA_REQUIRED_SWITCH;
1494 else if (pa_streq(rvalue, "volume"))
1495 req = PA_ALSA_REQUIRED_VOLUME;
1496 else if (pa_streq(rvalue, "enumeration"))
1497 req = PA_ALSA_REQUIRED_ENUMERATION;
1498 else if (pa_streq(rvalue, "any"))
1499 req = PA_ALSA_REQUIRED_ANY;
1500 else {
1501 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1502 return -1;
1505 if (pa_streq(lvalue, "required-absent"))
1506 e->required_absent = req;
1507 else
1508 e->required = req;
1510 return 0;
1513 static int element_parse_direction(
1514 const char *filename,
1515 unsigned line,
1516 const char *section,
1517 const char *lvalue,
1518 const char *rvalue,
1519 void *data,
1520 void *userdata) {
1522 pa_alsa_path *p = userdata;
1523 pa_alsa_element *e;
1525 pa_assert(p);
1527 if (!(e = element_get(p, section, TRUE))) {
1528 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1529 return -1;
1532 if (pa_streq(rvalue, "playback"))
1533 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1534 else if (pa_streq(rvalue, "capture"))
1535 e->direction = PA_ALSA_DIRECTION_INPUT;
1536 else {
1537 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1538 return -1;
1541 return 0;
1544 static int element_parse_direction_try_other(
1545 const char *filename,
1546 unsigned line,
1547 const char *section,
1548 const char *lvalue,
1549 const char *rvalue,
1550 void *data,
1551 void *userdata) {
1553 pa_alsa_path *p = userdata;
1554 pa_alsa_element *e;
1555 int yes;
1557 if (!(e = element_get(p, section, TRUE))) {
1558 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1559 return -1;
1562 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1563 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1564 return -1;
1567 e->direction_try_other = !!yes;
1568 return 0;
1571 static pa_channel_position_mask_t parse_mask(const char *m) {
1572 pa_channel_position_mask_t v;
1574 if (pa_streq(m, "all-left"))
1575 v = PA_CHANNEL_POSITION_MASK_LEFT;
1576 else if (pa_streq(m, "all-right"))
1577 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1578 else if (pa_streq(m, "all-center"))
1579 v = PA_CHANNEL_POSITION_MASK_CENTER;
1580 else if (pa_streq(m, "all-front"))
1581 v = PA_CHANNEL_POSITION_MASK_FRONT;
1582 else if (pa_streq(m, "all-rear"))
1583 v = PA_CHANNEL_POSITION_MASK_REAR;
1584 else if (pa_streq(m, "all-side"))
1585 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1586 else if (pa_streq(m, "all-top"))
1587 v = PA_CHANNEL_POSITION_MASK_TOP;
1588 else if (pa_streq(m, "all-no-lfe"))
1589 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1590 else if (pa_streq(m, "all"))
1591 v = PA_CHANNEL_POSITION_MASK_ALL;
1592 else {
1593 pa_channel_position_t p;
1595 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1596 return 0;
1598 v = PA_CHANNEL_POSITION_MASK(p);
1601 return v;
1604 static int element_parse_override_map(
1605 const char *filename,
1606 unsigned line,
1607 const char *section,
1608 const char *lvalue,
1609 const char *rvalue,
1610 void *data,
1611 void *userdata) {
1613 pa_alsa_path *p = userdata;
1614 pa_alsa_element *e;
1615 const char *state = NULL;
1616 unsigned i = 0;
1617 char *n;
1619 if (!(e = element_get(p, section, TRUE))) {
1620 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1621 return -1;
1624 while ((n = pa_split(rvalue, ",", &state))) {
1625 pa_channel_position_mask_t m;
1627 if (!*n)
1628 m = 0;
1629 else {
1630 if ((m = parse_mask(n)) == 0) {
1631 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1632 pa_xfree(n);
1633 return -1;
1637 if (pa_streq(lvalue, "override-map.1"))
1638 e->masks[i++][0] = m;
1639 else
1640 e->masks[i++][1] = m;
1642 /* Later on we might add override-map.3 and so on here ... */
1644 pa_xfree(n);
1647 e->override_map = TRUE;
1649 return 0;
1652 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1653 snd_mixer_selem_id_t *sid;
1654 snd_mixer_elem_t *me;
1655 int r;
1657 pa_assert(e);
1658 pa_assert(m);
1660 SELEM_INIT(sid, e->alsa_name);
1661 if (!(me = snd_mixer_find_selem(m, sid))) {
1662 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1663 return -1;
1666 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1668 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1669 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1670 else
1671 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1673 if (r < 0)
1674 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1676 } else {
1677 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1679 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1680 pa_log_warn("Faile to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1683 return r;
1686 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1687 pa_alsa_option *o;
1688 uint32_t idx;
1690 pa_assert(s);
1691 pa_assert(m);
1693 PA_IDXSET_FOREACH(o, s->options, idx)
1694 element_set_option(o->element, m, o->alsa_idx);
1696 return 0;
1699 static int option_verify(pa_alsa_option *o) {
1700 static const struct description_map well_known_descriptions[] = {
1701 { "input", N_("Input") },
1702 { "input-docking", N_("Docking Station Input") },
1703 { "input-docking-microphone", N_("Docking Station Microphone") },
1704 { "input-linein", N_("Line-In") },
1705 { "input-microphone", N_("Microphone") },
1706 { "input-microphone-external", N_("External Microphone") },
1707 { "input-microphone-internal", N_("Internal Microphone") },
1708 { "input-radio", N_("Radio") },
1709 { "input-video", N_("Video") },
1710 { "input-agc-on", N_("Automatic Gain Control") },
1711 { "input-agc-off", N_("No Automatic Gain Control") },
1712 { "input-boost-on", N_("Boost") },
1713 { "input-boost-off", N_("No Boost") },
1714 { "output-amplifier-on", N_("Amplifier") },
1715 { "output-amplifier-off", N_("No Amplifier") },
1716 { "output-bass-boost-on", N_("Bass Boost") },
1717 { "output-bass-boost-off", N_("No Bass Boost") },
1718 { "output-speaker", N_("Speaker") },
1719 { "output-headphones", N_("Headphones") }
1722 pa_assert(o);
1724 if (!o->name) {
1725 pa_log("No name set for option %s", o->alsa_name);
1726 return -1;
1729 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1730 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1731 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1732 return -1;
1735 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1736 !pa_streq(o->alsa_name, "on") &&
1737 !pa_streq(o->alsa_name, "off")) {
1738 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1739 return -1;
1742 if (!o->description)
1743 o->description = pa_xstrdup(lookup_description(o->name,
1744 well_known_descriptions,
1745 PA_ELEMENTSOF(well_known_descriptions)));
1746 if (!o->description)
1747 o->description = pa_xstrdup(o->name);
1749 return 0;
1752 static int element_verify(pa_alsa_element *e) {
1753 pa_alsa_option *o;
1755 pa_assert(e);
1757 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1758 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1759 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1760 return -1;
1763 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1764 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1765 return -1;
1768 PA_LLIST_FOREACH(o, e->options)
1769 if (option_verify(o) < 0)
1770 return -1;
1772 return 0;
1775 static int path_verify(pa_alsa_path *p) {
1776 static const struct description_map well_known_descriptions[] = {
1777 { "analog-input", N_("Analog Input") },
1778 { "analog-input-microphone", N_("Analog Microphone") },
1779 { "analog-input-linein", N_("Analog Line-In") },
1780 { "analog-input-radio", N_("Analog Radio") },
1781 { "analog-input-video", N_("Analog Video") },
1782 { "analog-output", N_("Analog Output") },
1783 { "analog-output-headphones", N_("Analog Headphones") },
1784 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1785 { "analog-output-mono", N_("Analog Mono Output") },
1786 { "analog-output-speaker", N_("Analog Speakers") }
1789 pa_alsa_element *e;
1791 pa_assert(p);
1793 PA_LLIST_FOREACH(e, p->elements)
1794 if (element_verify(e) < 0)
1795 return -1;
1797 if (!p->description)
1798 p->description = pa_xstrdup(lookup_description(p->name,
1799 well_known_descriptions,
1800 PA_ELEMENTSOF(well_known_descriptions)));
1802 if (!p->description)
1803 p->description = pa_xstrdup(p->name);
1805 return 0;
1808 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1809 pa_alsa_path *p;
1810 char *fn;
1811 int r;
1812 const char *n;
1814 pa_config_item items[] = {
1815 /* [General] */
1816 { "priority", pa_config_parse_unsigned, NULL, "General" },
1817 { "description", pa_config_parse_string, NULL, "General" },
1818 { "name", pa_config_parse_string, NULL, "General" },
1820 /* [Option ...] */
1821 { "priority", option_parse_priority, NULL, NULL },
1822 { "name", option_parse_name, NULL, NULL },
1824 /* [Element ...] */
1825 { "switch", element_parse_switch, NULL, NULL },
1826 { "volume", element_parse_volume, NULL, NULL },
1827 { "enumeration", element_parse_enumeration, NULL, NULL },
1828 { "override-map.1", element_parse_override_map, NULL, NULL },
1829 { "override-map.2", element_parse_override_map, NULL, NULL },
1830 /* ... later on we might add override-map.3 and so on here ... */
1831 { "required", element_parse_required, NULL, NULL },
1832 { "required-absent", element_parse_required, NULL, NULL },
1833 { "direction", element_parse_direction, NULL, NULL },
1834 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1835 { NULL, NULL, NULL, NULL }
1838 pa_assert(fname);
1840 p = pa_xnew0(pa_alsa_path, 1);
1841 n = pa_path_get_filename(fname);
1842 p->name = pa_xstrndup(n, strcspn(n, "."));
1843 p->direction = direction;
1845 items[0].data = &p->priority;
1846 items[1].data = &p->description;
1847 items[2].data = &p->name;
1849 fn = pa_maybe_prefix_path(fname,
1850 #if defined(__linux__) && !defined(__OPTIMIZE__)
1851 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
1852 #endif
1853 PA_ALSA_PATHS_DIR);
1855 r = pa_config_parse(fn, NULL, items, p);
1856 pa_xfree(fn);
1858 if (r < 0)
1859 goto fail;
1861 if (path_verify(p) < 0)
1862 goto fail;
1864 return p;
1866 fail:
1867 pa_alsa_path_free(p);
1868 return NULL;
1871 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1872 pa_alsa_path *p;
1873 pa_alsa_element *e;
1875 pa_assert(element);
1877 p = pa_xnew0(pa_alsa_path, 1);
1878 p->name = pa_xstrdup(element);
1879 p->direction = direction;
1881 e = pa_xnew0(pa_alsa_element, 1);
1882 e->path = p;
1883 e->alsa_name = pa_xstrdup(element);
1884 e->direction = direction;
1886 e->switch_use = PA_ALSA_SWITCH_MUTE;
1887 e->volume_use = PA_ALSA_VOLUME_MERGE;
1889 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1890 return p;
1893 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1894 pa_alsa_option *o, *n;
1896 pa_assert(e);
1898 for (o = e->options; o; o = n) {
1899 n = o->next;
1901 if (o->alsa_idx < 0) {
1902 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1903 option_free(o);
1907 return
1908 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1909 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1910 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1913 static void path_drop_unsupported(pa_alsa_path *p) {
1914 pa_alsa_element *e, *n;
1916 pa_assert(p);
1918 for (e = p->elements; e; e = n) {
1919 n = e->next;
1921 if (!element_drop_unsupported(e)) {
1922 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1923 element_free(e);
1928 static void path_make_options_unique(pa_alsa_path *p) {
1929 pa_alsa_element *e;
1930 pa_alsa_option *o, *u;
1932 PA_LLIST_FOREACH(e, p->elements) {
1933 PA_LLIST_FOREACH(o, e->options) {
1934 unsigned i;
1935 char *m;
1937 for (u = o->next; u; u = u->next)
1938 if (pa_streq(u->name, o->name))
1939 break;
1941 if (!u)
1942 continue;
1944 m = pa_xstrdup(o->name);
1946 /* OK, this name is not unique, hence let's rename */
1947 for (i = 1, u = o; u; u = u->next) {
1948 char *nn, *nd;
1950 if (!pa_streq(u->name, m))
1951 continue;
1953 nn = pa_sprintf_malloc("%s-%u", m, i);
1954 pa_xfree(u->name);
1955 u->name = nn;
1957 nd = pa_sprintf_malloc("%s %u", u->description, i);
1958 pa_xfree(u->description);
1959 u->description = nd;
1961 i++;
1964 pa_xfree(m);
1969 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1970 pa_alsa_option *o;
1972 for (; e; e = e->next)
1973 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1974 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1975 break;
1977 if (!e)
1978 return FALSE;
1980 for (o = e->options; o; o = o->next) {
1981 pa_alsa_setting *s;
1983 if (template) {
1984 s = pa_xnewdup(pa_alsa_setting, template, 1);
1985 s->options = pa_idxset_copy(template->options);
1986 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1987 s->description =
1988 (template->description[0] && o->description[0])
1989 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1990 : (template->description[0]
1991 ? pa_xstrdup(template->description)
1992 : pa_xstrdup(o->description));
1994 s->priority = PA_MAX(template->priority, o->priority);
1995 } else {
1996 s = pa_xnew0(pa_alsa_setting, 1);
1997 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1998 s->name = pa_xstrdup(o->name);
1999 s->description = pa_xstrdup(o->description);
2000 s->priority = o->priority;
2003 pa_idxset_put(s->options, o, NULL);
2005 if (element_create_settings(e->next, s))
2006 /* This is not a leaf, so let's get rid of it */
2007 setting_free(s);
2008 else {
2009 /* This is a leaf, so let's add it */
2010 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2012 e->path->last_setting = s;
2016 return TRUE;
2019 static void path_create_settings(pa_alsa_path *p) {
2020 pa_assert(p);
2022 element_create_settings(p->elements, NULL);
2025 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2026 pa_alsa_element *e;
2027 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2028 pa_channel_position_t t;
2030 pa_assert(p);
2031 pa_assert(m);
2033 if (p->probed)
2034 return 0;
2036 pa_zero(min_dB);
2037 pa_zero(max_dB);
2039 pa_log_debug("Probing path '%s'", p->name);
2041 PA_LLIST_FOREACH(e, p->elements) {
2042 if (element_probe(e, m) < 0) {
2043 p->supported = FALSE;
2044 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2045 return -1;
2048 if (ignore_dB)
2049 e->has_dB = FALSE;
2051 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2053 if (!p->has_volume) {
2054 p->min_volume = e->min_volume;
2055 p->max_volume = e->max_volume;
2058 if (e->has_dB) {
2059 if (!p->has_volume) {
2060 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2061 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2062 min_dB[t] = e->min_dB;
2063 max_dB[t] = e->max_dB;
2066 p->has_dB = TRUE;
2067 } else {
2069 if (p->has_dB) {
2070 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2071 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2072 min_dB[t] += e->min_dB;
2073 max_dB[t] += e->max_dB;
2075 } else
2076 /* Hmm, there's another element before us
2077 * which cannot do dB volumes, so we we need
2078 * to 'neutralize' this slider */
2079 e->volume_use = PA_ALSA_VOLUME_ZERO;
2081 } else if (p->has_volume)
2082 /* We can't use this volume, so let's ignore it */
2083 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2085 p->has_volume = TRUE;
2088 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2089 p->has_mute = TRUE;
2092 path_drop_unsupported(p);
2093 path_make_options_unique(p);
2094 path_create_settings(p);
2096 p->supported = TRUE;
2097 p->probed = TRUE;
2099 p->min_dB = INFINITY;
2100 p->max_dB = -INFINITY;
2102 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2103 if (p->min_dB > min_dB[t])
2104 p->min_dB = min_dB[t];
2106 if (p->max_dB < max_dB[t])
2107 p->max_dB = max_dB[t];
2110 return 0;
2113 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2114 pa_assert(s);
2116 pa_log_debug("Setting %s (%s) priority=%u",
2117 s->name,
2118 pa_strnull(s->description),
2119 s->priority);
2122 void pa_alsa_option_dump(pa_alsa_option *o) {
2123 pa_assert(o);
2125 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2126 o->alsa_name,
2127 pa_strnull(o->name),
2128 pa_strnull(o->description),
2129 o->alsa_idx,
2130 o->priority);
2133 void pa_alsa_element_dump(pa_alsa_element *e) {
2134 pa_alsa_option *o;
2135 pa_assert(e);
2137 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",
2138 e->alsa_name,
2139 e->direction,
2140 e->switch_use,
2141 e->volume_use,
2142 e->enumeration_use,
2143 e->required,
2144 e->required_absent,
2145 (long long unsigned) e->merged_mask,
2146 e->n_channels,
2147 pa_yes_no(e->override_map));
2149 PA_LLIST_FOREACH(o, e->options)
2150 pa_alsa_option_dump(o);
2153 void pa_alsa_path_dump(pa_alsa_path *p) {
2154 pa_alsa_element *e;
2155 pa_alsa_setting *s;
2156 pa_assert(p);
2158 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2159 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2160 p->name,
2161 pa_strnull(p->description),
2162 p->direction,
2163 p->priority,
2164 pa_yes_no(p->probed),
2165 pa_yes_no(p->supported),
2166 pa_yes_no(p->has_mute),
2167 pa_yes_no(p->has_volume),
2168 pa_yes_no(p->has_dB),
2169 p->min_volume, p->max_volume,
2170 p->min_dB, p->max_dB);
2172 PA_LLIST_FOREACH(e, p->elements)
2173 pa_alsa_element_dump(e);
2175 PA_LLIST_FOREACH(s, p->settings)
2176 pa_alsa_setting_dump(s);
2179 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2180 snd_mixer_selem_id_t *sid;
2181 snd_mixer_elem_t *me;
2183 pa_assert(e);
2184 pa_assert(m);
2185 pa_assert(cb);
2187 SELEM_INIT(sid, e->alsa_name);
2188 if (!(me = snd_mixer_find_selem(m, sid))) {
2189 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2190 return;
2193 snd_mixer_elem_set_callback(me, cb);
2194 snd_mixer_elem_set_callback_private(me, userdata);
2197 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2198 pa_alsa_element *e;
2200 pa_assert(p);
2201 pa_assert(m);
2202 pa_assert(cb);
2204 PA_LLIST_FOREACH(e, p->elements)
2205 element_set_callback(e, m, cb, userdata);
2208 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2209 pa_alsa_path *p;
2211 pa_assert(ps);
2212 pa_assert(m);
2213 pa_assert(cb);
2215 PA_LLIST_FOREACH(p, ps->paths)
2216 pa_alsa_path_set_callback(p, m, cb, userdata);
2219 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2220 pa_alsa_path_set *ps;
2221 char **pn = NULL, **en = NULL, **ie;
2223 pa_assert(m);
2224 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2226 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2227 return NULL;
2229 ps = pa_xnew0(pa_alsa_path_set, 1);
2230 ps->direction = direction;
2232 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2233 pn = m->output_path_names;
2234 else if (direction == PA_ALSA_DIRECTION_INPUT)
2235 pn = m->input_path_names;
2237 if (pn) {
2238 char **in;
2240 for (in = pn; *in; in++) {
2241 pa_alsa_path *p;
2242 pa_bool_t duplicate = FALSE;
2243 char **kn, *fn;
2245 for (kn = pn; kn != in; kn++)
2246 if (pa_streq(*kn, *in)) {
2247 duplicate = TRUE;
2248 break;
2251 if (duplicate)
2252 continue;
2254 fn = pa_sprintf_malloc("%s.conf", *in);
2256 if ((p = pa_alsa_path_new(fn, direction))) {
2257 p->path_set = ps;
2258 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2259 ps->last_path = p;
2262 pa_xfree(fn);
2265 return ps;
2268 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2269 en = m->output_element;
2270 else if (direction == PA_ALSA_DIRECTION_INPUT)
2271 en = m->input_element;
2273 if (!en) {
2274 pa_alsa_path_set_free(ps);
2275 return NULL;
2278 for (ie = en; *ie; ie++) {
2279 char **je;
2280 pa_alsa_path *p;
2282 p = pa_alsa_path_synthesize(*ie, direction);
2283 p->path_set = ps;
2285 /* Mark all other passed elements for require-absent */
2286 for (je = en; *je; je++) {
2287 pa_alsa_element *e;
2288 e = pa_xnew0(pa_alsa_element, 1);
2289 e->path = p;
2290 e->alsa_name = pa_xstrdup(*je);
2291 e->direction = direction;
2292 e->required_absent = PA_ALSA_REQUIRED_ANY;
2294 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2295 p->last_element = e;
2298 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2299 ps->last_path = p;
2302 return ps;
2305 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2306 pa_alsa_path *p;
2307 pa_assert(ps);
2309 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2310 (void*) ps,
2311 ps->direction,
2312 pa_yes_no(ps->probed));
2314 PA_LLIST_FOREACH(p, ps->paths)
2315 pa_alsa_path_dump(p);
2318 static void path_set_unify(pa_alsa_path_set *ps) {
2319 pa_alsa_path *p;
2320 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2321 pa_assert(ps);
2323 /* We have issues dealing with paths that vary too wildly. That
2324 * means for now we have to have all paths support volume/mute/dB
2325 * or none. */
2327 PA_LLIST_FOREACH(p, ps->paths) {
2328 pa_assert(p->probed);
2330 if (!p->has_volume)
2331 has_volume = FALSE;
2332 else if (!p->has_dB)
2333 has_dB = FALSE;
2335 if (!p->has_mute)
2336 has_mute = FALSE;
2339 if (!has_volume || !has_dB || !has_mute) {
2341 if (!has_volume)
2342 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2343 else if (!has_dB)
2344 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2346 if (!has_mute)
2347 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2349 PA_LLIST_FOREACH(p, ps->paths) {
2350 if (!has_volume)
2351 p->has_volume = FALSE;
2352 else if (!has_dB)
2353 p->has_dB = FALSE;
2355 if (!has_mute)
2356 p->has_mute = FALSE;
2361 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2362 pa_alsa_path *p, *q;
2364 PA_LLIST_FOREACH(p, ps->paths) {
2365 unsigned i;
2366 char *m;
2368 for (q = p->next; q; q = q->next)
2369 if (pa_streq(q->name, p->name))
2370 break;
2372 if (!q)
2373 continue;
2375 m = pa_xstrdup(p->name);
2377 /* OK, this name is not unique, hence let's rename */
2378 for (i = 1, q = p; q; q = q->next) {
2379 char *nn, *nd;
2381 if (!pa_streq(q->name, m))
2382 continue;
2384 nn = pa_sprintf_malloc("%s-%u", m, i);
2385 pa_xfree(q->name);
2386 q->name = nn;
2388 nd = pa_sprintf_malloc("%s %u", q->description, i);
2389 pa_xfree(q->description);
2390 q->description = nd;
2392 i++;
2395 pa_xfree(m);
2399 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2400 pa_alsa_path *p, *n;
2402 pa_assert(ps);
2404 if (ps->probed)
2405 return;
2407 for (p = ps->paths; p; p = n) {
2408 n = p->next;
2410 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2411 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2412 pa_alsa_path_free(p);
2416 path_set_unify(ps);
2417 path_set_make_paths_unique(ps);
2418 ps->probed = TRUE;
2421 static void mapping_free(pa_alsa_mapping *m) {
2422 pa_assert(m);
2424 pa_xfree(m->name);
2425 pa_xfree(m->description);
2427 pa_xstrfreev(m->device_strings);
2428 pa_xstrfreev(m->input_path_names);
2429 pa_xstrfreev(m->output_path_names);
2430 pa_xstrfreev(m->input_element);
2431 pa_xstrfreev(m->output_element);
2433 pa_assert(!m->input_pcm);
2434 pa_assert(!m->output_pcm);
2436 pa_xfree(m);
2439 static void profile_free(pa_alsa_profile *p) {
2440 pa_assert(p);
2442 pa_xfree(p->name);
2443 pa_xfree(p->description);
2445 pa_xstrfreev(p->input_mapping_names);
2446 pa_xstrfreev(p->output_mapping_names);
2448 if (p->input_mappings)
2449 pa_idxset_free(p->input_mappings, NULL, NULL);
2451 if (p->output_mappings)
2452 pa_idxset_free(p->output_mappings, NULL, NULL);
2454 pa_xfree(p);
2457 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2458 pa_assert(ps);
2460 if (ps->profiles) {
2461 pa_alsa_profile *p;
2463 while ((p = pa_hashmap_steal_first(ps->profiles)))
2464 profile_free(p);
2466 pa_hashmap_free(ps->profiles, NULL, NULL);
2469 if (ps->mappings) {
2470 pa_alsa_mapping *m;
2472 while ((m = pa_hashmap_steal_first(ps->mappings)))
2473 mapping_free(m);
2475 pa_hashmap_free(ps->mappings, NULL, NULL);
2478 pa_xfree(ps);
2481 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2482 pa_alsa_mapping *m;
2484 if (!pa_startswith(name, "Mapping "))
2485 return NULL;
2487 name += 8;
2489 if ((m = pa_hashmap_get(ps->mappings, name)))
2490 return m;
2492 m = pa_xnew0(pa_alsa_mapping, 1);
2493 m->profile_set = ps;
2494 m->name = pa_xstrdup(name);
2495 pa_channel_map_init(&m->channel_map);
2497 pa_hashmap_put(ps->mappings, m->name, m);
2499 return m;
2502 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2503 pa_alsa_profile *p;
2505 if (!pa_startswith(name, "Profile "))
2506 return NULL;
2508 name += 8;
2510 if ((p = pa_hashmap_get(ps->profiles, name)))
2511 return p;
2513 p = pa_xnew0(pa_alsa_profile, 1);
2514 p->profile_set = ps;
2515 p->name = pa_xstrdup(name);
2517 pa_hashmap_put(ps->profiles, p->name, p);
2519 return p;
2522 static int mapping_parse_device_strings(
2523 const char *filename,
2524 unsigned line,
2525 const char *section,
2526 const char *lvalue,
2527 const char *rvalue,
2528 void *data,
2529 void *userdata) {
2531 pa_alsa_profile_set *ps = userdata;
2532 pa_alsa_mapping *m;
2534 pa_assert(ps);
2536 if (!(m = mapping_get(ps, section))) {
2537 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2538 return -1;
2541 pa_xstrfreev(m->device_strings);
2542 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2543 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2544 return -1;
2547 return 0;
2550 static int mapping_parse_channel_map(
2551 const char *filename,
2552 unsigned line,
2553 const char *section,
2554 const char *lvalue,
2555 const char *rvalue,
2556 void *data,
2557 void *userdata) {
2559 pa_alsa_profile_set *ps = userdata;
2560 pa_alsa_mapping *m;
2562 pa_assert(ps);
2564 if (!(m = mapping_get(ps, section))) {
2565 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2566 return -1;
2569 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2570 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2571 return -1;
2574 return 0;
2577 static int mapping_parse_paths(
2578 const char *filename,
2579 unsigned line,
2580 const char *section,
2581 const char *lvalue,
2582 const char *rvalue,
2583 void *data,
2584 void *userdata) {
2586 pa_alsa_profile_set *ps = userdata;
2587 pa_alsa_mapping *m;
2589 pa_assert(ps);
2591 if (!(m = mapping_get(ps, section))) {
2592 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2593 return -1;
2596 if (pa_streq(lvalue, "paths-input")) {
2597 pa_xstrfreev(m->input_path_names);
2598 m->input_path_names = pa_split_spaces_strv(rvalue);
2599 } else {
2600 pa_xstrfreev(m->output_path_names);
2601 m->output_path_names = pa_split_spaces_strv(rvalue);
2604 return 0;
2607 static int mapping_parse_element(
2608 const char *filename,
2609 unsigned line,
2610 const char *section,
2611 const char *lvalue,
2612 const char *rvalue,
2613 void *data,
2614 void *userdata) {
2616 pa_alsa_profile_set *ps = userdata;
2617 pa_alsa_mapping *m;
2619 pa_assert(ps);
2621 if (!(m = mapping_get(ps, section))) {
2622 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2623 return -1;
2626 if (pa_streq(lvalue, "element-input")) {
2627 pa_xstrfreev(m->input_element);
2628 m->input_element = pa_split_spaces_strv(rvalue);
2629 } else {
2630 pa_xstrfreev(m->output_element);
2631 m->output_element = pa_split_spaces_strv(rvalue);
2634 return 0;
2637 static int mapping_parse_direction(
2638 const char *filename,
2639 unsigned line,
2640 const char *section,
2641 const char *lvalue,
2642 const char *rvalue,
2643 void *data,
2644 void *userdata) {
2646 pa_alsa_profile_set *ps = userdata;
2647 pa_alsa_mapping *m;
2649 pa_assert(ps);
2651 if (!(m = mapping_get(ps, section))) {
2652 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2653 return -1;
2656 if (pa_streq(rvalue, "input"))
2657 m->direction = PA_ALSA_DIRECTION_INPUT;
2658 else if (pa_streq(rvalue, "output"))
2659 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2660 else if (pa_streq(rvalue, "any"))
2661 m->direction = PA_ALSA_DIRECTION_ANY;
2662 else {
2663 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2664 return -1;
2667 return 0;
2670 static int mapping_parse_description(
2671 const char *filename,
2672 unsigned line,
2673 const char *section,
2674 const char *lvalue,
2675 const char *rvalue,
2676 void *data,
2677 void *userdata) {
2679 pa_alsa_profile_set *ps = userdata;
2680 pa_alsa_profile *p;
2681 pa_alsa_mapping *m;
2683 pa_assert(ps);
2685 if ((m = mapping_get(ps, section))) {
2686 pa_xfree(m->description);
2687 m->description = pa_xstrdup(rvalue);
2688 } else if ((p = profile_get(ps, section))) {
2689 pa_xfree(p->description);
2690 p->description = pa_xstrdup(rvalue);
2691 } else {
2692 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2693 return -1;
2696 return 0;
2699 static int mapping_parse_priority(
2700 const char *filename,
2701 unsigned line,
2702 const char *section,
2703 const char *lvalue,
2704 const char *rvalue,
2705 void *data,
2706 void *userdata) {
2708 pa_alsa_profile_set *ps = userdata;
2709 pa_alsa_profile *p;
2710 pa_alsa_mapping *m;
2711 uint32_t prio;
2713 pa_assert(ps);
2715 if (pa_atou(rvalue, &prio) < 0) {
2716 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2717 return -1;
2720 if ((m = mapping_get(ps, section)))
2721 m->priority = prio;
2722 else if ((p = profile_get(ps, section)))
2723 p->priority = prio;
2724 else {
2725 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2726 return -1;
2729 return 0;
2732 static int profile_parse_mappings(
2733 const char *filename,
2734 unsigned line,
2735 const char *section,
2736 const char *lvalue,
2737 const char *rvalue,
2738 void *data,
2739 void *userdata) {
2741 pa_alsa_profile_set *ps = userdata;
2742 pa_alsa_profile *p;
2744 pa_assert(ps);
2746 if (!(p = profile_get(ps, section))) {
2747 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2748 return -1;
2751 if (pa_streq(lvalue, "input-mappings")) {
2752 pa_xstrfreev(p->input_mapping_names);
2753 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2754 } else {
2755 pa_xstrfreev(p->output_mapping_names);
2756 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2759 return 0;
2762 static int profile_parse_skip_probe(
2763 const char *filename,
2764 unsigned line,
2765 const char *section,
2766 const char *lvalue,
2767 const char *rvalue,
2768 void *data,
2769 void *userdata) {
2771 pa_alsa_profile_set *ps = userdata;
2772 pa_alsa_profile *p;
2773 int b;
2775 pa_assert(ps);
2777 if (!(p = profile_get(ps, section))) {
2778 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2779 return -1;
2782 if ((b = pa_parse_boolean(rvalue)) < 0) {
2783 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2784 return -1;
2787 p->supported = b;
2789 return 0;
2792 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2794 static const struct description_map well_known_descriptions[] = {
2795 { "analog-mono", N_("Analog Mono") },
2796 { "analog-stereo", N_("Analog Stereo") },
2797 { "analog-surround-21", N_("Analog Surround 2.1") },
2798 { "analog-surround-30", N_("Analog Surround 3.0") },
2799 { "analog-surround-31", N_("Analog Surround 3.1") },
2800 { "analog-surround-40", N_("Analog Surround 4.0") },
2801 { "analog-surround-41", N_("Analog Surround 4.1") },
2802 { "analog-surround-50", N_("Analog Surround 5.0") },
2803 { "analog-surround-51", N_("Analog Surround 5.1") },
2804 { "analog-surround-61", N_("Analog Surround 6.0") },
2805 { "analog-surround-61", N_("Analog Surround 6.1") },
2806 { "analog-surround-70", N_("Analog Surround 7.0") },
2807 { "analog-surround-71", N_("Analog Surround 7.1") },
2808 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2809 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2810 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2811 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2812 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2815 pa_assert(m);
2817 if (!pa_channel_map_valid(&m->channel_map)) {
2818 pa_log("Mapping %s is missing channel map.", m->name);
2819 return -1;
2822 if (!m->device_strings) {
2823 pa_log("Mapping %s is missing device strings.", m->name);
2824 return -1;
2827 if ((m->input_path_names && m->input_element) ||
2828 (m->output_path_names && m->output_element)) {
2829 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2830 return -1;
2833 if (!m->description)
2834 m->description = pa_xstrdup(lookup_description(m->name,
2835 well_known_descriptions,
2836 PA_ELEMENTSOF(well_known_descriptions)));
2838 if (!m->description)
2839 m->description = pa_xstrdup(m->name);
2841 if (bonus) {
2842 if (pa_channel_map_equal(&m->channel_map, bonus))
2843 m->priority += 50;
2844 else if (m->channel_map.channels == bonus->channels)
2845 m->priority += 30;
2848 return 0;
2851 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2852 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2854 pa_assert(m);
2856 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2857 m->name,
2858 pa_strnull(m->description),
2859 m->priority,
2860 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2861 pa_yes_no(m->supported),
2862 m->direction);
2865 static void profile_set_add_auto_pair(
2866 pa_alsa_profile_set *ps,
2867 pa_alsa_mapping *m, /* output */
2868 pa_alsa_mapping *n /* input */) {
2870 char *name;
2871 pa_alsa_profile *p;
2873 pa_assert(ps);
2874 pa_assert(m || n);
2876 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2877 return;
2879 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2880 return;
2882 if (m && n)
2883 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2884 else if (m)
2885 name = pa_sprintf_malloc("output:%s", m->name);
2886 else
2887 name = pa_sprintf_malloc("input:%s", n->name);
2889 if (pa_hashmap_get(ps->profiles, name)) {
2890 pa_xfree(name);
2891 return;
2894 p = pa_xnew0(pa_alsa_profile, 1);
2895 p->profile_set = ps;
2896 p->name = name;
2898 if (m) {
2899 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2900 pa_idxset_put(p->output_mappings, m, NULL);
2901 p->priority += m->priority * 100;
2904 if (n) {
2905 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2906 pa_idxset_put(p->input_mappings, n, NULL);
2907 p->priority += n->priority;
2910 pa_hashmap_put(ps->profiles, p->name, p);
2913 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2914 pa_alsa_mapping *m, *n;
2915 void *m_state, *n_state;
2917 pa_assert(ps);
2919 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2920 profile_set_add_auto_pair(ps, m, NULL);
2922 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2923 profile_set_add_auto_pair(ps, m, n);
2926 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2927 profile_set_add_auto_pair(ps, NULL, n);
2930 static int profile_verify(pa_alsa_profile *p) {
2932 static const struct description_map well_known_descriptions[] = {
2933 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2934 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2935 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2936 { "off", N_("Off") }
2939 pa_assert(p);
2941 /* Replace the output mapping names by the actual mappings */
2942 if (p->output_mapping_names) {
2943 char **name;
2945 pa_assert(!p->output_mappings);
2946 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2948 for (name = p->output_mapping_names; *name; name++) {
2949 pa_alsa_mapping *m;
2950 char **in;
2951 pa_bool_t duplicate = FALSE;
2953 for (in = name + 1; *in; in++)
2954 if (pa_streq(*name, *in)) {
2955 duplicate = TRUE;
2956 break;
2959 if (duplicate)
2960 continue;
2962 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2963 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2964 return -1;
2967 pa_idxset_put(p->output_mappings, m, NULL);
2969 if (p->supported)
2970 m->supported++;
2973 pa_xstrfreev(p->output_mapping_names);
2974 p->output_mapping_names = NULL;
2977 /* Replace the input mapping names by the actual mappings */
2978 if (p->input_mapping_names) {
2979 char **name;
2981 pa_assert(!p->input_mappings);
2982 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2984 for (name = p->input_mapping_names; *name; name++) {
2985 pa_alsa_mapping *m;
2986 char **in;
2987 pa_bool_t duplicate = FALSE;
2989 for (in = name + 1; *in; in++)
2990 if (pa_streq(*name, *in)) {
2991 duplicate = TRUE;
2992 break;
2995 if (duplicate)
2996 continue;
2998 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
2999 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3000 return -1;
3003 pa_idxset_put(p->input_mappings, m, NULL);
3005 if (p->supported)
3006 m->supported++;
3009 pa_xstrfreev(p->input_mapping_names);
3010 p->input_mapping_names = NULL;
3013 if (!p->input_mappings && !p->output_mappings) {
3014 pa_log("Profile '%s' lacks mappings.", p->name);
3015 return -1;
3018 if (!p->description)
3019 p->description = pa_xstrdup(lookup_description(p->name,
3020 well_known_descriptions,
3021 PA_ELEMENTSOF(well_known_descriptions)));
3023 if (!p->description) {
3024 pa_strbuf *sb;
3025 uint32_t idx;
3026 pa_alsa_mapping *m;
3028 sb = pa_strbuf_new();
3030 if (p->output_mappings)
3031 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3032 if (!pa_strbuf_isempty(sb))
3033 pa_strbuf_puts(sb, " + ");
3035 pa_strbuf_printf(sb, "%s Output", m->description);
3038 if (p->input_mappings)
3039 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3040 if (!pa_strbuf_isempty(sb))
3041 pa_strbuf_puts(sb, " + ");
3043 pa_strbuf_printf(sb, "%s Input", m->description);
3046 p->description = pa_strbuf_tostring_free(sb);
3049 return 0;
3052 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3053 uint32_t idx;
3054 pa_alsa_mapping *m;
3055 pa_assert(p);
3057 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3058 p->name,
3059 pa_strnull(p->description),
3060 p->priority,
3061 pa_yes_no(p->supported),
3062 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3063 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3065 if (p->input_mappings)
3066 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3067 pa_log_debug("Input %s", m->name);
3069 if (p->output_mappings)
3070 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3071 pa_log_debug("Output %s", m->name);
3074 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3075 pa_alsa_profile_set *ps;
3076 pa_alsa_profile *p;
3077 pa_alsa_mapping *m;
3078 char *fn;
3079 int r;
3080 void *state;
3082 static pa_config_item items[] = {
3083 /* [General] */
3084 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3086 /* [Mapping ...] */
3087 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3088 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3089 { "paths-input", mapping_parse_paths, NULL, NULL },
3090 { "paths-output", mapping_parse_paths, NULL, NULL },
3091 { "element-input", mapping_parse_element, NULL, NULL },
3092 { "element-output", mapping_parse_element, NULL, NULL },
3093 { "direction", mapping_parse_direction, NULL, NULL },
3095 /* Shared by [Mapping ...] and [Profile ...] */
3096 { "description", mapping_parse_description, NULL, NULL },
3097 { "priority", mapping_parse_priority, NULL, NULL },
3099 /* [Profile ...] */
3100 { "input-mappings", profile_parse_mappings, NULL, NULL },
3101 { "output-mappings", profile_parse_mappings, NULL, NULL },
3102 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3103 { NULL, NULL, NULL, NULL }
3106 ps = pa_xnew0(pa_alsa_profile_set, 1);
3107 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3108 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3110 items[0].data = &ps->auto_profiles;
3112 if (!fname)
3113 fname = "default.conf";
3115 fn = pa_maybe_prefix_path(fname,
3116 #if defined(__linux__) && !defined(__OPTIMIZE__)
3117 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3118 #endif
3119 PA_ALSA_PROFILE_SETS_DIR);
3121 r = pa_config_parse(fn, NULL, items, ps);
3122 pa_xfree(fn);
3124 if (r < 0)
3125 goto fail;
3127 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3128 if (mapping_verify(m, bonus) < 0)
3129 goto fail;
3131 if (ps->auto_profiles)
3132 profile_set_add_auto(ps);
3134 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3135 if (profile_verify(p) < 0)
3136 goto fail;
3138 return ps;
3140 fail:
3141 pa_alsa_profile_set_free(ps);
3142 return NULL;
3145 void pa_alsa_profile_set_probe(
3146 pa_alsa_profile_set *ps,
3147 const char *dev_id,
3148 const pa_sample_spec *ss,
3149 unsigned default_n_fragments,
3150 unsigned default_fragment_size_msec) {
3152 void *state;
3153 pa_alsa_profile *p, *last = NULL;
3154 pa_alsa_mapping *m;
3156 pa_assert(ps);
3157 pa_assert(dev_id);
3158 pa_assert(ss);
3160 if (ps->probed)
3161 return;
3163 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3164 pa_sample_spec try_ss;
3165 pa_channel_map try_map;
3166 snd_pcm_uframes_t try_period_size, try_buffer_size;
3167 uint32_t idx;
3169 /* Is this already marked that it is supported? (i.e. from the config file) */
3170 if (p->supported)
3171 continue;
3173 pa_log_debug("Looking at profile %s", p->name);
3175 /* Close PCMs from the last iteration we don't need anymore */
3176 if (last && last->output_mappings)
3177 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3179 if (!m->output_pcm)
3180 break;
3182 if (last->supported)
3183 m->supported++;
3185 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3186 snd_pcm_close(m->output_pcm);
3187 m->output_pcm = NULL;
3191 if (last && last->input_mappings)
3192 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3194 if (!m->input_pcm)
3195 break;
3197 if (last->supported)
3198 m->supported++;
3200 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3201 snd_pcm_close(m->input_pcm);
3202 m->input_pcm = NULL;
3206 p->supported = TRUE;
3208 /* Check if we can open all new ones */
3209 if (p->output_mappings)
3210 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3212 if (m->output_pcm)
3213 continue;
3215 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3216 try_map = m->channel_map;
3217 try_ss = *ss;
3218 try_ss.channels = try_map.channels;
3220 try_period_size =
3221 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3222 pa_frame_size(&try_ss);
3223 try_buffer_size = default_n_fragments * try_period_size;
3225 if (!(m ->output_pcm = pa_alsa_open_by_template(
3226 m->device_strings,
3227 dev_id,
3228 NULL,
3229 &try_ss, &try_map,
3230 SND_PCM_STREAM_PLAYBACK,
3231 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3232 TRUE))) {
3233 p->supported = FALSE;
3234 break;
3238 if (p->input_mappings && p->supported)
3239 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3241 if (m->input_pcm)
3242 continue;
3244 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3245 try_map = m->channel_map;
3246 try_ss = *ss;
3247 try_ss.channels = try_map.channels;
3249 try_period_size =
3250 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3251 pa_frame_size(&try_ss);
3252 try_buffer_size = default_n_fragments * try_period_size;
3254 if (!(m ->input_pcm = pa_alsa_open_by_template(
3255 m->device_strings,
3256 dev_id,
3257 NULL,
3258 &try_ss, &try_map,
3259 SND_PCM_STREAM_CAPTURE,
3260 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3261 TRUE))) {
3262 p->supported = FALSE;
3263 break;
3267 last = p;
3269 if (p->supported)
3270 pa_log_debug("Profile %s supported.", p->name);
3273 /* Clean up */
3274 if (last) {
3275 uint32_t idx;
3277 if (last->output_mappings)
3278 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3279 if (m->output_pcm) {
3281 if (last->supported)
3282 m->supported++;
3284 snd_pcm_close(m->output_pcm);
3285 m->output_pcm = NULL;
3288 if (last->input_mappings)
3289 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3290 if (m->input_pcm) {
3292 if (last->supported)
3293 m->supported++;
3295 snd_pcm_close(m->input_pcm);
3296 m->input_pcm = NULL;
3300 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3301 if (!p->supported) {
3302 pa_hashmap_remove(ps->profiles, p->name);
3303 profile_free(p);
3306 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3307 if (m->supported <= 0) {
3308 pa_hashmap_remove(ps->mappings, m->name);
3309 mapping_free(m);
3312 ps->probed = TRUE;
3315 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3316 pa_alsa_profile *p;
3317 pa_alsa_mapping *m;
3318 void *state;
3320 pa_assert(ps);
3322 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3323 (void*)
3325 pa_yes_no(ps->auto_profiles),
3326 pa_yes_no(ps->probed),
3327 pa_hashmap_size(ps->mappings),
3328 pa_hashmap_size(ps->profiles));
3330 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3331 pa_alsa_mapping_dump(m);
3333 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3334 pa_alsa_profile_dump(p);
3337 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3338 pa_alsa_path *path;
3340 pa_assert(p);
3341 pa_assert(!*p);
3342 pa_assert(ps);
3344 /* if there is no path, we don't want a port list */
3345 if (!ps->paths)
3346 return;
3348 if (!ps->paths->next){
3349 pa_alsa_setting *s;
3351 /* If there is only one path, but no or only one setting, then
3352 * we want a port list either */
3353 if (!ps->paths->settings || !ps->paths->settings->next)
3354 return;
3356 /* Ok, there is only one path, however with multiple settings,
3357 * so let's create a port for each setting */
3358 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3360 PA_LLIST_FOREACH(s, ps->paths->settings) {
3361 pa_device_port *port;
3362 pa_alsa_port_data *data;
3364 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3365 port->priority = s->priority;
3367 data = PA_DEVICE_PORT_DATA(port);
3368 data->path = ps->paths;
3369 data->setting = s;
3371 pa_hashmap_put(*p, port->name, port);
3374 } else {
3376 /* We have multiple paths, so let's create a port for each
3377 * one, and each of each settings */
3378 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3380 PA_LLIST_FOREACH(path, ps->paths) {
3382 if (!path->settings || !path->settings->next) {
3383 pa_device_port *port;
3384 pa_alsa_port_data *data;
3386 /* If there is no or just one setting we only need a
3387 * single entry */
3389 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3390 port->priority = path->priority * 100;
3393 data = PA_DEVICE_PORT_DATA(port);
3394 data->path = path;
3395 data->setting = path->settings;
3397 pa_hashmap_put(*p, port->name, port);
3398 } else {
3399 pa_alsa_setting *s;
3401 PA_LLIST_FOREACH(s, path->settings) {
3402 pa_device_port *port;
3403 pa_alsa_port_data *data;
3404 char *n, *d;
3406 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3408 if (s->description[0])
3409 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3410 else
3411 d = pa_xstrdup(path->description);
3413 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3414 port->priority = path->priority * 100 + s->priority;
3416 pa_xfree(n);
3417 pa_xfree(d);
3419 data = PA_DEVICE_PORT_DATA(port);
3420 data->path = path;
3421 data->setting = s;
3423 pa_hashmap_put(*p, port->name, port);
3429 pa_log_debug("Added %u ports", pa_hashmap_size(*p));