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