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