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