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