]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa: cover bass boost mixer element
[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 unsigned k;
483
484 pa_assert(m);
485 pa_assert(e);
486 pa_assert(cm);
487 pa_assert(v);
488
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;
493 }
494
495 pa_cvolume_mute(v, cm->channels);
496
497 /* We take the highest volume of all channels that match */
498
499 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
500 int r;
501 pa_volume_t f;
502
503 if (e->has_dB) {
504 long value = 0;
505
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;
516 }
517
518 if (r < 0)
519 continue;
520
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
523 #endif
524
525 f = from_alsa_dB(value);
526
527 } else {
528 long value = 0;
529
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;
540 }
541
542 if (r < 0)
543 continue;
544
545 f = from_alsa_volume(value, e->min_volume, e->max_volume);
546 }
547
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;
552
553 mask |= e->masks[c][e->n_channels-1];
554 }
555
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;
559
560 return 0;
561 }
562
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;
565
566 pa_assert(m);
567 pa_assert(p);
568 pa_assert(cm);
569 pa_assert(v);
570
571 if (!p->has_volume)
572 return -1;
573
574 pa_cvolume_reset(v, cm->channels);
575
576 PA_LLIST_FOREACH(e, p->elements) {
577 pa_cvolume ev;
578
579 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
580 continue;
581
582 pa_assert(!p->has_dB || e->has_dB);
583
584 if (element_get_volume(e, m, cm, &ev) < 0)
585 return -1;
586
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;
591 }
592
593 pa_sw_cvolume_multiply(v, v, &ev);
594 }
595
596 return 0;
597 }
598
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;
603
604 pa_assert(m);
605 pa_assert(e);
606 pa_assert(b);
607
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;
612 }
613
614 /* We return muted if at least one channel is muted */
615
616 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
617 int r;
618 int value = 0;
619
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;
630 }
631
632 if (r < 0)
633 continue;
634
635 if (!value) {
636 *b = FALSE;
637 return 0;
638 }
639 }
640
641 *b = TRUE;
642 return 0;
643 }
644
645 int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
646 pa_alsa_element *e;
647
648 pa_assert(m);
649 pa_assert(p);
650 pa_assert(muted);
651
652 if (!p->has_mute)
653 return -1;
654
655 PA_LLIST_FOREACH(e, p->elements) {
656 pa_bool_t b;
657
658 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
659 continue;
660
661 if (element_get_switch(e, m, &b) < 0)
662 return -1;
663
664 if (!b) {
665 *muted = TRUE;
666 return 0;
667 }
668 }
669
670 *muted = FALSE;
671 return 0;
672 }
673
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;
681
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));
687
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;
692 }
693
694 pa_cvolume_mute(&rv, cm->channels);
695
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;
700
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];
706 }
707
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);
713 }
714
715 if (e->has_dB) {
716 long value = to_alsa_dB(f);
717
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;
733 }
734
735 if (r < 0)
736 continue;
737
738 #ifdef HAVE_VALGRIND_MEMCHECK_H
739 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
740 #endif
741
742 f = from_alsa_dB(value);
743
744 } else {
745 long value;
746
747 value = to_alsa_volume(f, e->min_volume, e->max_volume);
748
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;
761 }
762
763 if (r < 0)
764 continue;
765
766 f = from_alsa_volume(value, e->min_volume, e->max_volume);
767 }
768
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;
773
774 mask |= e->masks[c][e->n_channels-1];
775 }
776
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;
780
781 *v = rv;
782 return 0;
783 }
784
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;
788
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));
794
795 if (!p->has_volume)
796 return -1;
797
798 rv = *v; /* Remaining adjustment */
799 pa_cvolume_reset(v, cm->channels); /* Adjustment done */
800
801 PA_LLIST_FOREACH(e, p->elements) {
802 pa_cvolume ev;
803
804 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
805 continue;
806
807 pa_assert(!p->has_dB || e->has_dB);
808
809 ev = rv;
810 if (element_set_volume(e, m, cm, &ev) < 0)
811 return -1;
812
813 if (!p->has_dB) {
814 *v = ev;
815 return 0;
816 }
817
818 pa_sw_cvolume_multiply(v, v, &ev);
819 pa_sw_cvolume_divide(&rv, &rv, &ev);
820 }
821
822 return 0;
823 }
824
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;
829
830 pa_assert(m);
831 pa_assert(e);
832
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;
837 }
838
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);
843
844 if (r < 0)
845 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
846
847 return r;
848 }
849
850 int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t muted) {
851 pa_alsa_element *e;
852
853 pa_assert(m);
854 pa_assert(p);
855
856 if (!p->has_mute)
857 return -1;
858
859 PA_LLIST_FOREACH(e, p->elements) {
860
861 if (e->switch_use != PA_ALSA_SWITCH_MUTE)
862 continue;
863
864 if (element_set_switch(e, m, !muted) < 0)
865 return -1;
866 }
867
868 return 0;
869 }
870
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;
875
876 pa_assert(m);
877 pa_assert(e);
878
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;
883 }
884
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);
889
890 if (r < 0)
891 pa_log_warn("Faile to set volume to muted of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
892
893 return r;
894 }
895
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;
901
902 pa_assert(m);
903 pa_assert(e);
904
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;
909 }
910
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);
915
916 if (r < 0)
917 pa_log_warn("Faile to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
918
919 return r;
920 }
921
922 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
923 pa_alsa_element *e;
924 int r = 0;
925
926 pa_assert(m);
927 pa_assert(p);
928
929 pa_log_debug("Activating path %s", p->name);
930 pa_alsa_path_dump(p);
931
932 PA_LLIST_FOREACH(e, p->elements) {
933
934 switch (e->switch_use) {
935 case PA_ALSA_SWITCH_OFF:
936 r = element_set_switch(e, m, FALSE);
937 break;
938
939 case PA_ALSA_SWITCH_ON:
940 r = element_set_switch(e, m, TRUE);
941 break;
942
943 case PA_ALSA_SWITCH_MUTE:
944 case PA_ALSA_SWITCH_IGNORE:
945 case PA_ALSA_SWITCH_SELECT:
946 r = 0;
947 break;
948 }
949
950 if (r < 0)
951 return -1;
952
953 switch (e->volume_use) {
954 case PA_ALSA_VOLUME_OFF:
955 r = element_mute_volume(e, m);
956 break;
957
958 case PA_ALSA_VOLUME_ZERO:
959 r = element_zero_volume(e, m);
960 break;
961
962 case PA_ALSA_VOLUME_MERGE:
963 case PA_ALSA_VOLUME_IGNORE:
964 r = 0;
965 break;
966 }
967
968 if (r < 0)
969 return -1;
970 }
971
972 return 0;
973 }
974
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;
979
980 pa_assert(e);
981 pa_assert(me);
982
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));
991 }
992
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));
1001 }
1002
1003 has_enumeration = snd_mixer_selem_is_enumerated(me);
1004
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;
1009
1010 if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
1011 return -1;
1012
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;
1017
1018 if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
1019 return -1;
1020
1021 return 0;
1022 }
1023
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;
1027
1028 pa_assert(m);
1029 pa_assert(e);
1030
1031 SELEM_INIT(sid, e->alsa_name);
1032
1033 if (!(me = snd_mixer_find_selem(m, sid))) {
1034
1035 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1036 return -1;
1037
1038 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1039 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1040 e->enumeration_use = PA_ALSA_VOLUME_IGNORE;
1041
1042 return 0;
1043 }
1044
1045 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1046 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1047
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;
1053 }
1054
1055 } else {
1056
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;
1062 }
1063 }
1064
1065 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1066 e->direction_try_other = FALSE;
1067 }
1068
1069 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1070
1071 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1072
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;
1078 }
1079
1080 } else {
1081
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;
1087 }
1088 }
1089
1090 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1091 long min_dB = 0, max_dB = 0;
1092 int r;
1093
1094 e->direction_try_other = FALSE;
1095
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;
1100
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
1106
1107 e->min_dB = ((double) min_dB) / 100.0;
1108 e->max_dB = ((double) max_dB) / 100.0;
1109
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;
1113 }
1114 }
1115
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);
1120
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;
1124 }
1125
1126
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;
1130
1131 } else {
1132 pa_bool_t is_mono;
1133 pa_channel_position_t p;
1134
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;
1139
1140 if (is_mono) {
1141 e->n_channels = 1;
1142
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;
1147 }
1148
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++) {
1153
1154 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1155 continue;
1156
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;
1161 }
1162
1163 if (e->n_channels <= 0) {
1164 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1165 return -1;
1166 }
1167
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;
1171
1172 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1173 continue;
1174
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;
1179
1180 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1181 }
1182 }
1183
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];
1187 }
1188 }
1189 }
1190
1191 }
1192
1193 if (check_required(e, me) < 0)
1194 return -1;
1195
1196 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1197 pa_alsa_option *o;
1198
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;
1204
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;
1208 }
1209
1210 PA_LLIST_FOREACH(o, e->options) {
1211 int i;
1212
1213 for (i = 0; i < n; i++) {
1214 char buf[128];
1215
1216 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1217 continue;
1218
1219 if (!pa_streq(buf, o->alsa_name))
1220 continue;
1221
1222 o->alsa_idx = i;
1223 }
1224 }
1225 }
1226
1227 return 0;
1228 }
1229
1230 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1231 pa_alsa_element *e;
1232
1233 pa_assert(p);
1234 pa_assert(section);
1235
1236 if (prefixed) {
1237 if (!pa_startswith(section, "Element "))
1238 return NULL;
1239
1240 section += 8;
1241 }
1242
1243 /* This is not an element section, but an enum section? */
1244 if (strchr(section, ':'))
1245 return NULL;
1246
1247 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1248 return p->last_element;
1249
1250 PA_LLIST_FOREACH(e, p->elements)
1251 if (pa_streq(e->alsa_name, section))
1252 goto finish;
1253
1254 e = pa_xnew0(pa_alsa_element, 1);
1255 e->path = p;
1256 e->alsa_name = pa_xstrdup(section);
1257 e->direction = p->direction;
1258
1259 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1260
1261 finish:
1262 p->last_element = e;
1263 return e;
1264 }
1265
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;
1271
1272 if (!pa_startswith(section, "Option "))
1273 return NULL;
1274
1275 section += 7;
1276
1277 /* This is not an enum section, but an element section? */
1278 if (!(on = strchr(section, ':')))
1279 return NULL;
1280
1281 en = pa_xstrndup(section, on - section);
1282 on++;
1283
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;
1289 }
1290
1291 pa_assert_se(e = element_get(p, en, FALSE));
1292 pa_xfree(en);
1293
1294 PA_LLIST_FOREACH(o, e->options)
1295 if (pa_streq(o->alsa_name, on))
1296 goto finish;
1297
1298 o = pa_xnew0(pa_alsa_option, 1);
1299 o->element = e;
1300 o->alsa_name = pa_xstrdup(on);
1301 o->alsa_idx = -1;
1302
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);
1307
1308 finish:
1309 p->last_option = o;
1310 return o;
1311 }
1312
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) {
1321
1322 pa_alsa_path *p = userdata;
1323 pa_alsa_element *e;
1324
1325 pa_assert(p);
1326
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;
1330 }
1331
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;
1345 }
1346
1347 return 0;
1348 }
1349
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) {
1358
1359 pa_alsa_path *p = userdata;
1360 pa_alsa_element *e;
1361
1362 pa_assert(p);
1363
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;
1367 }
1368
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;
1380 }
1381
1382 return 0;
1383 }
1384
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) {
1393
1394 pa_alsa_path *p = userdata;
1395 pa_alsa_element *e;
1396
1397 pa_assert(p);
1398
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;
1402 }
1403
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;
1411 }
1412
1413 return 0;
1414 }
1415
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) {
1424
1425 pa_alsa_path *p = userdata;
1426 pa_alsa_option *o;
1427 uint32_t prio;
1428
1429 pa_assert(p);
1430
1431 if (!(o = option_get(p, section))) {
1432 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1433 return -1;
1434 }
1435
1436 if (pa_atou(rvalue, &prio) < 0) {
1437 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1438 return -1;
1439 }
1440
1441 o->priority = prio;
1442 return 0;
1443 }
1444
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) {
1453
1454 pa_alsa_path *p = userdata;
1455 pa_alsa_option *o;
1456
1457 pa_assert(p);
1458
1459 if (!(o = option_get(p, section))) {
1460 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1461 return -1;
1462 }
1463
1464 pa_xfree(o->name);
1465 o->name = pa_xstrdup(rvalue);
1466
1467 return 0;
1468 }
1469
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) {
1478
1479 pa_alsa_path *p = userdata;
1480 pa_alsa_element *e;
1481 pa_alsa_required_t req;
1482
1483 pa_assert(p);
1484
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;
1488 }
1489
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;
1503 }
1504
1505 if (pa_streq(lvalue, "required-absent"))
1506 e->required_absent = req;
1507 else
1508 e->required = req;
1509
1510 return 0;
1511 }
1512
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) {
1521
1522 pa_alsa_path *p = userdata;
1523 pa_alsa_element *e;
1524
1525 pa_assert(p);
1526
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;
1530 }
1531
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;
1539 }
1540
1541 return 0;
1542 }
1543
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) {
1552
1553 pa_alsa_path *p = userdata;
1554 pa_alsa_element *e;
1555 int yes;
1556
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;
1560 }
1561
1562 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1563 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1564 return -1;
1565 }
1566
1567 e->direction_try_other = !!yes;
1568 return 0;
1569 }
1570
1571 static pa_channel_position_mask_t parse_mask(const char *m) {
1572 pa_channel_position_mask_t v;
1573
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;
1594
1595 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1596 return 0;
1597
1598 v = PA_CHANNEL_POSITION_MASK(p);
1599 }
1600
1601 return v;
1602 }
1603
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) {
1612
1613 pa_alsa_path *p = userdata;
1614 pa_alsa_element *e;
1615 const char *state = NULL;
1616 unsigned i = 0;
1617 char *n;
1618
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;
1622 }
1623
1624 while ((n = pa_split(rvalue, ",", &state))) {
1625 pa_channel_position_mask_t m;
1626
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;
1634 }
1635 }
1636
1637 if (pa_streq(lvalue, "override-map.1"))
1638 e->masks[i++][0] = m;
1639 else
1640 e->masks[i++][1] = m;
1641
1642 /* Later on we might add override-map.3 and so on here ... */
1643
1644 pa_xfree(n);
1645 }
1646
1647 e->override_map = TRUE;
1648
1649 return 0;
1650 }
1651
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;
1656
1657 pa_assert(e);
1658 pa_assert(m);
1659
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;
1664 }
1665
1666 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1667
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);
1672
1673 if (r < 0)
1674 pa_log_warn("Faile to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1675
1676 } else {
1677 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1678
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));
1681 }
1682
1683 return r;
1684 }
1685
1686 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1687 pa_alsa_option *o;
1688 uint32_t idx;
1689
1690 pa_assert(s);
1691 pa_assert(m);
1692
1693 PA_IDXSET_FOREACH(o, s->options, idx)
1694 element_set_option(o->element, m, o->alsa_idx);
1695
1696 return 0;
1697 }
1698
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") }
1720 };
1721
1722 pa_assert(o);
1723
1724 if (!o->name) {
1725 pa_log("No name set for option %s", o->alsa_name);
1726 return -1;
1727 }
1728
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;
1733 }
1734
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;
1740 }
1741
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);
1748
1749 return 0;
1750 }
1751
1752 static int element_verify(pa_alsa_element *e) {
1753 pa_alsa_option *o;
1754
1755 pa_assert(e);
1756
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;
1761 }
1762
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;
1766 }
1767
1768 PA_LLIST_FOREACH(o, e->options)
1769 if (option_verify(o) < 0)
1770 return -1;
1771
1772 return 0;
1773 }
1774
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-headphones-2", N_("Analog Headphones 2") },
1787 { "analog-output-speaker", N_("Analog Speaker") }
1788 };
1789
1790 pa_alsa_element *e;
1791
1792 pa_assert(p);
1793
1794 PA_LLIST_FOREACH(e, p->elements)
1795 if (element_verify(e) < 0)
1796 return -1;
1797
1798 if (!p->description)
1799 p->description = pa_xstrdup(lookup_description(p->name,
1800 well_known_descriptions,
1801 PA_ELEMENTSOF(well_known_descriptions)));
1802
1803 if (!p->description)
1804 p->description = pa_xstrdup(p->name);
1805
1806 return 0;
1807 }
1808
1809 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1810 pa_alsa_path *p;
1811 char *fn;
1812 int r;
1813 const char *n;
1814
1815 pa_config_item items[] = {
1816 /* [General] */
1817 { "priority", pa_config_parse_unsigned, NULL, "General" },
1818 { "description", pa_config_parse_string, NULL, "General" },
1819 { "name", pa_config_parse_string, NULL, "General" },
1820
1821 /* [Option ...] */
1822 { "priority", option_parse_priority, NULL, NULL },
1823 { "name", option_parse_name, NULL, NULL },
1824
1825 /* [Element ...] */
1826 { "switch", element_parse_switch, NULL, NULL },
1827 { "volume", element_parse_volume, NULL, NULL },
1828 { "enumeration", element_parse_enumeration, NULL, NULL },
1829 { "override-map.1", element_parse_override_map, NULL, NULL },
1830 { "override-map.2", element_parse_override_map, NULL, NULL },
1831 /* ... later on we might add override-map.3 and so on here ... */
1832 { "required", element_parse_required, NULL, NULL },
1833 { "required-absent", element_parse_required, NULL, NULL },
1834 { "direction", element_parse_direction, NULL, NULL },
1835 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
1836 { NULL, NULL, NULL, NULL }
1837 };
1838
1839 pa_assert(fname);
1840
1841 p = pa_xnew0(pa_alsa_path, 1);
1842 n = pa_path_get_filename(fname);
1843 p->name = pa_xstrndup(n, strcspn(n, "."));
1844 p->direction = direction;
1845
1846 items[0].data = &p->priority;
1847 items[1].data = &p->description;
1848 items[2].data = &p->name;
1849
1850 fn = pa_maybe_prefix_path(fname,
1851 #if defined(__linux__) && !defined(__OPTIMIZE__)
1852 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
1853 #endif
1854 PA_ALSA_PATHS_DIR);
1855
1856 r = pa_config_parse(fn, NULL, items, p);
1857 pa_xfree(fn);
1858
1859 if (r < 0)
1860 goto fail;
1861
1862 if (path_verify(p) < 0)
1863 goto fail;
1864
1865 return p;
1866
1867 fail:
1868 pa_alsa_path_free(p);
1869 return NULL;
1870 }
1871
1872 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
1873 pa_alsa_path *p;
1874 pa_alsa_element *e;
1875
1876 pa_assert(element);
1877
1878 p = pa_xnew0(pa_alsa_path, 1);
1879 p->name = pa_xstrdup(element);
1880 p->direction = direction;
1881
1882 e = pa_xnew0(pa_alsa_element, 1);
1883 e->path = p;
1884 e->alsa_name = pa_xstrdup(element);
1885 e->direction = direction;
1886
1887 e->switch_use = PA_ALSA_SWITCH_MUTE;
1888 e->volume_use = PA_ALSA_VOLUME_MERGE;
1889
1890 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
1891 return p;
1892 }
1893
1894 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
1895 pa_alsa_option *o, *n;
1896
1897 pa_assert(e);
1898
1899 for (o = e->options; o; o = n) {
1900 n = o->next;
1901
1902 if (o->alsa_idx < 0) {
1903 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
1904 option_free(o);
1905 }
1906 }
1907
1908 return
1909 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
1910 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
1911 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
1912 }
1913
1914 static void path_drop_unsupported(pa_alsa_path *p) {
1915 pa_alsa_element *e, *n;
1916
1917 pa_assert(p);
1918
1919 for (e = p->elements; e; e = n) {
1920 n = e->next;
1921
1922 if (!element_drop_unsupported(e)) {
1923 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
1924 element_free(e);
1925 }
1926 }
1927 }
1928
1929 static void path_make_options_unique(pa_alsa_path *p) {
1930 pa_alsa_element *e;
1931 pa_alsa_option *o, *u;
1932
1933 PA_LLIST_FOREACH(e, p->elements) {
1934 PA_LLIST_FOREACH(o, e->options) {
1935 unsigned i;
1936 char *m;
1937
1938 for (u = o->next; u; u = u->next)
1939 if (pa_streq(u->name, o->name))
1940 break;
1941
1942 if (!u)
1943 continue;
1944
1945 m = pa_xstrdup(o->name);
1946
1947 /* OK, this name is not unique, hence let's rename */
1948 for (i = 1, u = o; u; u = u->next) {
1949 char *nn, *nd;
1950
1951 if (!pa_streq(u->name, m))
1952 continue;
1953
1954 nn = pa_sprintf_malloc("%s-%u", m, i);
1955 pa_xfree(u->name);
1956 u->name = nn;
1957
1958 nd = pa_sprintf_malloc("%s %u", u->description, i);
1959 pa_xfree(u->description);
1960 u->description = nd;
1961
1962 i++;
1963 }
1964
1965 pa_xfree(m);
1966 }
1967 }
1968 }
1969
1970 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
1971 pa_alsa_option *o;
1972
1973 for (; e; e = e->next)
1974 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
1975 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
1976 break;
1977
1978 if (!e)
1979 return FALSE;
1980
1981 for (o = e->options; o; o = o->next) {
1982 pa_alsa_setting *s;
1983
1984 if (template) {
1985 s = pa_xnewdup(pa_alsa_setting, template, 1);
1986 s->options = pa_idxset_copy(template->options);
1987 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
1988 s->description =
1989 (template->description[0] && o->description[0])
1990 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
1991 : (template->description[0]
1992 ? pa_xstrdup(template->description)
1993 : pa_xstrdup(o->description));
1994
1995 s->priority = PA_MAX(template->priority, o->priority);
1996 } else {
1997 s = pa_xnew0(pa_alsa_setting, 1);
1998 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1999 s->name = pa_xstrdup(o->name);
2000 s->description = pa_xstrdup(o->description);
2001 s->priority = o->priority;
2002 }
2003
2004 pa_idxset_put(s->options, o, NULL);
2005
2006 if (element_create_settings(e->next, s))
2007 /* This is not a leaf, so let's get rid of it */
2008 setting_free(s);
2009 else {
2010 /* This is a leaf, so let's add it */
2011 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2012
2013 e->path->last_setting = s;
2014 }
2015 }
2016
2017 return TRUE;
2018 }
2019
2020 static void path_create_settings(pa_alsa_path *p) {
2021 pa_assert(p);
2022
2023 element_create_settings(p->elements, NULL);
2024 }
2025
2026 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2027 pa_alsa_element *e;
2028 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2029 pa_channel_position_t t;
2030
2031 pa_assert(p);
2032 pa_assert(m);
2033
2034 if (p->probed)
2035 return 0;
2036
2037 pa_zero(min_dB);
2038 pa_zero(max_dB);
2039
2040 pa_log_debug("Probing path '%s'", p->name);
2041
2042 PA_LLIST_FOREACH(e, p->elements) {
2043 if (element_probe(e, m) < 0) {
2044 p->supported = FALSE;
2045 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2046 return -1;
2047 }
2048
2049 if (ignore_dB)
2050 e->has_dB = FALSE;
2051
2052 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2053
2054 if (!p->has_volume) {
2055 p->min_volume = e->min_volume;
2056 p->max_volume = e->max_volume;
2057 }
2058
2059 if (e->has_dB) {
2060 if (!p->has_volume) {
2061 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2062 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2063 min_dB[t] = e->min_dB;
2064 max_dB[t] = e->max_dB;
2065 }
2066
2067 p->has_dB = TRUE;
2068 } else {
2069
2070 if (p->has_dB) {
2071 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2072 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2073 min_dB[t] += e->min_dB;
2074 max_dB[t] += e->max_dB;
2075 }
2076 } else
2077 /* Hmm, there's another element before us
2078 * which cannot do dB volumes, so we we need
2079 * to 'neutralize' this slider */
2080 e->volume_use = PA_ALSA_VOLUME_ZERO;
2081 }
2082 } else if (p->has_volume)
2083 /* We can't use this volume, so let's ignore it */
2084 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2085
2086 p->has_volume = TRUE;
2087 }
2088
2089 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2090 p->has_mute = TRUE;
2091 }
2092
2093 path_drop_unsupported(p);
2094 path_make_options_unique(p);
2095 path_create_settings(p);
2096
2097 p->supported = TRUE;
2098 p->probed = TRUE;
2099
2100 p->min_dB = INFINITY;
2101 p->max_dB = -INFINITY;
2102
2103 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2104 if (p->min_dB > min_dB[t])
2105 p->min_dB = min_dB[t];
2106
2107 if (p->max_dB < max_dB[t])
2108 p->max_dB = max_dB[t];
2109 }
2110
2111 return 0;
2112 }
2113
2114 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2115 pa_assert(s);
2116
2117 pa_log_debug("Setting %s (%s) priority=%u",
2118 s->name,
2119 pa_strnull(s->description),
2120 s->priority);
2121 }
2122
2123 void pa_alsa_option_dump(pa_alsa_option *o) {
2124 pa_assert(o);
2125
2126 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2127 o->alsa_name,
2128 pa_strnull(o->name),
2129 pa_strnull(o->description),
2130 o->alsa_idx,
2131 o->priority);
2132 }
2133
2134 void pa_alsa_element_dump(pa_alsa_element *e) {
2135 pa_alsa_option *o;
2136 pa_assert(e);
2137
2138 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",
2139 e->alsa_name,
2140 e->direction,
2141 e->switch_use,
2142 e->volume_use,
2143 e->enumeration_use,
2144 e->required,
2145 e->required_absent,
2146 (long long unsigned) e->merged_mask,
2147 e->n_channels,
2148 pa_yes_no(e->override_map));
2149
2150 PA_LLIST_FOREACH(o, e->options)
2151 pa_alsa_option_dump(o);
2152 }
2153
2154 void pa_alsa_path_dump(pa_alsa_path *p) {
2155 pa_alsa_element *e;
2156 pa_alsa_setting *s;
2157 pa_assert(p);
2158
2159 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2160 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2161 p->name,
2162 pa_strnull(p->description),
2163 p->direction,
2164 p->priority,
2165 pa_yes_no(p->probed),
2166 pa_yes_no(p->supported),
2167 pa_yes_no(p->has_mute),
2168 pa_yes_no(p->has_volume),
2169 pa_yes_no(p->has_dB),
2170 p->min_volume, p->max_volume,
2171 p->min_dB, p->max_dB);
2172
2173 PA_LLIST_FOREACH(e, p->elements)
2174 pa_alsa_element_dump(e);
2175
2176 PA_LLIST_FOREACH(s, p->settings)
2177 pa_alsa_setting_dump(s);
2178 }
2179
2180 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2181 snd_mixer_selem_id_t *sid;
2182 snd_mixer_elem_t *me;
2183
2184 pa_assert(e);
2185 pa_assert(m);
2186 pa_assert(cb);
2187
2188 SELEM_INIT(sid, e->alsa_name);
2189 if (!(me = snd_mixer_find_selem(m, sid))) {
2190 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2191 return;
2192 }
2193
2194 snd_mixer_elem_set_callback(me, cb);
2195 snd_mixer_elem_set_callback_private(me, userdata);
2196 }
2197
2198 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2199 pa_alsa_element *e;
2200
2201 pa_assert(p);
2202 pa_assert(m);
2203 pa_assert(cb);
2204
2205 PA_LLIST_FOREACH(e, p->elements)
2206 element_set_callback(e, m, cb, userdata);
2207 }
2208
2209 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2210 pa_alsa_path *p;
2211
2212 pa_assert(ps);
2213 pa_assert(m);
2214 pa_assert(cb);
2215
2216 PA_LLIST_FOREACH(p, ps->paths)
2217 pa_alsa_path_set_callback(p, m, cb, userdata);
2218 }
2219
2220 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2221 pa_alsa_path_set *ps;
2222 char **pn = NULL, **en = NULL, **ie;
2223
2224 pa_assert(m);
2225 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2226
2227 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2228 return NULL;
2229
2230 ps = pa_xnew0(pa_alsa_path_set, 1);
2231 ps->direction = direction;
2232
2233 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2234 pn = m->output_path_names;
2235 else if (direction == PA_ALSA_DIRECTION_INPUT)
2236 pn = m->input_path_names;
2237
2238 if (pn) {
2239 char **in;
2240
2241 for (in = pn; *in; in++) {
2242 pa_alsa_path *p;
2243 pa_bool_t duplicate = FALSE;
2244 char **kn, *fn;
2245
2246 for (kn = pn; kn != in; kn++)
2247 if (pa_streq(*kn, *in)) {
2248 duplicate = TRUE;
2249 break;
2250 }
2251
2252 if (duplicate)
2253 continue;
2254
2255 fn = pa_sprintf_malloc("%s.conf", *in);
2256
2257 if ((p = pa_alsa_path_new(fn, direction))) {
2258 p->path_set = ps;
2259 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2260 ps->last_path = p;
2261 }
2262
2263 pa_xfree(fn);
2264 }
2265
2266 return ps;
2267 }
2268
2269 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2270 en = m->output_element;
2271 else if (direction == PA_ALSA_DIRECTION_INPUT)
2272 en = m->input_element;
2273
2274 if (!en) {
2275 pa_alsa_path_set_free(ps);
2276 return NULL;
2277 }
2278
2279 for (ie = en; *ie; ie++) {
2280 char **je;
2281 pa_alsa_path *p;
2282
2283 p = pa_alsa_path_synthesize(*ie, direction);
2284 p->path_set = ps;
2285
2286 /* Mark all other passed elements for require-absent */
2287 for (je = en; *je; je++) {
2288 pa_alsa_element *e;
2289 e = pa_xnew0(pa_alsa_element, 1);
2290 e->path = p;
2291 e->alsa_name = pa_xstrdup(*je);
2292 e->direction = direction;
2293 e->required_absent = PA_ALSA_REQUIRED_ANY;
2294
2295 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2296 p->last_element = e;
2297 }
2298
2299 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2300 ps->last_path = p;
2301 }
2302
2303 return ps;
2304 }
2305
2306 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2307 pa_alsa_path *p;
2308 pa_assert(ps);
2309
2310 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2311 (void*) ps,
2312 ps->direction,
2313 pa_yes_no(ps->probed));
2314
2315 PA_LLIST_FOREACH(p, ps->paths)
2316 pa_alsa_path_dump(p);
2317 }
2318
2319 static void path_set_unify(pa_alsa_path_set *ps) {
2320 pa_alsa_path *p;
2321 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2322 pa_assert(ps);
2323
2324 /* We have issues dealing with paths that vary too wildly. That
2325 * means for now we have to have all paths support volume/mute/dB
2326 * or none. */
2327
2328 PA_LLIST_FOREACH(p, ps->paths) {
2329 pa_assert(p->probed);
2330
2331 if (!p->has_volume)
2332 has_volume = FALSE;
2333 else if (!p->has_dB)
2334 has_dB = FALSE;
2335
2336 if (!p->has_mute)
2337 has_mute = FALSE;
2338 }
2339
2340 if (!has_volume || !has_dB || !has_mute) {
2341
2342 if (!has_volume)
2343 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2344 else if (!has_dB)
2345 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2346
2347 if (!has_mute)
2348 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2349
2350 PA_LLIST_FOREACH(p, ps->paths) {
2351 if (!has_volume)
2352 p->has_volume = FALSE;
2353 else if (!has_dB)
2354 p->has_dB = FALSE;
2355
2356 if (!has_mute)
2357 p->has_mute = FALSE;
2358 }
2359 }
2360 }
2361
2362 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2363 pa_alsa_path *p, *q;
2364
2365 PA_LLIST_FOREACH(p, ps->paths) {
2366 unsigned i;
2367 char *m;
2368
2369 for (q = p->next; q; q = q->next)
2370 if (pa_streq(q->name, p->name))
2371 break;
2372
2373 if (!q)
2374 continue;
2375
2376 m = pa_xstrdup(p->name);
2377
2378 /* OK, this name is not unique, hence let's rename */
2379 for (i = 1, q = p; q; q = q->next) {
2380 char *nn, *nd;
2381
2382 if (!pa_streq(q->name, m))
2383 continue;
2384
2385 nn = pa_sprintf_malloc("%s-%u", m, i);
2386 pa_xfree(q->name);
2387 q->name = nn;
2388
2389 nd = pa_sprintf_malloc("%s %u", q->description, i);
2390 pa_xfree(q->description);
2391 q->description = nd;
2392
2393 i++;
2394 }
2395
2396 pa_xfree(m);
2397 }
2398 }
2399
2400 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2401 pa_alsa_path *p, *n;
2402
2403 pa_assert(ps);
2404
2405 if (ps->probed)
2406 return;
2407
2408 for (p = ps->paths; p; p = n) {
2409 n = p->next;
2410
2411 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2412 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2413 pa_alsa_path_free(p);
2414 }
2415 }
2416
2417 path_set_unify(ps);
2418 path_set_make_paths_unique(ps);
2419 ps->probed = TRUE;
2420 }
2421
2422 static void mapping_free(pa_alsa_mapping *m) {
2423 pa_assert(m);
2424
2425 pa_xfree(m->name);
2426 pa_xfree(m->description);
2427
2428 pa_xstrfreev(m->device_strings);
2429 pa_xstrfreev(m->input_path_names);
2430 pa_xstrfreev(m->output_path_names);
2431 pa_xstrfreev(m->input_element);
2432 pa_xstrfreev(m->output_element);
2433
2434 pa_assert(!m->input_pcm);
2435 pa_assert(!m->output_pcm);
2436
2437 pa_xfree(m);
2438 }
2439
2440 static void profile_free(pa_alsa_profile *p) {
2441 pa_assert(p);
2442
2443 pa_xfree(p->name);
2444 pa_xfree(p->description);
2445
2446 pa_xstrfreev(p->input_mapping_names);
2447 pa_xstrfreev(p->output_mapping_names);
2448
2449 if (p->input_mappings)
2450 pa_idxset_free(p->input_mappings, NULL, NULL);
2451
2452 if (p->output_mappings)
2453 pa_idxset_free(p->output_mappings, NULL, NULL);
2454
2455 pa_xfree(p);
2456 }
2457
2458 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2459 pa_assert(ps);
2460
2461 if (ps->profiles) {
2462 pa_alsa_profile *p;
2463
2464 while ((p = pa_hashmap_steal_first(ps->profiles)))
2465 profile_free(p);
2466
2467 pa_hashmap_free(ps->profiles, NULL, NULL);
2468 }
2469
2470 if (ps->mappings) {
2471 pa_alsa_mapping *m;
2472
2473 while ((m = pa_hashmap_steal_first(ps->mappings)))
2474 mapping_free(m);
2475
2476 pa_hashmap_free(ps->mappings, NULL, NULL);
2477 }
2478
2479 pa_xfree(ps);
2480 }
2481
2482 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2483 pa_alsa_mapping *m;
2484
2485 if (!pa_startswith(name, "Mapping "))
2486 return NULL;
2487
2488 name += 8;
2489
2490 if ((m = pa_hashmap_get(ps->mappings, name)))
2491 return m;
2492
2493 m = pa_xnew0(pa_alsa_mapping, 1);
2494 m->profile_set = ps;
2495 m->name = pa_xstrdup(name);
2496 pa_channel_map_init(&m->channel_map);
2497
2498 pa_hashmap_put(ps->mappings, m->name, m);
2499
2500 return m;
2501 }
2502
2503 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2504 pa_alsa_profile *p;
2505
2506 if (!pa_startswith(name, "Profile "))
2507 return NULL;
2508
2509 name += 8;
2510
2511 if ((p = pa_hashmap_get(ps->profiles, name)))
2512 return p;
2513
2514 p = pa_xnew0(pa_alsa_profile, 1);
2515 p->profile_set = ps;
2516 p->name = pa_xstrdup(name);
2517
2518 pa_hashmap_put(ps->profiles, p->name, p);
2519
2520 return p;
2521 }
2522
2523 static int mapping_parse_device_strings(
2524 const char *filename,
2525 unsigned line,
2526 const char *section,
2527 const char *lvalue,
2528 const char *rvalue,
2529 void *data,
2530 void *userdata) {
2531
2532 pa_alsa_profile_set *ps = userdata;
2533 pa_alsa_mapping *m;
2534
2535 pa_assert(ps);
2536
2537 if (!(m = mapping_get(ps, section))) {
2538 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2539 return -1;
2540 }
2541
2542 pa_xstrfreev(m->device_strings);
2543 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2544 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2545 return -1;
2546 }
2547
2548 return 0;
2549 }
2550
2551 static int mapping_parse_channel_map(
2552 const char *filename,
2553 unsigned line,
2554 const char *section,
2555 const char *lvalue,
2556 const char *rvalue,
2557 void *data,
2558 void *userdata) {
2559
2560 pa_alsa_profile_set *ps = userdata;
2561 pa_alsa_mapping *m;
2562
2563 pa_assert(ps);
2564
2565 if (!(m = mapping_get(ps, section))) {
2566 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2567 return -1;
2568 }
2569
2570 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2571 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2572 return -1;
2573 }
2574
2575 return 0;
2576 }
2577
2578 static int mapping_parse_paths(
2579 const char *filename,
2580 unsigned line,
2581 const char *section,
2582 const char *lvalue,
2583 const char *rvalue,
2584 void *data,
2585 void *userdata) {
2586
2587 pa_alsa_profile_set *ps = userdata;
2588 pa_alsa_mapping *m;
2589
2590 pa_assert(ps);
2591
2592 if (!(m = mapping_get(ps, section))) {
2593 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2594 return -1;
2595 }
2596
2597 if (pa_streq(lvalue, "paths-input")) {
2598 pa_xstrfreev(m->input_path_names);
2599 m->input_path_names = pa_split_spaces_strv(rvalue);
2600 } else {
2601 pa_xstrfreev(m->output_path_names);
2602 m->output_path_names = pa_split_spaces_strv(rvalue);
2603 }
2604
2605 return 0;
2606 }
2607
2608 static int mapping_parse_element(
2609 const char *filename,
2610 unsigned line,
2611 const char *section,
2612 const char *lvalue,
2613 const char *rvalue,
2614 void *data,
2615 void *userdata) {
2616
2617 pa_alsa_profile_set *ps = userdata;
2618 pa_alsa_mapping *m;
2619
2620 pa_assert(ps);
2621
2622 if (!(m = mapping_get(ps, section))) {
2623 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2624 return -1;
2625 }
2626
2627 if (pa_streq(lvalue, "element-input")) {
2628 pa_xstrfreev(m->input_element);
2629 m->input_element = pa_split_spaces_strv(rvalue);
2630 } else {
2631 pa_xstrfreev(m->output_element);
2632 m->output_element = pa_split_spaces_strv(rvalue);
2633 }
2634
2635 return 0;
2636 }
2637
2638 static int mapping_parse_direction(
2639 const char *filename,
2640 unsigned line,
2641 const char *section,
2642 const char *lvalue,
2643 const char *rvalue,
2644 void *data,
2645 void *userdata) {
2646
2647 pa_alsa_profile_set *ps = userdata;
2648 pa_alsa_mapping *m;
2649
2650 pa_assert(ps);
2651
2652 if (!(m = mapping_get(ps, section))) {
2653 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2654 return -1;
2655 }
2656
2657 if (pa_streq(rvalue, "input"))
2658 m->direction = PA_ALSA_DIRECTION_INPUT;
2659 else if (pa_streq(rvalue, "output"))
2660 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2661 else if (pa_streq(rvalue, "any"))
2662 m->direction = PA_ALSA_DIRECTION_ANY;
2663 else {
2664 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2665 return -1;
2666 }
2667
2668 return 0;
2669 }
2670
2671 static int mapping_parse_description(
2672 const char *filename,
2673 unsigned line,
2674 const char *section,
2675 const char *lvalue,
2676 const char *rvalue,
2677 void *data,
2678 void *userdata) {
2679
2680 pa_alsa_profile_set *ps = userdata;
2681 pa_alsa_profile *p;
2682 pa_alsa_mapping *m;
2683
2684 pa_assert(ps);
2685
2686 if ((m = mapping_get(ps, section))) {
2687 pa_xstrdup(m->description);
2688 m->description = pa_xstrdup(rvalue);
2689 } else if ((p = profile_get(ps, section))) {
2690 pa_xfree(p->description);
2691 p->description = pa_xstrdup(rvalue);
2692 } else {
2693 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2694 return -1;
2695 }
2696
2697 return 0;
2698 }
2699
2700 static int mapping_parse_priority(
2701 const char *filename,
2702 unsigned line,
2703 const char *section,
2704 const char *lvalue,
2705 const char *rvalue,
2706 void *data,
2707 void *userdata) {
2708
2709 pa_alsa_profile_set *ps = userdata;
2710 pa_alsa_profile *p;
2711 pa_alsa_mapping *m;
2712 uint32_t prio;
2713
2714 pa_assert(ps);
2715
2716 if (pa_atou(rvalue, &prio) < 0) {
2717 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2718 return -1;
2719 }
2720
2721 if ((m = mapping_get(ps, section)))
2722 m->priority = prio;
2723 else if ((p = profile_get(ps, section)))
2724 p->priority = prio;
2725 else {
2726 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2727 return -1;
2728 }
2729
2730 return 0;
2731 }
2732
2733 static int profile_parse_mappings(
2734 const char *filename,
2735 unsigned line,
2736 const char *section,
2737 const char *lvalue,
2738 const char *rvalue,
2739 void *data,
2740 void *userdata) {
2741
2742 pa_alsa_profile_set *ps = userdata;
2743 pa_alsa_profile *p;
2744
2745 pa_assert(ps);
2746
2747 if (!(p = profile_get(ps, section))) {
2748 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2749 return -1;
2750 }
2751
2752 if (pa_streq(lvalue, "input-mappings")) {
2753 pa_xstrfreev(p->input_mapping_names);
2754 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2755 } else {
2756 pa_xstrfreev(p->output_mapping_names);
2757 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2758 }
2759
2760 return 0;
2761 }
2762
2763 static int profile_parse_skip_probe(
2764 const char *filename,
2765 unsigned line,
2766 const char *section,
2767 const char *lvalue,
2768 const char *rvalue,
2769 void *data,
2770 void *userdata) {
2771
2772 pa_alsa_profile_set *ps = userdata;
2773 pa_alsa_profile *p;
2774 int b;
2775
2776 pa_assert(ps);
2777
2778 if (!(p = profile_get(ps, section))) {
2779 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2780 return -1;
2781 }
2782
2783 if ((b = pa_parse_boolean(rvalue)) < 0) {
2784 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2785 return -1;
2786 }
2787
2788 p->supported = b;
2789
2790 return 0;
2791 }
2792
2793 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2794
2795 static const struct description_map well_known_descriptions[] = {
2796 { "analog-mono", N_("Analog Mono") },
2797 { "analog-stereo", N_("Analog Stereo") },
2798 { "analog-surround-21", N_("Analog Surround 2.1") },
2799 { "analog-surround-30", N_("Analog Surround 3.0") },
2800 { "analog-surround-31", N_("Analog Surround 3.1") },
2801 { "analog-surround-40", N_("Analog Surround 4.0") },
2802 { "analog-surround-41", N_("Analog Surround 4.1") },
2803 { "analog-surround-50", N_("Analog Surround 5.0") },
2804 { "analog-surround-51", N_("Analog Surround 5.1") },
2805 { "analog-surround-61", N_("Analog Surround 6.0") },
2806 { "analog-surround-61", N_("Analog Surround 6.1") },
2807 { "analog-surround-70", N_("Analog Surround 7.0") },
2808 { "analog-surround-71", N_("Analog Surround 7.1") },
2809 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2810 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2811 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2812 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2813 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2814 };
2815
2816 pa_assert(m);
2817
2818 if (!pa_channel_map_valid(&m->channel_map)) {
2819 pa_log("Mapping %s is missing channel map.", m->name);
2820 return -1;
2821 }
2822
2823 if (!m->device_strings) {
2824 pa_log("Mapping %s is missing device strings.", m->name);
2825 return -1;
2826 }
2827
2828 if ((m->input_path_names && m->input_element) ||
2829 (m->output_path_names && m->output_element)) {
2830 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
2831 return -1;
2832 }
2833
2834 if (!m->description)
2835 m->description = pa_xstrdup(lookup_description(m->name,
2836 well_known_descriptions,
2837 PA_ELEMENTSOF(well_known_descriptions)));
2838
2839 if (!m->description)
2840 m->description = pa_xstrdup(m->name);
2841
2842 if (bonus) {
2843 if (pa_channel_map_equal(&m->channel_map, bonus))
2844 m->priority += 50;
2845 else if (m->channel_map.channels == bonus->channels)
2846 m->priority += 30;
2847 }
2848
2849 return 0;
2850 }
2851
2852 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
2853 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
2854
2855 pa_assert(m);
2856
2857 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2858 m->name,
2859 pa_strnull(m->description),
2860 m->priority,
2861 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
2862 pa_yes_no(m->supported),
2863 m->direction);
2864 }
2865
2866 static void profile_set_add_auto_pair(
2867 pa_alsa_profile_set *ps,
2868 pa_alsa_mapping *m, /* output */
2869 pa_alsa_mapping *n /* input */) {
2870
2871 char *name;
2872 pa_alsa_profile *p;
2873
2874 pa_assert(ps);
2875 pa_assert(m || n);
2876
2877 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
2878 return;
2879
2880 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
2881 return;
2882
2883 if (m && n)
2884 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
2885 else if (m)
2886 name = pa_sprintf_malloc("output:%s", m->name);
2887 else
2888 name = pa_sprintf_malloc("input:%s", n->name);
2889
2890 if (pa_hashmap_get(ps->profiles, name)) {
2891 pa_xfree(name);
2892 return;
2893 }
2894
2895 p = pa_xnew0(pa_alsa_profile, 1);
2896 p->profile_set = ps;
2897 p->name = name;
2898
2899 if (m) {
2900 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2901 pa_idxset_put(p->output_mappings, m, NULL);
2902 p->priority += m->priority * 100;
2903 }
2904
2905 if (n) {
2906 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2907 pa_idxset_put(p->input_mappings, n, NULL);
2908 p->priority += n->priority;
2909 }
2910
2911 pa_hashmap_put(ps->profiles, p->name, p);
2912 }
2913
2914 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
2915 pa_alsa_mapping *m, *n;
2916 void *m_state, *n_state;
2917
2918 pa_assert(ps);
2919
2920 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
2921 profile_set_add_auto_pair(ps, m, NULL);
2922
2923 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2924 profile_set_add_auto_pair(ps, m, n);
2925 }
2926
2927 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
2928 profile_set_add_auto_pair(ps, NULL, n);
2929 }
2930
2931 static int profile_verify(pa_alsa_profile *p) {
2932
2933 static const struct description_map well_known_descriptions[] = {
2934 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2935 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2936 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2937 { "off", N_("Off") }
2938 };
2939
2940 pa_assert(p);
2941
2942 /* Replace the output mapping names by the actual mappings */
2943 if (p->output_mapping_names) {
2944 char **name;
2945
2946 pa_assert(!p->output_mappings);
2947 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2948
2949 for (name = p->output_mapping_names; *name; name++) {
2950 pa_alsa_mapping *m;
2951 char **in;
2952 pa_bool_t duplicate = FALSE;
2953
2954 for (in = name + 1; *in; in++)
2955 if (pa_streq(*name, *in)) {
2956 duplicate = TRUE;
2957 break;
2958 }
2959
2960 if (duplicate)
2961 continue;
2962
2963 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
2964 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
2965 return -1;
2966 }
2967
2968 pa_idxset_put(p->output_mappings, m, NULL);
2969
2970 if (p->supported)
2971 m->supported++;
2972 }
2973
2974 pa_xstrfreev(p->output_mapping_names);
2975 p->output_mapping_names = NULL;
2976 }
2977
2978 /* Replace the input mapping names by the actual mappings */
2979 if (p->input_mapping_names) {
2980 char **name;
2981
2982 pa_assert(!p->input_mappings);
2983 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2984
2985 for (name = p->input_mapping_names; *name; name++) {
2986 pa_alsa_mapping *m;
2987 char **in;
2988 pa_bool_t duplicate = FALSE;
2989
2990 for (in = name + 1; *in; in++)
2991 if (pa_streq(*name, *in)) {
2992 duplicate = TRUE;
2993 break;
2994 }
2995
2996 if (duplicate)
2997 continue;
2998
2999 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3000 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3001 return -1;
3002 }
3003
3004 pa_idxset_put(p->input_mappings, m, NULL);
3005
3006 if (p->supported)
3007 m->supported++;
3008 }
3009
3010 pa_xstrfreev(p->input_mapping_names);
3011 p->input_mapping_names = NULL;
3012 }
3013
3014 if (!p->input_mappings && !p->output_mappings) {
3015 pa_log("Profile '%s' lacks mappings.", p->name);
3016 return -1;
3017 }
3018
3019 if (!p->description)
3020 p->description = pa_xstrdup(lookup_description(p->name,
3021 well_known_descriptions,
3022 PA_ELEMENTSOF(well_known_descriptions)));
3023
3024 if (!p->description) {
3025 pa_strbuf *sb;
3026 uint32_t idx;
3027 pa_alsa_mapping *m;
3028
3029 sb = pa_strbuf_new();
3030
3031 if (p->output_mappings)
3032 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3033 if (!pa_strbuf_isempty(sb))
3034 pa_strbuf_puts(sb, " + ");
3035
3036 pa_strbuf_printf(sb, "%s Output", m->description);
3037 }
3038
3039 if (p->input_mappings)
3040 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3041 if (!pa_strbuf_isempty(sb))
3042 pa_strbuf_puts(sb, " + ");
3043
3044 pa_strbuf_printf(sb, "%s Input", m->description);
3045 }
3046
3047 p->description = pa_strbuf_tostring_free(sb);
3048 }
3049
3050 return 0;
3051 }
3052
3053 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3054 uint32_t idx;
3055 pa_alsa_mapping *m;
3056 pa_assert(p);
3057
3058 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3059 p->name,
3060 pa_strnull(p->description),
3061 p->priority,
3062 pa_yes_no(p->supported),
3063 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3064 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3065
3066 if (p->input_mappings)
3067 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3068 pa_log_debug("Input %s", m->name);
3069
3070 if (p->output_mappings)
3071 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3072 pa_log_debug("Output %s", m->name);
3073 }
3074
3075 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3076 pa_alsa_profile_set *ps;
3077 pa_alsa_profile *p;
3078 pa_alsa_mapping *m;
3079 char *fn;
3080 int r;
3081 void *state;
3082
3083 static pa_config_item items[] = {
3084 /* [General] */
3085 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3086
3087 /* [Mapping ...] */
3088 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3089 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3090 { "paths-input", mapping_parse_paths, NULL, NULL },
3091 { "paths-output", mapping_parse_paths, NULL, NULL },
3092 { "element-input", mapping_parse_element, NULL, NULL },
3093 { "element-output", mapping_parse_element, NULL, NULL },
3094 { "direction", mapping_parse_direction, NULL, NULL },
3095
3096 /* Shared by [Mapping ...] and [Profile ...] */
3097 { "description", mapping_parse_description, NULL, NULL },
3098 { "priority", mapping_parse_priority, NULL, NULL },
3099
3100 /* [Profile ...] */
3101 { "input-mappings", profile_parse_mappings, NULL, NULL },
3102 { "output-mappings", profile_parse_mappings, NULL, NULL },
3103 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3104 { NULL, NULL, NULL, NULL }
3105 };
3106
3107 ps = pa_xnew0(pa_alsa_profile_set, 1);
3108 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3109 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3110
3111 items[0].data = &ps->auto_profiles;
3112
3113 if (!fname)
3114 fname = "default.conf";
3115
3116 fn = pa_maybe_prefix_path(fname,
3117 #if defined(__linux__) && !defined(__OPTIMIZE__)
3118 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3119 #endif
3120 PA_ALSA_PROFILE_SETS_DIR);
3121
3122 r = pa_config_parse(fn, NULL, items, ps);
3123 pa_xfree(fn);
3124
3125 if (r < 0)
3126 goto fail;
3127
3128 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3129 if (mapping_verify(m, bonus) < 0)
3130 goto fail;
3131
3132 if (ps->auto_profiles)
3133 profile_set_add_auto(ps);
3134
3135 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3136 if (profile_verify(p) < 0)
3137 goto fail;
3138
3139 return ps;
3140
3141 fail:
3142 pa_alsa_profile_set_free(ps);
3143 return NULL;
3144 }
3145
3146 void pa_alsa_profile_set_probe(
3147 pa_alsa_profile_set *ps,
3148 const char *dev_id,
3149 const pa_sample_spec *ss,
3150 unsigned default_n_fragments,
3151 unsigned default_fragment_size_msec) {
3152
3153 void *state;
3154 pa_alsa_profile *p, *last = NULL;
3155 pa_alsa_mapping *m;
3156
3157 pa_assert(ps);
3158 pa_assert(dev_id);
3159 pa_assert(ss);
3160
3161 if (ps->probed)
3162 return;
3163
3164 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3165 pa_sample_spec try_ss;
3166 pa_channel_map try_map;
3167 snd_pcm_uframes_t try_period_size, try_buffer_size;
3168 uint32_t idx;
3169
3170 /* Is this already marked that it is supported? (i.e. from the config file) */
3171 if (p->supported)
3172 continue;
3173
3174 pa_log_debug("Looking at profile %s", p->name);
3175
3176 /* Close PCMs from the last iteration we don't need anymore */
3177 if (last && last->output_mappings)
3178 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3179
3180 if (!m->output_pcm)
3181 break;
3182
3183 if (last->supported)
3184 m->supported++;
3185
3186 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3187 snd_pcm_close(m->output_pcm);
3188 m->output_pcm = NULL;
3189 }
3190 }
3191
3192 if (last && last->input_mappings)
3193 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3194
3195 if (!m->input_pcm)
3196 break;
3197
3198 if (last->supported)
3199 m->supported++;
3200
3201 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3202 snd_pcm_close(m->input_pcm);
3203 m->input_pcm = NULL;
3204 }
3205 }
3206
3207 p->supported = TRUE;
3208
3209 /* Check if we can open all new ones */
3210 if (p->output_mappings)
3211 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3212
3213 if (m->output_pcm)
3214 continue;
3215
3216 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3217 try_map = m->channel_map;
3218 try_ss = *ss;
3219 try_ss.channels = try_map.channels;
3220
3221 try_period_size =
3222 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3223 pa_frame_size(&try_ss);
3224 try_buffer_size = default_n_fragments * try_period_size;
3225
3226 if (!(m ->output_pcm = pa_alsa_open_by_template(
3227 m->device_strings,
3228 dev_id,
3229 NULL,
3230 &try_ss, &try_map,
3231 SND_PCM_STREAM_PLAYBACK,
3232 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3233 TRUE))) {
3234 p->supported = FALSE;
3235 break;
3236 }
3237 }
3238
3239 if (p->input_mappings && p->supported)
3240 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3241
3242 if (m->input_pcm)
3243 continue;
3244
3245 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3246 try_map = m->channel_map;
3247 try_ss = *ss;
3248 try_ss.channels = try_map.channels;
3249
3250 try_period_size =
3251 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3252 pa_frame_size(&try_ss);
3253 try_buffer_size = default_n_fragments * try_period_size;
3254
3255 if (!(m ->input_pcm = pa_alsa_open_by_template(
3256 m->device_strings,
3257 dev_id,
3258 NULL,
3259 &try_ss, &try_map,
3260 SND_PCM_STREAM_CAPTURE,
3261 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3262 TRUE))) {
3263 p->supported = FALSE;
3264 break;
3265 }
3266 }
3267
3268 last = p;
3269
3270 if (p->supported)
3271 pa_log_debug("Profile %s supported.", p->name);
3272 }
3273
3274 /* Clean up */
3275 if (last) {
3276 uint32_t idx;
3277
3278 if (last->output_mappings)
3279 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3280 if (m->output_pcm) {
3281
3282 if (last->supported)
3283 m->supported++;
3284
3285 snd_pcm_close(m->output_pcm);
3286 m->output_pcm = NULL;
3287 }
3288
3289 if (last->input_mappings)
3290 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3291 if (m->input_pcm) {
3292
3293 if (last->supported)
3294 m->supported++;
3295
3296 snd_pcm_close(m->input_pcm);
3297 m->input_pcm = NULL;
3298 }
3299 }
3300
3301 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3302 if (!p->supported) {
3303 pa_hashmap_remove(ps->profiles, p->name);
3304 profile_free(p);
3305 }
3306
3307 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3308 if (m->supported <= 0) {
3309 pa_hashmap_remove(ps->mappings, m->name);
3310 mapping_free(m);
3311 }
3312
3313 ps->probed = TRUE;
3314 }
3315
3316 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3317 pa_alsa_profile *p;
3318 pa_alsa_mapping *m;
3319 void *state;
3320
3321 pa_assert(ps);
3322
3323 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3324 (void*)
3325 ps,
3326 pa_yes_no(ps->auto_profiles),
3327 pa_yes_no(ps->probed),
3328 pa_hashmap_size(ps->mappings),
3329 pa_hashmap_size(ps->profiles));
3330
3331 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3332 pa_alsa_mapping_dump(m);
3333
3334 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3335 pa_alsa_profile_dump(p);
3336 }
3337
3338 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3339 pa_alsa_path *path;
3340
3341 pa_assert(p);
3342 pa_assert(!*p);
3343 pa_assert(ps);
3344
3345 /* if there is no path, we don't want a port list */
3346 if (!ps->paths)
3347 return;
3348
3349 if (!ps->paths->next){
3350 pa_alsa_setting *s;
3351
3352 /* If there is only one path, but no or only one setting, then
3353 * we want a port list either */
3354 if (!ps->paths->settings || !ps->paths->settings->next)
3355 return;
3356
3357 /* Ok, there is only one path, however with multiple settings,
3358 * so let's create a port for each setting */
3359 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3360
3361 PA_LLIST_FOREACH(s, ps->paths->settings) {
3362 pa_device_port *port;
3363 pa_alsa_port_data *data;
3364
3365 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3366 port->priority = s->priority;
3367
3368 data = PA_DEVICE_PORT_DATA(port);
3369 data->path = ps->paths;
3370 data->setting = s;
3371
3372 pa_hashmap_put(*p, port->name, port);
3373 }
3374
3375 } else {
3376
3377 /* We have multiple paths, so let's create a port for each
3378 * one, and each of each settings */
3379 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3380
3381 PA_LLIST_FOREACH(path, ps->paths) {
3382
3383 if (!path->settings || !path->settings->next) {
3384 pa_device_port *port;
3385 pa_alsa_port_data *data;
3386
3387 /* If there is no or just one setting we only need a
3388 * single entry */
3389
3390 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3391 port->priority = path->priority * 100;
3392
3393
3394 data = PA_DEVICE_PORT_DATA(port);
3395 data->path = path;
3396 data->setting = path->settings;
3397
3398 pa_hashmap_put(*p, port->name, port);
3399 } else {
3400 pa_alsa_setting *s;
3401
3402 PA_LLIST_FOREACH(s, path->settings) {
3403 pa_device_port *port;
3404 pa_alsa_port_data *data;
3405 char *n, *d;
3406
3407 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3408
3409 if (s->description[0])
3410 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3411 else
3412 d = pa_xstrdup(path->description);
3413
3414 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3415 port->priority = path->priority * 100 + s->priority;
3416
3417 pa_xfree(n);
3418 pa_xfree(d);
3419
3420 data = PA_DEVICE_PORT_DATA(port);
3421 data->path = path;
3422 data->setting = s;
3423
3424 pa_hashmap_put(*p, port->name, port);
3425 }
3426 }
3427 }
3428 }
3429
3430 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3431 }