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