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