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