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