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