]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa-mixer: add required-any and required-* for enum options
[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 if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
1125 switch (e->required_any) {
1126 case PA_ALSA_REQUIRED_VOLUME:
1127 e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
1128 break;
1129 case PA_ALSA_REQUIRED_SWITCH:
1130 e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
1131 break;
1132 case PA_ALSA_REQUIRED_ENUMERATION:
1133 e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1134 break;
1135 case PA_ALSA_REQUIRED_ANY:
1136 e->path->req_any_present |=
1137 (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
1138 (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
1139 (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
1140 break;
1141 }
1142 }
1143
1144 if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1145 pa_alsa_option *o;
1146 PA_LLIST_FOREACH(o, e->options) {
1147 e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
1148 (o->alsa_idx >= 0);
1149 if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
1150 return -1;
1151 if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
1152 return -1;
1153 }
1154 }
1155
1156 if (check_required(e, me) < 0)
1157 return -1;
1158
1159 return 0;
1160 }
1161
1162 static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
1163 snd_mixer_selem_id_t *sid;
1164 snd_mixer_elem_t *me;
1165
1166 pa_assert(m);
1167 pa_assert(e);
1168
1169 SELEM_INIT(sid, e->alsa_name);
1170
1171 if (!(me = snd_mixer_find_selem(m, sid))) {
1172
1173 if (e->required != PA_ALSA_REQUIRED_IGNORE)
1174 return -1;
1175
1176 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1177 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1178 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1179
1180 return 0;
1181 }
1182
1183 if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
1184 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1185
1186 if (!snd_mixer_selem_has_playback_switch(me)) {
1187 if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
1188 e->direction = PA_ALSA_DIRECTION_INPUT;
1189 else
1190 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1191 }
1192
1193 } else {
1194
1195 if (!snd_mixer_selem_has_capture_switch(me)) {
1196 if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
1197 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1198 else
1199 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1200 }
1201 }
1202
1203 if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
1204 e->direction_try_other = FALSE;
1205 }
1206
1207 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1208
1209 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
1210
1211 if (!snd_mixer_selem_has_playback_volume(me)) {
1212 if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
1213 e->direction = PA_ALSA_DIRECTION_INPUT;
1214 else
1215 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1216 }
1217
1218 } else {
1219
1220 if (!snd_mixer_selem_has_capture_volume(me)) {
1221 if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
1222 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1223 else
1224 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1225 }
1226 }
1227
1228 if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
1229 long min_dB = 0, max_dB = 0;
1230 int r;
1231
1232 e->direction_try_other = FALSE;
1233
1234 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1235 e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
1236 else
1237 e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
1238
1239 if (e->has_dB) {
1240 #ifdef HAVE_VALGRIND_MEMCHECK_H
1241 VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB));
1242 VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB));
1243 #endif
1244
1245 e->min_dB = ((double) min_dB) / 100.0;
1246 e->max_dB = ((double) max_dB) / 100.0;
1247
1248 if (min_dB >= max_dB) {
1249 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);
1250 e->has_dB = FALSE;
1251 }
1252 }
1253
1254 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1255 r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
1256 else
1257 r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
1258
1259 if (r < 0) {
1260 pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
1261 return -1;
1262 }
1263
1264
1265 if (e->min_volume >= e->max_volume) {
1266 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);
1267 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1268
1269 } else {
1270 pa_bool_t is_mono;
1271 pa_channel_position_t p;
1272
1273 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1274 is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
1275 else
1276 is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
1277
1278 if (is_mono) {
1279 e->n_channels = 1;
1280
1281 if (!e->override_map) {
1282 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1283 e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
1284 e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
1285 }
1286
1287 e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
1288 } else {
1289 e->n_channels = 0;
1290 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1291
1292 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1293 continue;
1294
1295 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1296 e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1297 else
1298 e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1299 }
1300
1301 if (e->n_channels <= 0) {
1302 pa_log_warn("Volume element %s with no channels?", e->alsa_name);
1303 return -1;
1304 }
1305
1306 if (!e->override_map) {
1307 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
1308 pa_bool_t has_channel;
1309
1310 if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
1311 continue;
1312
1313 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1314 has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
1315 else
1316 has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
1317
1318 e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
1319 }
1320 }
1321
1322 e->merged_mask = 0;
1323 for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++)
1324 e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
1325 }
1326 }
1327 }
1328
1329 }
1330
1331 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1332 pa_alsa_option *o;
1333
1334 PA_LLIST_FOREACH(o, e->options)
1335 o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
1336 } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1337 int n;
1338 pa_alsa_option *o;
1339
1340 if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
1341 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
1342 return -1;
1343 }
1344
1345 PA_LLIST_FOREACH(o, e->options) {
1346 int i;
1347
1348 for (i = 0; i < n; i++) {
1349 char buf[128];
1350
1351 if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
1352 continue;
1353
1354 if (!pa_streq(buf, o->alsa_name))
1355 continue;
1356
1357 o->alsa_idx = i;
1358 }
1359 }
1360 }
1361
1362 return 0;
1363 }
1364
1365 static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_bool_t prefixed) {
1366 pa_alsa_element *e;
1367
1368 pa_assert(p);
1369 pa_assert(section);
1370
1371 if (prefixed) {
1372 if (!pa_startswith(section, "Element "))
1373 return NULL;
1374
1375 section += 8;
1376 }
1377
1378 /* This is not an element section, but an enum section? */
1379 if (strchr(section, ':'))
1380 return NULL;
1381
1382 if (p->last_element && pa_streq(p->last_element->alsa_name, section))
1383 return p->last_element;
1384
1385 PA_LLIST_FOREACH(e, p->elements)
1386 if (pa_streq(e->alsa_name, section))
1387 goto finish;
1388
1389 e = pa_xnew0(pa_alsa_element, 1);
1390 e->path = p;
1391 e->alsa_name = pa_xstrdup(section);
1392 e->direction = p->direction;
1393
1394 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
1395
1396 finish:
1397 p->last_element = e;
1398 return e;
1399 }
1400
1401 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1402 char *en;
1403 const char *on;
1404 pa_alsa_option *o;
1405 pa_alsa_element *e;
1406
1407 if (!pa_startswith(section, "Option "))
1408 return NULL;
1409
1410 section += 7;
1411
1412 /* This is not an enum section, but an element section? */
1413 if (!(on = strchr(section, ':')))
1414 return NULL;
1415
1416 en = pa_xstrndup(section, on - section);
1417 on++;
1418
1419 if (p->last_option &&
1420 pa_streq(p->last_option->element->alsa_name, en) &&
1421 pa_streq(p->last_option->alsa_name, on)) {
1422 pa_xfree(en);
1423 return p->last_option;
1424 }
1425
1426 pa_assert_se(e = element_get(p, en, FALSE));
1427 pa_xfree(en);
1428
1429 PA_LLIST_FOREACH(o, e->options)
1430 if (pa_streq(o->alsa_name, on))
1431 goto finish;
1432
1433 o = pa_xnew0(pa_alsa_option, 1);
1434 o->element = e;
1435 o->alsa_name = pa_xstrdup(on);
1436 o->alsa_idx = -1;
1437
1438 if (p->last_option && p->last_option->element == e)
1439 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1440 else
1441 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1442
1443 finish:
1444 p->last_option = o;
1445 return o;
1446 }
1447
1448 static int element_parse_switch(
1449 const char *filename,
1450 unsigned line,
1451 const char *section,
1452 const char *lvalue,
1453 const char *rvalue,
1454 void *data,
1455 void *userdata) {
1456
1457 pa_alsa_path *p = userdata;
1458 pa_alsa_element *e;
1459
1460 pa_assert(p);
1461
1462 if (!(e = element_get(p, section, TRUE))) {
1463 pa_log("[%s:%u] Switch makes no sense in '%s'", filename, line, section);
1464 return -1;
1465 }
1466
1467 if (pa_streq(rvalue, "ignore"))
1468 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1469 else if (pa_streq(rvalue, "mute"))
1470 e->switch_use = PA_ALSA_SWITCH_MUTE;
1471 else if (pa_streq(rvalue, "off"))
1472 e->switch_use = PA_ALSA_SWITCH_OFF;
1473 else if (pa_streq(rvalue, "on"))
1474 e->switch_use = PA_ALSA_SWITCH_ON;
1475 else if (pa_streq(rvalue, "select"))
1476 e->switch_use = PA_ALSA_SWITCH_SELECT;
1477 else {
1478 pa_log("[%s:%u] Switch invalid of '%s'", filename, line, section);
1479 return -1;
1480 }
1481
1482 return 0;
1483 }
1484
1485 static int element_parse_volume(
1486 const char *filename,
1487 unsigned line,
1488 const char *section,
1489 const char *lvalue,
1490 const char *rvalue,
1491 void *data,
1492 void *userdata) {
1493
1494 pa_alsa_path *p = userdata;
1495 pa_alsa_element *e;
1496
1497 pa_assert(p);
1498
1499 if (!(e = element_get(p, section, TRUE))) {
1500 pa_log("[%s:%u] Volume makes no sense in '%s'", filename, line, section);
1501 return -1;
1502 }
1503
1504 if (pa_streq(rvalue, "ignore"))
1505 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1506 else if (pa_streq(rvalue, "merge"))
1507 e->volume_use = PA_ALSA_VOLUME_MERGE;
1508 else if (pa_streq(rvalue, "off"))
1509 e->volume_use = PA_ALSA_VOLUME_OFF;
1510 else if (pa_streq(rvalue, "zero"))
1511 e->volume_use = PA_ALSA_VOLUME_ZERO;
1512 else {
1513 pa_log("[%s:%u] Volume invalid of '%s'", filename, line, section);
1514 return -1;
1515 }
1516
1517 return 0;
1518 }
1519
1520 static int element_parse_enumeration(
1521 const char *filename,
1522 unsigned line,
1523 const char *section,
1524 const char *lvalue,
1525 const char *rvalue,
1526 void *data,
1527 void *userdata) {
1528
1529 pa_alsa_path *p = userdata;
1530 pa_alsa_element *e;
1531
1532 pa_assert(p);
1533
1534 if (!(e = element_get(p, section, TRUE))) {
1535 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename, line, section);
1536 return -1;
1537 }
1538
1539 if (pa_streq(rvalue, "ignore"))
1540 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1541 else if (pa_streq(rvalue, "select"))
1542 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1543 else {
1544 pa_log("[%s:%u] Enumeration invalid of '%s'", filename, line, section);
1545 return -1;
1546 }
1547
1548 return 0;
1549 }
1550
1551 static int option_parse_priority(
1552 const char *filename,
1553 unsigned line,
1554 const char *section,
1555 const char *lvalue,
1556 const char *rvalue,
1557 void *data,
1558 void *userdata) {
1559
1560 pa_alsa_path *p = userdata;
1561 pa_alsa_option *o;
1562 uint32_t prio;
1563
1564 pa_assert(p);
1565
1566 if (!(o = option_get(p, section))) {
1567 pa_log("[%s:%u] Priority makes no sense in '%s'", filename, line, section);
1568 return -1;
1569 }
1570
1571 if (pa_atou(rvalue, &prio) < 0) {
1572 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
1573 return -1;
1574 }
1575
1576 o->priority = prio;
1577 return 0;
1578 }
1579
1580 static int option_parse_name(
1581 const char *filename,
1582 unsigned line,
1583 const char *section,
1584 const char *lvalue,
1585 const char *rvalue,
1586 void *data,
1587 void *userdata) {
1588
1589 pa_alsa_path *p = userdata;
1590 pa_alsa_option *o;
1591
1592 pa_assert(p);
1593
1594 if (!(o = option_get(p, section))) {
1595 pa_log("[%s:%u] Name makes no sense in '%s'", filename, line, section);
1596 return -1;
1597 }
1598
1599 pa_xfree(o->name);
1600 o->name = pa_xstrdup(rvalue);
1601
1602 return 0;
1603 }
1604
1605 static int element_parse_required(
1606 const char *filename,
1607 unsigned line,
1608 const char *section,
1609 const char *lvalue,
1610 const char *rvalue,
1611 void *data,
1612 void *userdata) {
1613
1614 pa_alsa_path *p = userdata;
1615 pa_alsa_element *e;
1616 pa_alsa_option *o;
1617 pa_alsa_required_t req;
1618
1619 pa_assert(p);
1620
1621 e = element_get(p, section, TRUE);
1622 o = option_get(p, section);
1623 if (!e && !o) {
1624 pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section);
1625 return -1;
1626 }
1627
1628 if (pa_streq(rvalue, "ignore"))
1629 req = PA_ALSA_REQUIRED_IGNORE;
1630 else if (pa_streq(rvalue, "switch") && e)
1631 req = PA_ALSA_REQUIRED_SWITCH;
1632 else if (pa_streq(rvalue, "volume") && e)
1633 req = PA_ALSA_REQUIRED_VOLUME;
1634 else if (pa_streq(rvalue, "enumeration"))
1635 req = PA_ALSA_REQUIRED_ENUMERATION;
1636 else if (pa_streq(rvalue, "any"))
1637 req = PA_ALSA_REQUIRED_ANY;
1638 else {
1639 pa_log("[%s:%u] Required invalid of '%s'", filename, line, section);
1640 return -1;
1641 }
1642
1643 if (pa_streq(lvalue, "required-absent")) {
1644 if (e)
1645 e->required_absent = req;
1646 if (o)
1647 o->required_absent = req;
1648 }
1649 else if (pa_streq(lvalue, "required-any")) {
1650 if (e) {
1651 e->required_any = req;
1652 e->path->has_req_any = TRUE;
1653 }
1654 if (o) {
1655 o->required_any = req;
1656 o->element->path->has_req_any = TRUE;
1657 }
1658 }
1659 else {
1660 if (e)
1661 e->required = req;
1662 if (o)
1663 o->required = req;
1664 }
1665
1666 return 0;
1667 }
1668
1669 static int element_parse_direction(
1670 const char *filename,
1671 unsigned line,
1672 const char *section,
1673 const char *lvalue,
1674 const char *rvalue,
1675 void *data,
1676 void *userdata) {
1677
1678 pa_alsa_path *p = userdata;
1679 pa_alsa_element *e;
1680
1681 pa_assert(p);
1682
1683 if (!(e = element_get(p, section, TRUE))) {
1684 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1685 return -1;
1686 }
1687
1688 if (pa_streq(rvalue, "playback"))
1689 e->direction = PA_ALSA_DIRECTION_OUTPUT;
1690 else if (pa_streq(rvalue, "capture"))
1691 e->direction = PA_ALSA_DIRECTION_INPUT;
1692 else {
1693 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1694 return -1;
1695 }
1696
1697 return 0;
1698 }
1699
1700 static int element_parse_direction_try_other(
1701 const char *filename,
1702 unsigned line,
1703 const char *section,
1704 const char *lvalue,
1705 const char *rvalue,
1706 void *data,
1707 void *userdata) {
1708
1709 pa_alsa_path *p = userdata;
1710 pa_alsa_element *e;
1711 int yes;
1712
1713 if (!(e = element_get(p, section, TRUE))) {
1714 pa_log("[%s:%u] Direction makes no sense in '%s'", filename, line, section);
1715 return -1;
1716 }
1717
1718 if ((yes = pa_parse_boolean(rvalue)) < 0) {
1719 pa_log("[%s:%u] Direction invalid of '%s'", filename, line, section);
1720 return -1;
1721 }
1722
1723 e->direction_try_other = !!yes;
1724 return 0;
1725 }
1726
1727 static pa_channel_position_mask_t parse_mask(const char *m) {
1728 pa_channel_position_mask_t v;
1729
1730 if (pa_streq(m, "all-left"))
1731 v = PA_CHANNEL_POSITION_MASK_LEFT;
1732 else if (pa_streq(m, "all-right"))
1733 v = PA_CHANNEL_POSITION_MASK_RIGHT;
1734 else if (pa_streq(m, "all-center"))
1735 v = PA_CHANNEL_POSITION_MASK_CENTER;
1736 else if (pa_streq(m, "all-front"))
1737 v = PA_CHANNEL_POSITION_MASK_FRONT;
1738 else if (pa_streq(m, "all-rear"))
1739 v = PA_CHANNEL_POSITION_MASK_REAR;
1740 else if (pa_streq(m, "all-side"))
1741 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
1742 else if (pa_streq(m, "all-top"))
1743 v = PA_CHANNEL_POSITION_MASK_TOP;
1744 else if (pa_streq(m, "all-no-lfe"))
1745 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
1746 else if (pa_streq(m, "all"))
1747 v = PA_CHANNEL_POSITION_MASK_ALL;
1748 else {
1749 pa_channel_position_t p;
1750
1751 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
1752 return 0;
1753
1754 v = PA_CHANNEL_POSITION_MASK(p);
1755 }
1756
1757 return v;
1758 }
1759
1760 static int element_parse_override_map(
1761 const char *filename,
1762 unsigned line,
1763 const char *section,
1764 const char *lvalue,
1765 const char *rvalue,
1766 void *data,
1767 void *userdata) {
1768
1769 pa_alsa_path *p = userdata;
1770 pa_alsa_element *e;
1771 const char *state = NULL;
1772 unsigned i = 0;
1773 char *n;
1774
1775 if (!(e = element_get(p, section, TRUE))) {
1776 pa_log("[%s:%u] Override map makes no sense in '%s'", filename, line, section);
1777 return -1;
1778 }
1779
1780 while ((n = pa_split(rvalue, ",", &state))) {
1781 pa_channel_position_mask_t m;
1782
1783 if (!*n)
1784 m = 0;
1785 else {
1786 if ((m = parse_mask(n)) == 0) {
1787 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename, line, n, section);
1788 pa_xfree(n);
1789 return -1;
1790 }
1791 }
1792
1793 if (pa_streq(lvalue, "override-map.1"))
1794 e->masks[i++][0] = m;
1795 else
1796 e->masks[i++][1] = m;
1797
1798 /* Later on we might add override-map.3 and so on here ... */
1799
1800 pa_xfree(n);
1801 }
1802
1803 e->override_map = TRUE;
1804
1805 return 0;
1806 }
1807
1808 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
1809 snd_mixer_selem_id_t *sid;
1810 snd_mixer_elem_t *me;
1811 int r;
1812
1813 pa_assert(e);
1814 pa_assert(m);
1815
1816 SELEM_INIT(sid, e->alsa_name);
1817 if (!(me = snd_mixer_find_selem(m, sid))) {
1818 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
1819 return -1;
1820 }
1821
1822 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
1823
1824 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
1825 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
1826 else
1827 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
1828
1829 if (r < 0)
1830 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1831
1832 } else {
1833 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
1834
1835 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
1836 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
1837 }
1838
1839 return r;
1840 }
1841
1842 int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
1843 pa_alsa_option *o;
1844 uint32_t idx;
1845
1846 pa_assert(s);
1847 pa_assert(m);
1848
1849 PA_IDXSET_FOREACH(o, s->options, idx)
1850 element_set_option(o->element, m, o->alsa_idx);
1851
1852 return 0;
1853 }
1854
1855 static int option_verify(pa_alsa_option *o) {
1856 static const struct description_map well_known_descriptions[] = {
1857 { "input", N_("Input") },
1858 { "input-docking", N_("Docking Station Input") },
1859 { "input-docking-microphone", N_("Docking Station Microphone") },
1860 { "input-docking-linein", N_("Docking Station Line-In") },
1861 { "input-linein", N_("Line-In") },
1862 { "input-microphone", N_("Microphone") },
1863 { "input-microphone-front", N_("Front Microphone") },
1864 { "input-microphone-rear", N_("Rear Microphone") },
1865 { "input-microphone-external", N_("External Microphone") },
1866 { "input-microphone-internal", N_("Internal Microphone") },
1867 { "input-radio", N_("Radio") },
1868 { "input-video", N_("Video") },
1869 { "input-agc-on", N_("Automatic Gain Control") },
1870 { "input-agc-off", N_("No Automatic Gain Control") },
1871 { "input-boost-on", N_("Boost") },
1872 { "input-boost-off", N_("No Boost") },
1873 { "output-amplifier-on", N_("Amplifier") },
1874 { "output-amplifier-off", N_("No Amplifier") },
1875 { "output-bass-boost-on", N_("Bass Boost") },
1876 { "output-bass-boost-off", N_("No Bass Boost") },
1877 { "output-speaker", N_("Speaker") },
1878 { "output-headphones", N_("Headphones") }
1879 };
1880
1881 pa_assert(o);
1882
1883 if (!o->name) {
1884 pa_log("No name set for option %s", o->alsa_name);
1885 return -1;
1886 }
1887
1888 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
1889 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
1890 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
1891 return -1;
1892 }
1893
1894 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
1895 !pa_streq(o->alsa_name, "on") &&
1896 !pa_streq(o->alsa_name, "off")) {
1897 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
1898 return -1;
1899 }
1900
1901 if (!o->description)
1902 o->description = pa_xstrdup(lookup_description(o->name,
1903 well_known_descriptions,
1904 PA_ELEMENTSOF(well_known_descriptions)));
1905 if (!o->description)
1906 o->description = pa_xstrdup(o->name);
1907
1908 return 0;
1909 }
1910
1911 static int element_verify(pa_alsa_element *e) {
1912 pa_alsa_option *o;
1913
1914 pa_assert(e);
1915
1916 // pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
1917 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
1918 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
1919 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
1920 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
1921 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
1922 return -1;
1923 }
1924
1925 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
1926 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
1927 return -1;
1928 }
1929
1930 PA_LLIST_FOREACH(o, e->options)
1931 if (option_verify(o) < 0)
1932 return -1;
1933
1934 return 0;
1935 }
1936
1937 static int path_verify(pa_alsa_path *p) {
1938 static const struct description_map well_known_descriptions[] = {
1939 { "analog-input", N_("Analog Input") },
1940 { "analog-input-microphone", N_("Analog Microphone") },
1941 { "analog-input-microphone-front", N_("Front Microphone") },
1942 { "analog-input-microphone-rear", N_("Rear Microphone") },
1943 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
1944 { "analog-input-microphone-internal", N_("Internal Microphone") },
1945 { "analog-input-linein", N_("Analog Line-In") },
1946 { "analog-input-radio", N_("Analog Radio") },
1947 { "analog-input-video", N_("Analog Video") },
1948 { "analog-output", N_("Analog Output") },
1949 { "analog-output-headphones", N_("Analog Headphones") },
1950 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1951 { "analog-output-mono", N_("Analog Mono Output") },
1952 { "analog-output-speaker", N_("Analog Speakers") },
1953 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
1954 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
1955 };
1956
1957 pa_alsa_element *e;
1958
1959 pa_assert(p);
1960
1961 PA_LLIST_FOREACH(e, p->elements)
1962 if (element_verify(e) < 0)
1963 return -1;
1964
1965 if (!p->description)
1966 p->description = pa_xstrdup(lookup_description(p->name,
1967 well_known_descriptions,
1968 PA_ELEMENTSOF(well_known_descriptions)));
1969
1970 if (!p->description)
1971 p->description = pa_xstrdup(p->name);
1972
1973 return 0;
1974 }
1975
1976 pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) {
1977 pa_alsa_path *p;
1978 char *fn;
1979 int r;
1980 const char *n;
1981
1982 pa_config_item items[] = {
1983 /* [General] */
1984 { "priority", pa_config_parse_unsigned, NULL, "General" },
1985 { "description", pa_config_parse_string, NULL, "General" },
1986 { "name", pa_config_parse_string, NULL, "General" },
1987
1988 /* [Option ...] */
1989 { "priority", option_parse_priority, NULL, NULL },
1990 { "name", option_parse_name, NULL, NULL },
1991
1992 /* [Element ...] */
1993 { "switch", element_parse_switch, NULL, NULL },
1994 { "volume", element_parse_volume, NULL, NULL },
1995 { "enumeration", element_parse_enumeration, NULL, NULL },
1996 { "override-map.1", element_parse_override_map, NULL, NULL },
1997 { "override-map.2", element_parse_override_map, NULL, NULL },
1998 /* ... later on we might add override-map.3 and so on here ... */
1999 { "required", element_parse_required, NULL, NULL },
2000 { "required-any", element_parse_required, NULL, NULL },
2001 { "required-absent", element_parse_required, NULL, NULL },
2002 { "direction", element_parse_direction, NULL, NULL },
2003 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2004 { NULL, NULL, NULL, NULL }
2005 };
2006
2007 pa_assert(fname);
2008
2009 p = pa_xnew0(pa_alsa_path, 1);
2010 n = pa_path_get_filename(fname);
2011 p->name = pa_xstrndup(n, strcspn(n, "."));
2012 p->direction = direction;
2013
2014 items[0].data = &p->priority;
2015 items[1].data = &p->description;
2016 items[2].data = &p->name;
2017
2018 fn = pa_maybe_prefix_path(fname,
2019 #if defined(__linux__) && !defined(__OPTIMIZE__)
2020 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
2021 #endif
2022 PA_ALSA_PATHS_DIR);
2023
2024 r = pa_config_parse(fn, NULL, items, p);
2025 pa_xfree(fn);
2026
2027 if (r < 0)
2028 goto fail;
2029
2030 if (path_verify(p) < 0)
2031 goto fail;
2032
2033 return p;
2034
2035 fail:
2036 pa_alsa_path_free(p);
2037 return NULL;
2038 }
2039
2040 pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t direction) {
2041 pa_alsa_path *p;
2042 pa_alsa_element *e;
2043
2044 pa_assert(element);
2045
2046 p = pa_xnew0(pa_alsa_path, 1);
2047 p->name = pa_xstrdup(element);
2048 p->direction = direction;
2049
2050 e = pa_xnew0(pa_alsa_element, 1);
2051 e->path = p;
2052 e->alsa_name = pa_xstrdup(element);
2053 e->direction = direction;
2054
2055 e->switch_use = PA_ALSA_SWITCH_MUTE;
2056 e->volume_use = PA_ALSA_VOLUME_MERGE;
2057
2058 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2059 p->last_element = e;
2060 return p;
2061 }
2062
2063 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2064 pa_alsa_option *o, *n;
2065
2066 pa_assert(e);
2067
2068 for (o = e->options; o; o = n) {
2069 n = o->next;
2070
2071 if (o->alsa_idx < 0) {
2072 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2073 option_free(o);
2074 }
2075 }
2076
2077 return
2078 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2079 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2080 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2081 }
2082
2083 static void path_drop_unsupported(pa_alsa_path *p) {
2084 pa_alsa_element *e, *n;
2085
2086 pa_assert(p);
2087
2088 for (e = p->elements; e; e = n) {
2089 n = e->next;
2090
2091 if (!element_drop_unsupported(e)) {
2092 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2093 element_free(e);
2094 }
2095 }
2096 }
2097
2098 static void path_make_options_unique(pa_alsa_path *p) {
2099 pa_alsa_element *e;
2100 pa_alsa_option *o, *u;
2101
2102 PA_LLIST_FOREACH(e, p->elements) {
2103 PA_LLIST_FOREACH(o, e->options) {
2104 unsigned i;
2105 char *m;
2106
2107 for (u = o->next; u; u = u->next)
2108 if (pa_streq(u->name, o->name))
2109 break;
2110
2111 if (!u)
2112 continue;
2113
2114 m = pa_xstrdup(o->name);
2115
2116 /* OK, this name is not unique, hence let's rename */
2117 for (i = 1, u = o; u; u = u->next) {
2118 char *nn, *nd;
2119
2120 if (!pa_streq(u->name, m))
2121 continue;
2122
2123 nn = pa_sprintf_malloc("%s-%u", m, i);
2124 pa_xfree(u->name);
2125 u->name = nn;
2126
2127 nd = pa_sprintf_malloc("%s %u", u->description, i);
2128 pa_xfree(u->description);
2129 u->description = nd;
2130
2131 i++;
2132 }
2133
2134 pa_xfree(m);
2135 }
2136 }
2137 }
2138
2139 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2140 pa_alsa_option *o;
2141
2142 for (; e; e = e->next)
2143 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2144 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2145 break;
2146
2147 if (!e)
2148 return FALSE;
2149
2150 for (o = e->options; o; o = o->next) {
2151 pa_alsa_setting *s;
2152
2153 if (template) {
2154 s = pa_xnewdup(pa_alsa_setting, template, 1);
2155 s->options = pa_idxset_copy(template->options);
2156 s->name = pa_sprintf_malloc(_("%s+%s"), template->name, o->name);
2157 s->description =
2158 (template->description[0] && o->description[0])
2159 ? pa_sprintf_malloc(_("%s / %s"), template->description, o->description)
2160 : (template->description[0]
2161 ? pa_xstrdup(template->description)
2162 : pa_xstrdup(o->description));
2163
2164 s->priority = PA_MAX(template->priority, o->priority);
2165 } else {
2166 s = pa_xnew0(pa_alsa_setting, 1);
2167 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2168 s->name = pa_xstrdup(o->name);
2169 s->description = pa_xstrdup(o->description);
2170 s->priority = o->priority;
2171 }
2172
2173 pa_idxset_put(s->options, o, NULL);
2174
2175 if (element_create_settings(e->next, s))
2176 /* This is not a leaf, so let's get rid of it */
2177 setting_free(s);
2178 else {
2179 /* This is a leaf, so let's add it */
2180 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2181
2182 e->path->last_setting = s;
2183 }
2184 }
2185
2186 return TRUE;
2187 }
2188
2189 static void path_create_settings(pa_alsa_path *p) {
2190 pa_assert(p);
2191
2192 element_create_settings(p->elements, NULL);
2193 }
2194
2195 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
2196 pa_alsa_element *e;
2197 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2198 pa_channel_position_t t;
2199
2200 pa_assert(p);
2201 pa_assert(m);
2202
2203 if (p->probed)
2204 return 0;
2205
2206 pa_zero(min_dB);
2207 pa_zero(max_dB);
2208
2209 pa_log_debug("Probing path '%s'", p->name);
2210
2211 PA_LLIST_FOREACH(e, p->elements) {
2212 if (element_probe(e, m) < 0) {
2213 p->supported = FALSE;
2214 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2215 return -1;
2216 }
2217
2218 if (ignore_dB)
2219 e->has_dB = FALSE;
2220
2221 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2222
2223 if (!p->has_volume) {
2224 p->min_volume = e->min_volume;
2225 p->max_volume = e->max_volume;
2226 }
2227
2228 if (e->has_dB) {
2229 if (!p->has_volume) {
2230 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2231 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2232 min_dB[t] = e->min_dB;
2233 max_dB[t] = e->max_dB;
2234 }
2235
2236 p->has_dB = TRUE;
2237 } else {
2238
2239 if (p->has_dB) {
2240 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2241 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2242 min_dB[t] += e->min_dB;
2243 max_dB[t] += e->max_dB;
2244 }
2245 } else {
2246 /* Hmm, there's another element before us
2247 * which cannot do dB volumes, so we we need
2248 * to 'neutralize' this slider */
2249 e->volume_use = PA_ALSA_VOLUME_ZERO;
2250 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2251 }
2252 }
2253 } else if (p->has_volume)
2254 /* We can't use this volume, so let's ignore it */
2255 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2256
2257 p->has_volume = TRUE;
2258 }
2259
2260 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2261 p->has_mute = TRUE;
2262 }
2263
2264 if (p->has_req_any && !p->req_any_present) {
2265 p->supported = FALSE;
2266 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2267 return -1;
2268 }
2269
2270 path_drop_unsupported(p);
2271 path_make_options_unique(p);
2272 path_create_settings(p);
2273
2274 p->supported = TRUE;
2275 p->probed = TRUE;
2276
2277 p->min_dB = INFINITY;
2278 p->max_dB = -INFINITY;
2279
2280 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2281 if (p->min_dB > min_dB[t])
2282 p->min_dB = min_dB[t];
2283
2284 if (p->max_dB < max_dB[t])
2285 p->max_dB = max_dB[t];
2286 }
2287
2288 return 0;
2289 }
2290
2291 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2292 pa_assert(s);
2293
2294 pa_log_debug("Setting %s (%s) priority=%u",
2295 s->name,
2296 pa_strnull(s->description),
2297 s->priority);
2298 }
2299
2300 void pa_alsa_option_dump(pa_alsa_option *o) {
2301 pa_assert(o);
2302
2303 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2304 o->alsa_name,
2305 pa_strnull(o->name),
2306 pa_strnull(o->description),
2307 o->alsa_idx,
2308 o->priority);
2309 }
2310
2311 void pa_alsa_element_dump(pa_alsa_element *e) {
2312 pa_alsa_option *o;
2313 pa_assert(e);
2314
2315 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2316 e->alsa_name,
2317 e->direction,
2318 e->switch_use,
2319 e->volume_use,
2320 e->enumeration_use,
2321 e->required,
2322 e->required_any,
2323 e->required_absent,
2324 (long long unsigned) e->merged_mask,
2325 e->n_channels,
2326 pa_yes_no(e->override_map));
2327
2328 PA_LLIST_FOREACH(o, e->options)
2329 pa_alsa_option_dump(o);
2330 }
2331
2332 void pa_alsa_path_dump(pa_alsa_path *p) {
2333 pa_alsa_element *e;
2334 pa_alsa_setting *s;
2335 pa_assert(p);
2336
2337 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2338 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2339 p->name,
2340 pa_strnull(p->description),
2341 p->direction,
2342 p->priority,
2343 pa_yes_no(p->probed),
2344 pa_yes_no(p->supported),
2345 pa_yes_no(p->has_mute),
2346 pa_yes_no(p->has_volume),
2347 pa_yes_no(p->has_dB),
2348 p->min_volume, p->max_volume,
2349 p->min_dB, p->max_dB);
2350
2351 PA_LLIST_FOREACH(e, p->elements)
2352 pa_alsa_element_dump(e);
2353
2354 PA_LLIST_FOREACH(s, p->settings)
2355 pa_alsa_setting_dump(s);
2356 }
2357
2358 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2359 snd_mixer_selem_id_t *sid;
2360 snd_mixer_elem_t *me;
2361
2362 pa_assert(e);
2363 pa_assert(m);
2364 pa_assert(cb);
2365
2366 SELEM_INIT(sid, e->alsa_name);
2367 if (!(me = snd_mixer_find_selem(m, sid))) {
2368 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2369 return;
2370 }
2371
2372 snd_mixer_elem_set_callback(me, cb);
2373 snd_mixer_elem_set_callback_private(me, userdata);
2374 }
2375
2376 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2377 pa_alsa_element *e;
2378
2379 pa_assert(p);
2380 pa_assert(m);
2381 pa_assert(cb);
2382
2383 PA_LLIST_FOREACH(e, p->elements)
2384 element_set_callback(e, m, cb, userdata);
2385 }
2386
2387 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2388 pa_alsa_path *p;
2389
2390 pa_assert(ps);
2391 pa_assert(m);
2392 pa_assert(cb);
2393
2394 PA_LLIST_FOREACH(p, ps->paths)
2395 pa_alsa_path_set_callback(p, m, cb, userdata);
2396 }
2397
2398 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) {
2399 pa_alsa_path_set *ps;
2400 char **pn = NULL, **en = NULL, **ie;
2401
2402 pa_assert(m);
2403 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2404
2405 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2406 return NULL;
2407
2408 ps = pa_xnew0(pa_alsa_path_set, 1);
2409 ps->direction = direction;
2410
2411 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2412 pn = m->output_path_names;
2413 else if (direction == PA_ALSA_DIRECTION_INPUT)
2414 pn = m->input_path_names;
2415
2416 if (pn) {
2417 char **in;
2418
2419 for (in = pn; *in; in++) {
2420 pa_alsa_path *p;
2421 pa_bool_t duplicate = FALSE;
2422 char **kn, *fn;
2423
2424 for (kn = pn; kn != in; kn++)
2425 if (pa_streq(*kn, *in)) {
2426 duplicate = TRUE;
2427 break;
2428 }
2429
2430 if (duplicate)
2431 continue;
2432
2433 fn = pa_sprintf_malloc("%s.conf", *in);
2434
2435 if ((p = pa_alsa_path_new(fn, direction))) {
2436 p->path_set = ps;
2437 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2438 ps->last_path = p;
2439 }
2440
2441 pa_xfree(fn);
2442 }
2443
2444 return ps;
2445 }
2446
2447 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2448 en = m->output_element;
2449 else if (direction == PA_ALSA_DIRECTION_INPUT)
2450 en = m->input_element;
2451
2452 if (!en) {
2453 pa_alsa_path_set_free(ps);
2454 return NULL;
2455 }
2456
2457 for (ie = en; *ie; ie++) {
2458 char **je;
2459 pa_alsa_path *p;
2460
2461 p = pa_alsa_path_synthesize(*ie, direction);
2462 p->path_set = ps;
2463
2464 /* Mark all other passed elements for require-absent */
2465 for (je = en; *je; je++) {
2466 pa_alsa_element *e;
2467
2468 if (je == ie)
2469 continue;
2470
2471 e = pa_xnew0(pa_alsa_element, 1);
2472 e->path = p;
2473 e->alsa_name = pa_xstrdup(*je);
2474 e->direction = direction;
2475 e->required_absent = PA_ALSA_REQUIRED_ANY;
2476
2477 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2478 p->last_element = e;
2479 }
2480
2481 PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
2482 ps->last_path = p;
2483 }
2484
2485 return ps;
2486 }
2487
2488 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2489 pa_alsa_path *p;
2490 pa_assert(ps);
2491
2492 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2493 (void*) ps,
2494 ps->direction,
2495 pa_yes_no(ps->probed));
2496
2497 PA_LLIST_FOREACH(p, ps->paths)
2498 pa_alsa_path_dump(p);
2499 }
2500
2501 static void path_set_unify(pa_alsa_path_set *ps) {
2502 pa_alsa_path *p;
2503 pa_bool_t has_dB = TRUE, has_volume = TRUE, has_mute = TRUE;
2504 pa_assert(ps);
2505
2506 /* We have issues dealing with paths that vary too wildly. That
2507 * means for now we have to have all paths support volume/mute/dB
2508 * or none. */
2509
2510 PA_LLIST_FOREACH(p, ps->paths) {
2511 pa_assert(p->probed);
2512
2513 if (!p->has_volume)
2514 has_volume = FALSE;
2515 else if (!p->has_dB)
2516 has_dB = FALSE;
2517
2518 if (!p->has_mute)
2519 has_mute = FALSE;
2520 }
2521
2522 if (!has_volume || !has_dB || !has_mute) {
2523
2524 if (!has_volume)
2525 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2526 else if (!has_dB)
2527 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2528
2529 if (!has_mute)
2530 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2531
2532 PA_LLIST_FOREACH(p, ps->paths) {
2533 if (!has_volume)
2534 p->has_volume = FALSE;
2535 else if (!has_dB)
2536 p->has_dB = FALSE;
2537
2538 if (!has_mute)
2539 p->has_mute = FALSE;
2540 }
2541 }
2542 }
2543
2544 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
2545 pa_alsa_path *p, *q;
2546
2547 PA_LLIST_FOREACH(p, ps->paths) {
2548 unsigned i;
2549 char *m;
2550
2551 for (q = p->next; q; q = q->next)
2552 if (pa_streq(q->name, p->name))
2553 break;
2554
2555 if (!q)
2556 continue;
2557
2558 m = pa_xstrdup(p->name);
2559
2560 /* OK, this name is not unique, hence let's rename */
2561 for (i = 1, q = p; q; q = q->next) {
2562 char *nn, *nd;
2563
2564 if (!pa_streq(q->name, m))
2565 continue;
2566
2567 nn = pa_sprintf_malloc("%s-%u", m, i);
2568 pa_xfree(q->name);
2569 q->name = nn;
2570
2571 nd = pa_sprintf_malloc("%s %u", q->description, i);
2572 pa_xfree(q->description);
2573 q->description = nd;
2574
2575 i++;
2576 }
2577
2578 pa_xfree(m);
2579 }
2580 }
2581
2582 void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
2583 pa_alsa_path *p, *n;
2584
2585 pa_assert(ps);
2586
2587 if (ps->probed)
2588 return;
2589
2590 for (p = ps->paths; p; p = n) {
2591 n = p->next;
2592
2593 if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
2594 PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
2595 pa_alsa_path_free(p);
2596 }
2597 }
2598
2599 path_set_unify(ps);
2600 path_set_make_paths_unique(ps);
2601 ps->probed = TRUE;
2602 }
2603
2604 static void mapping_free(pa_alsa_mapping *m) {
2605 pa_assert(m);
2606
2607 pa_xfree(m->name);
2608 pa_xfree(m->description);
2609
2610 pa_xstrfreev(m->device_strings);
2611 pa_xstrfreev(m->input_path_names);
2612 pa_xstrfreev(m->output_path_names);
2613 pa_xstrfreev(m->input_element);
2614 pa_xstrfreev(m->output_element);
2615
2616 pa_assert(!m->input_pcm);
2617 pa_assert(!m->output_pcm);
2618
2619 pa_xfree(m);
2620 }
2621
2622 static void profile_free(pa_alsa_profile *p) {
2623 pa_assert(p);
2624
2625 pa_xfree(p->name);
2626 pa_xfree(p->description);
2627
2628 pa_xstrfreev(p->input_mapping_names);
2629 pa_xstrfreev(p->output_mapping_names);
2630
2631 if (p->input_mappings)
2632 pa_idxset_free(p->input_mappings, NULL, NULL);
2633
2634 if (p->output_mappings)
2635 pa_idxset_free(p->output_mappings, NULL, NULL);
2636
2637 pa_xfree(p);
2638 }
2639
2640 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
2641 pa_assert(ps);
2642
2643 if (ps->profiles) {
2644 pa_alsa_profile *p;
2645
2646 while ((p = pa_hashmap_steal_first(ps->profiles)))
2647 profile_free(p);
2648
2649 pa_hashmap_free(ps->profiles, NULL, NULL);
2650 }
2651
2652 if (ps->mappings) {
2653 pa_alsa_mapping *m;
2654
2655 while ((m = pa_hashmap_steal_first(ps->mappings)))
2656 mapping_free(m);
2657
2658 pa_hashmap_free(ps->mappings, NULL, NULL);
2659 }
2660
2661 pa_xfree(ps);
2662 }
2663
2664 static pa_alsa_mapping *mapping_get(pa_alsa_profile_set *ps, const char *name) {
2665 pa_alsa_mapping *m;
2666
2667 if (!pa_startswith(name, "Mapping "))
2668 return NULL;
2669
2670 name += 8;
2671
2672 if ((m = pa_hashmap_get(ps->mappings, name)))
2673 return m;
2674
2675 m = pa_xnew0(pa_alsa_mapping, 1);
2676 m->profile_set = ps;
2677 m->name = pa_xstrdup(name);
2678 pa_channel_map_init(&m->channel_map);
2679
2680 pa_hashmap_put(ps->mappings, m->name, m);
2681
2682 return m;
2683 }
2684
2685 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
2686 pa_alsa_profile *p;
2687
2688 if (!pa_startswith(name, "Profile "))
2689 return NULL;
2690
2691 name += 8;
2692
2693 if ((p = pa_hashmap_get(ps->profiles, name)))
2694 return p;
2695
2696 p = pa_xnew0(pa_alsa_profile, 1);
2697 p->profile_set = ps;
2698 p->name = pa_xstrdup(name);
2699
2700 pa_hashmap_put(ps->profiles, p->name, p);
2701
2702 return p;
2703 }
2704
2705 static int mapping_parse_device_strings(
2706 const char *filename,
2707 unsigned line,
2708 const char *section,
2709 const char *lvalue,
2710 const char *rvalue,
2711 void *data,
2712 void *userdata) {
2713
2714 pa_alsa_profile_set *ps = userdata;
2715 pa_alsa_mapping *m;
2716
2717 pa_assert(ps);
2718
2719 if (!(m = mapping_get(ps, section))) {
2720 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2721 return -1;
2722 }
2723
2724 pa_xstrfreev(m->device_strings);
2725 if (!(m->device_strings = pa_split_spaces_strv(rvalue))) {
2726 pa_log("[%s:%u] Device string list empty of '%s'", filename, line, section);
2727 return -1;
2728 }
2729
2730 return 0;
2731 }
2732
2733 static int mapping_parse_channel_map(
2734 const char *filename,
2735 unsigned line,
2736 const char *section,
2737 const char *lvalue,
2738 const char *rvalue,
2739 void *data,
2740 void *userdata) {
2741
2742 pa_alsa_profile_set *ps = userdata;
2743 pa_alsa_mapping *m;
2744
2745 pa_assert(ps);
2746
2747 if (!(m = mapping_get(ps, section))) {
2748 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2749 return -1;
2750 }
2751
2752 if (!(pa_channel_map_parse(&m->channel_map, rvalue))) {
2753 pa_log("[%s:%u] Channel map invalid of '%s'", filename, line, section);
2754 return -1;
2755 }
2756
2757 return 0;
2758 }
2759
2760 static int mapping_parse_paths(
2761 const char *filename,
2762 unsigned line,
2763 const char *section,
2764 const char *lvalue,
2765 const char *rvalue,
2766 void *data,
2767 void *userdata) {
2768
2769 pa_alsa_profile_set *ps = userdata;
2770 pa_alsa_mapping *m;
2771
2772 pa_assert(ps);
2773
2774 if (!(m = mapping_get(ps, section))) {
2775 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2776 return -1;
2777 }
2778
2779 if (pa_streq(lvalue, "paths-input")) {
2780 pa_xstrfreev(m->input_path_names);
2781 m->input_path_names = pa_split_spaces_strv(rvalue);
2782 } else {
2783 pa_xstrfreev(m->output_path_names);
2784 m->output_path_names = pa_split_spaces_strv(rvalue);
2785 }
2786
2787 return 0;
2788 }
2789
2790 static int mapping_parse_element(
2791 const char *filename,
2792 unsigned line,
2793 const char *section,
2794 const char *lvalue,
2795 const char *rvalue,
2796 void *data,
2797 void *userdata) {
2798
2799 pa_alsa_profile_set *ps = userdata;
2800 pa_alsa_mapping *m;
2801
2802 pa_assert(ps);
2803
2804 if (!(m = mapping_get(ps, section))) {
2805 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2806 return -1;
2807 }
2808
2809 if (pa_streq(lvalue, "element-input")) {
2810 pa_xstrfreev(m->input_element);
2811 m->input_element = pa_split_spaces_strv(rvalue);
2812 } else {
2813 pa_xstrfreev(m->output_element);
2814 m->output_element = pa_split_spaces_strv(rvalue);
2815 }
2816
2817 return 0;
2818 }
2819
2820 static int mapping_parse_direction(
2821 const char *filename,
2822 unsigned line,
2823 const char *section,
2824 const char *lvalue,
2825 const char *rvalue,
2826 void *data,
2827 void *userdata) {
2828
2829 pa_alsa_profile_set *ps = userdata;
2830 pa_alsa_mapping *m;
2831
2832 pa_assert(ps);
2833
2834 if (!(m = mapping_get(ps, section))) {
2835 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2836 return -1;
2837 }
2838
2839 if (pa_streq(rvalue, "input"))
2840 m->direction = PA_ALSA_DIRECTION_INPUT;
2841 else if (pa_streq(rvalue, "output"))
2842 m->direction = PA_ALSA_DIRECTION_OUTPUT;
2843 else if (pa_streq(rvalue, "any"))
2844 m->direction = PA_ALSA_DIRECTION_ANY;
2845 else {
2846 pa_log("[%s:%u] Direction %s invalid.", filename, line, rvalue);
2847 return -1;
2848 }
2849
2850 return 0;
2851 }
2852
2853 static int mapping_parse_description(
2854 const char *filename,
2855 unsigned line,
2856 const char *section,
2857 const char *lvalue,
2858 const char *rvalue,
2859 void *data,
2860 void *userdata) {
2861
2862 pa_alsa_profile_set *ps = userdata;
2863 pa_alsa_profile *p;
2864 pa_alsa_mapping *m;
2865
2866 pa_assert(ps);
2867
2868 if ((m = mapping_get(ps, section))) {
2869 pa_xfree(m->description);
2870 m->description = pa_xstrdup(rvalue);
2871 } else if ((p = profile_get(ps, section))) {
2872 pa_xfree(p->description);
2873 p->description = pa_xstrdup(rvalue);
2874 } else {
2875 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2876 return -1;
2877 }
2878
2879 return 0;
2880 }
2881
2882 static int mapping_parse_priority(
2883 const char *filename,
2884 unsigned line,
2885 const char *section,
2886 const char *lvalue,
2887 const char *rvalue,
2888 void *data,
2889 void *userdata) {
2890
2891 pa_alsa_profile_set *ps = userdata;
2892 pa_alsa_profile *p;
2893 pa_alsa_mapping *m;
2894 uint32_t prio;
2895
2896 pa_assert(ps);
2897
2898 if (pa_atou(rvalue, &prio) < 0) {
2899 pa_log("[%s:%u] Priority invalid of '%s'", filename, line, section);
2900 return -1;
2901 }
2902
2903 if ((m = mapping_get(ps, section)))
2904 m->priority = prio;
2905 else if ((p = profile_get(ps, section)))
2906 p->priority = prio;
2907 else {
2908 pa_log("[%s:%u] Section name %s invalid.", filename, line, section);
2909 return -1;
2910 }
2911
2912 return 0;
2913 }
2914
2915 static int profile_parse_mappings(
2916 const char *filename,
2917 unsigned line,
2918 const char *section,
2919 const char *lvalue,
2920 const char *rvalue,
2921 void *data,
2922 void *userdata) {
2923
2924 pa_alsa_profile_set *ps = userdata;
2925 pa_alsa_profile *p;
2926
2927 pa_assert(ps);
2928
2929 if (!(p = profile_get(ps, section))) {
2930 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2931 return -1;
2932 }
2933
2934 if (pa_streq(lvalue, "input-mappings")) {
2935 pa_xstrfreev(p->input_mapping_names);
2936 p->input_mapping_names = pa_split_spaces_strv(rvalue);
2937 } else {
2938 pa_xstrfreev(p->output_mapping_names);
2939 p->output_mapping_names = pa_split_spaces_strv(rvalue);
2940 }
2941
2942 return 0;
2943 }
2944
2945 static int profile_parse_skip_probe(
2946 const char *filename,
2947 unsigned line,
2948 const char *section,
2949 const char *lvalue,
2950 const char *rvalue,
2951 void *data,
2952 void *userdata) {
2953
2954 pa_alsa_profile_set *ps = userdata;
2955 pa_alsa_profile *p;
2956 int b;
2957
2958 pa_assert(ps);
2959
2960 if (!(p = profile_get(ps, section))) {
2961 pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section);
2962 return -1;
2963 }
2964
2965 if ((b = pa_parse_boolean(rvalue)) < 0) {
2966 pa_log("[%s:%u] Skip probe invalid of '%s'", filename, line, section);
2967 return -1;
2968 }
2969
2970 p->supported = b;
2971
2972 return 0;
2973 }
2974
2975 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
2976
2977 static const struct description_map well_known_descriptions[] = {
2978 { "analog-mono", N_("Analog Mono") },
2979 { "analog-stereo", N_("Analog Stereo") },
2980 { "analog-surround-21", N_("Analog Surround 2.1") },
2981 { "analog-surround-30", N_("Analog Surround 3.0") },
2982 { "analog-surround-31", N_("Analog Surround 3.1") },
2983 { "analog-surround-40", N_("Analog Surround 4.0") },
2984 { "analog-surround-41", N_("Analog Surround 4.1") },
2985 { "analog-surround-50", N_("Analog Surround 5.0") },
2986 { "analog-surround-51", N_("Analog Surround 5.1") },
2987 { "analog-surround-61", N_("Analog Surround 6.0") },
2988 { "analog-surround-61", N_("Analog Surround 6.1") },
2989 { "analog-surround-70", N_("Analog Surround 7.0") },
2990 { "analog-surround-71", N_("Analog Surround 7.1") },
2991 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2992 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
2993 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2994 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2995 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2996 };
2997
2998 pa_assert(m);
2999
3000 if (!pa_channel_map_valid(&m->channel_map)) {
3001 pa_log("Mapping %s is missing channel map.", m->name);
3002 return -1;
3003 }
3004
3005 if (!m->device_strings) {
3006 pa_log("Mapping %s is missing device strings.", m->name);
3007 return -1;
3008 }
3009
3010 if ((m->input_path_names && m->input_element) ||
3011 (m->output_path_names && m->output_element)) {
3012 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name);
3013 return -1;
3014 }
3015
3016 if (!m->description)
3017 m->description = pa_xstrdup(lookup_description(m->name,
3018 well_known_descriptions,
3019 PA_ELEMENTSOF(well_known_descriptions)));
3020
3021 if (!m->description)
3022 m->description = pa_xstrdup(m->name);
3023
3024 if (bonus) {
3025 if (pa_channel_map_equal(&m->channel_map, bonus))
3026 m->priority += 50;
3027 else if (m->channel_map.channels == bonus->channels)
3028 m->priority += 30;
3029 }
3030
3031 return 0;
3032 }
3033
3034 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3035 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3036
3037 pa_assert(m);
3038
3039 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3040 m->name,
3041 pa_strnull(m->description),
3042 m->priority,
3043 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3044 pa_yes_no(m->supported),
3045 m->direction);
3046 }
3047
3048 static void profile_set_add_auto_pair(
3049 pa_alsa_profile_set *ps,
3050 pa_alsa_mapping *m, /* output */
3051 pa_alsa_mapping *n /* input */) {
3052
3053 char *name;
3054 pa_alsa_profile *p;
3055
3056 pa_assert(ps);
3057 pa_assert(m || n);
3058
3059 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3060 return;
3061
3062 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3063 return;
3064
3065 if (m && n)
3066 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3067 else if (m)
3068 name = pa_sprintf_malloc("output:%s", m->name);
3069 else
3070 name = pa_sprintf_malloc("input:%s", n->name);
3071
3072 if (pa_hashmap_get(ps->profiles, name)) {
3073 pa_xfree(name);
3074 return;
3075 }
3076
3077 p = pa_xnew0(pa_alsa_profile, 1);
3078 p->profile_set = ps;
3079 p->name = name;
3080
3081 if (m) {
3082 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3083 pa_idxset_put(p->output_mappings, m, NULL);
3084 p->priority += m->priority * 100;
3085 }
3086
3087 if (n) {
3088 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3089 pa_idxset_put(p->input_mappings, n, NULL);
3090 p->priority += n->priority;
3091 }
3092
3093 pa_hashmap_put(ps->profiles, p->name, p);
3094 }
3095
3096 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3097 pa_alsa_mapping *m, *n;
3098 void *m_state, *n_state;
3099
3100 pa_assert(ps);
3101
3102 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3103 profile_set_add_auto_pair(ps, m, NULL);
3104
3105 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3106 profile_set_add_auto_pair(ps, m, n);
3107 }
3108
3109 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3110 profile_set_add_auto_pair(ps, NULL, n);
3111 }
3112
3113 static int profile_verify(pa_alsa_profile *p) {
3114
3115 static const struct description_map well_known_descriptions[] = {
3116 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3117 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3118 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3119 { "off", N_("Off") }
3120 };
3121
3122 pa_assert(p);
3123
3124 /* Replace the output mapping names by the actual mappings */
3125 if (p->output_mapping_names) {
3126 char **name;
3127
3128 pa_assert(!p->output_mappings);
3129 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3130
3131 for (name = p->output_mapping_names; *name; name++) {
3132 pa_alsa_mapping *m;
3133 char **in;
3134 pa_bool_t duplicate = FALSE;
3135
3136 for (in = name + 1; *in; in++)
3137 if (pa_streq(*name, *in)) {
3138 duplicate = TRUE;
3139 break;
3140 }
3141
3142 if (duplicate)
3143 continue;
3144
3145 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3146 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3147 return -1;
3148 }
3149
3150 pa_idxset_put(p->output_mappings, m, NULL);
3151
3152 if (p->supported)
3153 m->supported++;
3154 }
3155
3156 pa_xstrfreev(p->output_mapping_names);
3157 p->output_mapping_names = NULL;
3158 }
3159
3160 /* Replace the input mapping names by the actual mappings */
3161 if (p->input_mapping_names) {
3162 char **name;
3163
3164 pa_assert(!p->input_mappings);
3165 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3166
3167 for (name = p->input_mapping_names; *name; name++) {
3168 pa_alsa_mapping *m;
3169 char **in;
3170 pa_bool_t duplicate = FALSE;
3171
3172 for (in = name + 1; *in; in++)
3173 if (pa_streq(*name, *in)) {
3174 duplicate = TRUE;
3175 break;
3176 }
3177
3178 if (duplicate)
3179 continue;
3180
3181 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3182 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p->name, *name);
3183 return -1;
3184 }
3185
3186 pa_idxset_put(p->input_mappings, m, NULL);
3187
3188 if (p->supported)
3189 m->supported++;
3190 }
3191
3192 pa_xstrfreev(p->input_mapping_names);
3193 p->input_mapping_names = NULL;
3194 }
3195
3196 if (!p->input_mappings && !p->output_mappings) {
3197 pa_log("Profile '%s' lacks mappings.", p->name);
3198 return -1;
3199 }
3200
3201 if (!p->description)
3202 p->description = pa_xstrdup(lookup_description(p->name,
3203 well_known_descriptions,
3204 PA_ELEMENTSOF(well_known_descriptions)));
3205
3206 if (!p->description) {
3207 pa_strbuf *sb;
3208 uint32_t idx;
3209 pa_alsa_mapping *m;
3210
3211 sb = pa_strbuf_new();
3212
3213 if (p->output_mappings)
3214 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3215 if (!pa_strbuf_isempty(sb))
3216 pa_strbuf_puts(sb, " + ");
3217
3218 pa_strbuf_printf(sb, _("%s Output"), m->description);
3219 }
3220
3221 if (p->input_mappings)
3222 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3223 if (!pa_strbuf_isempty(sb))
3224 pa_strbuf_puts(sb, " + ");
3225
3226 pa_strbuf_printf(sb, _("%s Input"), m->description);
3227 }
3228
3229 p->description = pa_strbuf_tostring_free(sb);
3230 }
3231
3232 return 0;
3233 }
3234
3235 void pa_alsa_profile_dump(pa_alsa_profile *p) {
3236 uint32_t idx;
3237 pa_alsa_mapping *m;
3238 pa_assert(p);
3239
3240 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3241 p->name,
3242 pa_strnull(p->description),
3243 p->priority,
3244 pa_yes_no(p->supported),
3245 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
3246 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
3247
3248 if (p->input_mappings)
3249 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
3250 pa_log_debug("Input %s", m->name);
3251
3252 if (p->output_mappings)
3253 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
3254 pa_log_debug("Output %s", m->name);
3255 }
3256
3257 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
3258 pa_alsa_profile_set *ps;
3259 pa_alsa_profile *p;
3260 pa_alsa_mapping *m;
3261 char *fn;
3262 int r;
3263 void *state;
3264
3265 static pa_config_item items[] = {
3266 /* [General] */
3267 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
3268
3269 /* [Mapping ...] */
3270 { "device-strings", mapping_parse_device_strings, NULL, NULL },
3271 { "channel-map", mapping_parse_channel_map, NULL, NULL },
3272 { "paths-input", mapping_parse_paths, NULL, NULL },
3273 { "paths-output", mapping_parse_paths, NULL, NULL },
3274 { "element-input", mapping_parse_element, NULL, NULL },
3275 { "element-output", mapping_parse_element, NULL, NULL },
3276 { "direction", mapping_parse_direction, NULL, NULL },
3277
3278 /* Shared by [Mapping ...] and [Profile ...] */
3279 { "description", mapping_parse_description, NULL, NULL },
3280 { "priority", mapping_parse_priority, NULL, NULL },
3281
3282 /* [Profile ...] */
3283 { "input-mappings", profile_parse_mappings, NULL, NULL },
3284 { "output-mappings", profile_parse_mappings, NULL, NULL },
3285 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
3286 { NULL, NULL, NULL, NULL }
3287 };
3288
3289 ps = pa_xnew0(pa_alsa_profile_set, 1);
3290 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3291 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3292
3293 items[0].data = &ps->auto_profiles;
3294
3295 if (!fname)
3296 fname = "default.conf";
3297
3298 fn = pa_maybe_prefix_path(fname,
3299 #if defined(__linux__) && !defined(__OPTIMIZE__)
3300 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
3301 #endif
3302 PA_ALSA_PROFILE_SETS_DIR);
3303
3304 r = pa_config_parse(fn, NULL, items, ps);
3305 pa_xfree(fn);
3306
3307 if (r < 0)
3308 goto fail;
3309
3310 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3311 if (mapping_verify(m, bonus) < 0)
3312 goto fail;
3313
3314 if (ps->auto_profiles)
3315 profile_set_add_auto(ps);
3316
3317 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3318 if (profile_verify(p) < 0)
3319 goto fail;
3320
3321 return ps;
3322
3323 fail:
3324 pa_alsa_profile_set_free(ps);
3325 return NULL;
3326 }
3327
3328 void pa_alsa_profile_set_probe(
3329 pa_alsa_profile_set *ps,
3330 const char *dev_id,
3331 const pa_sample_spec *ss,
3332 unsigned default_n_fragments,
3333 unsigned default_fragment_size_msec) {
3334
3335 void *state;
3336 pa_alsa_profile *p, *last = NULL;
3337 pa_alsa_mapping *m;
3338
3339 pa_assert(ps);
3340 pa_assert(dev_id);
3341 pa_assert(ss);
3342
3343 if (ps->probed)
3344 return;
3345
3346 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
3347 pa_sample_spec try_ss;
3348 pa_channel_map try_map;
3349 snd_pcm_uframes_t try_period_size, try_buffer_size;
3350 uint32_t idx;
3351
3352 /* Is this already marked that it is supported? (i.e. from the config file) */
3353 if (p->supported)
3354 continue;
3355
3356 pa_log_debug("Looking at profile %s", p->name);
3357
3358 /* Close PCMs from the last iteration we don't need anymore */
3359 if (last && last->output_mappings)
3360 PA_IDXSET_FOREACH(m, last->output_mappings, idx) {
3361
3362 if (!m->output_pcm)
3363 break;
3364
3365 if (last->supported)
3366 m->supported++;
3367
3368 if (!p->output_mappings || !pa_idxset_get_by_data(p->output_mappings, m, NULL)) {
3369 snd_pcm_close(m->output_pcm);
3370 m->output_pcm = NULL;
3371 }
3372 }
3373
3374 if (last && last->input_mappings)
3375 PA_IDXSET_FOREACH(m, last->input_mappings, idx) {
3376
3377 if (!m->input_pcm)
3378 break;
3379
3380 if (last->supported)
3381 m->supported++;
3382
3383 if (!p->input_mappings || !pa_idxset_get_by_data(p->input_mappings, m, NULL)) {
3384 snd_pcm_close(m->input_pcm);
3385 m->input_pcm = NULL;
3386 }
3387 }
3388
3389 p->supported = TRUE;
3390
3391 /* Check if we can open all new ones */
3392 if (p->output_mappings)
3393 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
3394
3395 if (m->output_pcm)
3396 continue;
3397
3398 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
3399 try_map = m->channel_map;
3400 try_ss = *ss;
3401 try_ss.channels = try_map.channels;
3402
3403 try_period_size =
3404 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
3405 pa_frame_size(&try_ss);
3406 try_buffer_size = default_n_fragments * try_period_size;
3407
3408 if (!(m ->output_pcm = pa_alsa_open_by_template(
3409 m->device_strings,
3410 dev_id,
3411 NULL,
3412 &try_ss, &try_map,
3413 SND_PCM_STREAM_PLAYBACK,
3414 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3415 TRUE))) {
3416 p->supported = FALSE;
3417 break;
3418 }
3419 }
3420
3421 if (p->input_mappings && p->supported)
3422 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
3423
3424 if (m->input_pcm)
3425 continue;
3426
3427 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
3428 try_map = m->channel_map;
3429 try_ss = *ss;
3430 try_ss.channels = try_map.channels;
3431
3432 try_period_size =
3433 pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
3434 pa_frame_size(&try_ss);
3435 try_buffer_size = default_n_fragments * try_period_size;
3436
3437 if (!(m ->input_pcm = pa_alsa_open_by_template(
3438 m->device_strings,
3439 dev_id,
3440 NULL,
3441 &try_ss, &try_map,
3442 SND_PCM_STREAM_CAPTURE,
3443 &try_period_size, &try_buffer_size, 0, NULL, NULL,
3444 TRUE))) {
3445 p->supported = FALSE;
3446 break;
3447 }
3448 }
3449
3450 last = p;
3451
3452 if (p->supported)
3453 pa_log_debug("Profile %s supported.", p->name);
3454 }
3455
3456 /* Clean up */
3457 if (last) {
3458 uint32_t idx;
3459
3460 if (last->output_mappings)
3461 PA_IDXSET_FOREACH(m, last->output_mappings, idx)
3462 if (m->output_pcm) {
3463
3464 if (last->supported)
3465 m->supported++;
3466
3467 snd_pcm_close(m->output_pcm);
3468 m->output_pcm = NULL;
3469 }
3470
3471 if (last->input_mappings)
3472 PA_IDXSET_FOREACH(m, last->input_mappings, idx)
3473 if (m->input_pcm) {
3474
3475 if (last->supported)
3476 m->supported++;
3477
3478 snd_pcm_close(m->input_pcm);
3479 m->input_pcm = NULL;
3480 }
3481 }
3482
3483 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3484 if (!p->supported) {
3485 pa_hashmap_remove(ps->profiles, p->name);
3486 profile_free(p);
3487 }
3488
3489 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3490 if (m->supported <= 0) {
3491 pa_hashmap_remove(ps->mappings, m->name);
3492 mapping_free(m);
3493 }
3494
3495 ps->probed = TRUE;
3496 }
3497
3498 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
3499 pa_alsa_profile *p;
3500 pa_alsa_mapping *m;
3501 void *state;
3502
3503 pa_assert(ps);
3504
3505 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3506 (void*)
3507 ps,
3508 pa_yes_no(ps->auto_profiles),
3509 pa_yes_no(ps->probed),
3510 pa_hashmap_size(ps->mappings),
3511 pa_hashmap_size(ps->profiles));
3512
3513 PA_HASHMAP_FOREACH(m, ps->mappings, state)
3514 pa_alsa_mapping_dump(m);
3515
3516 PA_HASHMAP_FOREACH(p, ps->profiles, state)
3517 pa_alsa_profile_dump(p);
3518 }
3519
3520 void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) {
3521 pa_alsa_path *path;
3522
3523 pa_assert(p);
3524 pa_assert(!*p);
3525 pa_assert(ps);
3526
3527 /* if there is no path, we don't want a port list */
3528 if (!ps->paths)
3529 return;
3530
3531 if (!ps->paths->next){
3532 pa_alsa_setting *s;
3533
3534 /* If there is only one path, but no or only one setting, then
3535 * we want a port list either */
3536 if (!ps->paths->settings || !ps->paths->settings->next)
3537 return;
3538
3539 /* Ok, there is only one path, however with multiple settings,
3540 * so let's create a port for each setting */
3541 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3542
3543 PA_LLIST_FOREACH(s, ps->paths->settings) {
3544 pa_device_port *port;
3545 pa_alsa_port_data *data;
3546
3547 port = pa_device_port_new(s->name, s->description, sizeof(pa_alsa_port_data));
3548 port->priority = s->priority;
3549
3550 data = PA_DEVICE_PORT_DATA(port);
3551 data->path = ps->paths;
3552 data->setting = s;
3553
3554 pa_hashmap_put(*p, port->name, port);
3555 }
3556
3557 } else {
3558
3559 /* We have multiple paths, so let's create a port for each
3560 * one, and each of each settings */
3561 *p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
3562
3563 PA_LLIST_FOREACH(path, ps->paths) {
3564
3565 if (!path->settings || !path->settings->next) {
3566 pa_device_port *port;
3567 pa_alsa_port_data *data;
3568
3569 /* If there is no or just one setting we only need a
3570 * single entry */
3571
3572 port = pa_device_port_new(path->name, path->description, sizeof(pa_alsa_port_data));
3573 port->priority = path->priority * 100;
3574
3575
3576 data = PA_DEVICE_PORT_DATA(port);
3577 data->path = path;
3578 data->setting = path->settings;
3579
3580 pa_hashmap_put(*p, port->name, port);
3581 } else {
3582 pa_alsa_setting *s;
3583
3584 PA_LLIST_FOREACH(s, path->settings) {
3585 pa_device_port *port;
3586 pa_alsa_port_data *data;
3587 char *n, *d;
3588
3589 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
3590
3591 if (s->description[0])
3592 d = pa_sprintf_malloc(_("%s / %s"), path->description, s->description);
3593 else
3594 d = pa_xstrdup(path->description);
3595
3596 port = pa_device_port_new(n, d, sizeof(pa_alsa_port_data));
3597 port->priority = path->priority * 100 + s->priority;
3598
3599 pa_xfree(n);
3600 pa_xfree(d);
3601
3602 data = PA_DEVICE_PORT_DATA(port);
3603 data->path = path;
3604 data->setting = s;
3605
3606 pa_hashmap_put(*p, port->name, port);
3607 }
3608 }
3609 }
3610 }
3611
3612 pa_log_debug("Added %u ports", pa_hashmap_size(*p));
3613 }