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