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