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