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