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