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