]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa-mixer: Add "iec958-stereo-input" to well known path names
[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 *name;
57 const char *description;
58 };
59
60 static const char *lookup_description(const char *name, const struct description_map dm[], unsigned n) {
61 unsigned i;
62
63 for (i = 0; i < n; i++)
64 if (pa_streq(dm[i].name, name))
65 return _(dm[i].description);
66
67 return NULL;
68 }
69
70 struct pa_alsa_fdlist {
71 unsigned num_fds;
72 struct pollfd *fds;
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd *work_fds;
75
76 snd_mixer_t *mixer;
77 snd_hctl_t *hctl;
78
79 pa_mainloop_api *m;
80 pa_defer_event *defer;
81 pa_io_event **ios;
82
83 pa_bool_t polled;
84
85 void (*cb)(void *userdata);
86 void *userdata;
87 };
88
89 static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
90
91 struct pa_alsa_fdlist *fdl = userdata;
92 int err;
93 unsigned i;
94 unsigned short revents;
95
96 pa_assert(a);
97 pa_assert(fdl);
98 pa_assert(fdl->mixer || fdl->hctl);
99 pa_assert(fdl->fds);
100 pa_assert(fdl->work_fds);
101
102 if (fdl->polled)
103 return;
104
105 fdl->polled = TRUE;
106
107 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
108
109 for (i = 0; i < fdl->num_fds; i++) {
110 if (e == fdl->ios[i]) {
111 if (events & PA_IO_EVENT_INPUT)
112 fdl->work_fds[i].revents |= POLLIN;
113 if (events & PA_IO_EVENT_OUTPUT)
114 fdl->work_fds[i].revents |= POLLOUT;
115 if (events & PA_IO_EVENT_ERROR)
116 fdl->work_fds[i].revents |= POLLERR;
117 if (events & PA_IO_EVENT_HANGUP)
118 fdl->work_fds[i].revents |= POLLHUP;
119 break;
120 }
121 }
122
123 pa_assert(i != fdl->num_fds);
124
125 if (fdl->hctl)
126 err = snd_hctl_poll_descriptors_revents(fdl->hctl, fdl->work_fds, fdl->num_fds, &revents);
127 else
128 err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
129
130 if (err < 0) {
131 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
132 return;
133 }
134
135 a->defer_enable(fdl->defer, 1);
136
137 if (revents) {
138 if (fdl->hctl)
139 snd_hctl_handle_events(fdl->hctl);
140 else
141 snd_mixer_handle_events(fdl->mixer);
142 }
143 }
144
145 static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
146 struct pa_alsa_fdlist *fdl = userdata;
147 unsigned num_fds, i;
148 int err, n;
149 struct pollfd *temp;
150
151 pa_assert(a);
152 pa_assert(fdl);
153 pa_assert(fdl->mixer || fdl->hctl);
154
155 a->defer_enable(fdl->defer, 0);
156
157 if (fdl->hctl)
158 n = snd_hctl_poll_descriptors_count(fdl->hctl);
159 else
160 n = snd_mixer_poll_descriptors_count(fdl->mixer);
161
162 if (n < 0) {
163 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
164 return;
165 }
166 num_fds = (unsigned) n;
167
168 if (num_fds != fdl->num_fds) {
169 if (fdl->fds)
170 pa_xfree(fdl->fds);
171 if (fdl->work_fds)
172 pa_xfree(fdl->work_fds);
173 fdl->fds = pa_xnew0(struct pollfd, num_fds);
174 fdl->work_fds = pa_xnew(struct pollfd, num_fds);
175 }
176
177 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
178
179 if (fdl->hctl)
180 err = snd_hctl_poll_descriptors(fdl->hctl, fdl->work_fds, num_fds);
181 else
182 err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
183
184 if (err < 0) {
185 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
186 return;
187 }
188
189 fdl->polled = FALSE;
190
191 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
192 return;
193
194 if (fdl->ios) {
195 for (i = 0; i < fdl->num_fds; i++)
196 a->io_free(fdl->ios[i]);
197
198 if (num_fds != fdl->num_fds) {
199 pa_xfree(fdl->ios);
200 fdl->ios = NULL;
201 }
202 }
203
204 if (!fdl->ios)
205 fdl->ios = pa_xnew(pa_io_event*, num_fds);
206
207 /* Swap pointers */
208 temp = fdl->work_fds;
209 fdl->work_fds = fdl->fds;
210 fdl->fds = temp;
211
212 fdl->num_fds = num_fds;
213
214 for (i = 0;i < num_fds;i++)
215 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
216 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
217 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
218 io_cb, fdl);
219 }
220
221 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
222 struct pa_alsa_fdlist *fdl;
223
224 fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
225
226 return fdl;
227 }
228
229 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
230 pa_assert(fdl);
231
232 if (fdl->defer) {
233 pa_assert(fdl->m);
234 fdl->m->defer_free(fdl->defer);
235 }
236
237 if (fdl->ios) {
238 unsigned i;
239 pa_assert(fdl->m);
240 for (i = 0; i < fdl->num_fds; i++)
241 fdl->m->io_free(fdl->ios[i]);
242 pa_xfree(fdl->ios);
243 }
244
245 if (fdl->fds)
246 pa_xfree(fdl->fds);
247 if (fdl->work_fds)
248 pa_xfree(fdl->work_fds);
249
250 pa_xfree(fdl);
251 }
252
253 /* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
254 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) {
255 pa_assert(fdl);
256 pa_assert(hctl_handle || mixer_handle);
257 pa_assert(!(hctl_handle && mixer_handle));
258 pa_assert(m);
259 pa_assert(!fdl->m);
260
261 fdl->hctl = hctl_handle;
262 fdl->mixer = mixer_handle;
263 fdl->m = m;
264 fdl->defer = m->defer_new(m, defer_cb, fdl);
265
266 return 0;
267 }
268
269 struct pa_alsa_mixer_pdata {
270 pa_rtpoll *rtpoll;
271 pa_rtpoll_item *poll_item;
272 snd_mixer_t *mixer;
273 };
274
275
276 struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
277 struct pa_alsa_mixer_pdata *pd;
278
279 pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
280
281 return pd;
282 }
283
284 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
285 pa_assert(pd);
286
287 if (pd->poll_item) {
288 pa_rtpoll_item_free(pd->poll_item);
289 }
290
291 pa_xfree(pd);
292 }
293
294 static int rtpoll_work_cb(pa_rtpoll_item *i) {
295 struct pa_alsa_mixer_pdata *pd;
296 struct pollfd *p;
297 unsigned n_fds;
298 unsigned short revents = 0;
299 int err, ret = 0;
300
301 pd = pa_rtpoll_item_get_userdata(i);
302 pa_assert_fp(pd);
303 pa_assert_fp(i == pd->poll_item);
304
305 p = pa_rtpoll_item_get_pollfd(i, &n_fds);
306
307 if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
308 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
309 ret = -1;
310 goto fail;
311 }
312
313 if (revents) {
314 if (revents & (POLLNVAL | POLLERR)) {
315 pa_log_debug("Device disconnected, stopping poll on mixer");
316 goto fail;
317 } else if (revents & POLLERR) {
318 /* This shouldn't happen. */
319 pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
320 goto fail;
321 }
322
323 err = snd_mixer_handle_events(pd->mixer);
324
325 if (PA_LIKELY(err >= 0)) {
326 pa_rtpoll_item_free(i);
327 pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
328 } else {
329 pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
330 ret = -1;
331 goto fail;
332 }
333 }
334
335 return ret;
336
337 fail:
338 pa_rtpoll_item_free(i);
339
340 pd->poll_item = NULL;
341 pd->rtpoll = NULL;
342 pd->mixer = NULL;
343
344 return ret;
345 }
346
347 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
348 pa_rtpoll_item *i;
349 struct pollfd *p;
350 int err, n;
351
352 pa_assert(pd);
353 pa_assert(mixer);
354 pa_assert(rtp);
355
356 if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
357 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
358 return -1;
359 }
360
361 i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
362
363 p = pa_rtpoll_item_get_pollfd(i, NULL);
364
365 memset(p, 0, sizeof(struct pollfd) * n);
366
367 if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
368 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
369 pa_rtpoll_item_free(i);
370 return -1;
371 }
372
373 pd->rtpoll = rtp;
374 pd->poll_item = i;
375 pd->mixer = mixer;
376
377 pa_rtpoll_item_set_userdata(i, pd);
378 pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
379
380 return 0;
381 }
382
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, 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, 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, pa_bool_t *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, pa_bool_t *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 pa_bool_t 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, pa_bool_t deferred_volume, pa_bool_t 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 pa_bool_t 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, pa_bool_t deferred_volume, pa_bool_t 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, pa_bool_t 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, pa_bool_t 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 pa_bool_t 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 pa_bool_t has_switch;
1280 pa_bool_t has_enumeration;
1281 pa_bool_t 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 pa_bool_t 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 pa_bool_t 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, pa_bool_t 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_PORT_AVAILABLE_NO;
1757 j->state_plugged = PA_PORT_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
1769 static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
1770 char *en;
1771 const char *on;
1772 pa_alsa_option *o;
1773 pa_alsa_element *e;
1774
1775 if (!pa_startswith(section, "Option "))
1776 return NULL;
1777
1778 section += 7;
1779
1780 /* This is not an enum section, but an element section? */
1781 if (!(on = strchr(section, ':')))
1782 return NULL;
1783
1784 en = pa_xstrndup(section, on - section);
1785 on++;
1786
1787 if (p->last_option &&
1788 pa_streq(p->last_option->element->alsa_name, en) &&
1789 pa_streq(p->last_option->alsa_name, on)) {
1790 pa_xfree(en);
1791 return p->last_option;
1792 }
1793
1794 pa_assert_se(e = element_get(p, en, FALSE));
1795 pa_xfree(en);
1796
1797 PA_LLIST_FOREACH(o, e->options)
1798 if (pa_streq(o->alsa_name, on))
1799 goto finish;
1800
1801 o = pa_xnew0(pa_alsa_option, 1);
1802 o->element = e;
1803 o->alsa_name = pa_xstrdup(on);
1804 o->alsa_idx = -1;
1805
1806 if (p->last_option && p->last_option->element == e)
1807 PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
1808 else
1809 PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
1810
1811 finish:
1812 p->last_option = o;
1813 return o;
1814 }
1815
1816 static int element_parse_switch(pa_config_parser_state *state) {
1817 pa_alsa_path *p;
1818 pa_alsa_element *e;
1819
1820 pa_assert(state);
1821
1822 p = state->userdata;
1823
1824 if (!(e = element_get(p, state->section, TRUE))) {
1825 pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
1826 return -1;
1827 }
1828
1829 if (pa_streq(state->rvalue, "ignore"))
1830 e->switch_use = PA_ALSA_SWITCH_IGNORE;
1831 else if (pa_streq(state->rvalue, "mute"))
1832 e->switch_use = PA_ALSA_SWITCH_MUTE;
1833 else if (pa_streq(state->rvalue, "off"))
1834 e->switch_use = PA_ALSA_SWITCH_OFF;
1835 else if (pa_streq(state->rvalue, "on"))
1836 e->switch_use = PA_ALSA_SWITCH_ON;
1837 else if (pa_streq(state->rvalue, "select"))
1838 e->switch_use = PA_ALSA_SWITCH_SELECT;
1839 else {
1840 pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
1841 return -1;
1842 }
1843
1844 return 0;
1845 }
1846
1847 static int element_parse_volume(pa_config_parser_state *state) {
1848 pa_alsa_path *p;
1849 pa_alsa_element *e;
1850
1851 pa_assert(state);
1852
1853 p = state->userdata;
1854
1855 if (!(e = element_get(p, state->section, TRUE))) {
1856 pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
1857 return -1;
1858 }
1859
1860 if (pa_streq(state->rvalue, "ignore"))
1861 e->volume_use = PA_ALSA_VOLUME_IGNORE;
1862 else if (pa_streq(state->rvalue, "merge"))
1863 e->volume_use = PA_ALSA_VOLUME_MERGE;
1864 else if (pa_streq(state->rvalue, "off"))
1865 e->volume_use = PA_ALSA_VOLUME_OFF;
1866 else if (pa_streq(state->rvalue, "zero"))
1867 e->volume_use = PA_ALSA_VOLUME_ZERO;
1868 else {
1869 uint32_t constant;
1870
1871 if (pa_atou(state->rvalue, &constant) >= 0) {
1872 e->volume_use = PA_ALSA_VOLUME_CONSTANT;
1873 e->constant_volume = constant;
1874 } else {
1875 pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
1876 return -1;
1877 }
1878 }
1879
1880 return 0;
1881 }
1882
1883 static int element_parse_enumeration(pa_config_parser_state *state) {
1884 pa_alsa_path *p;
1885 pa_alsa_element *e;
1886
1887 pa_assert(state);
1888
1889 p = state->userdata;
1890
1891 if (!(e = element_get(p, state->section, TRUE))) {
1892 pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
1893 return -1;
1894 }
1895
1896 if (pa_streq(state->rvalue, "ignore"))
1897 e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
1898 else if (pa_streq(state->rvalue, "select"))
1899 e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
1900 else {
1901 pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
1902 return -1;
1903 }
1904
1905 return 0;
1906 }
1907
1908 static int option_parse_priority(pa_config_parser_state *state) {
1909 pa_alsa_path *p;
1910 pa_alsa_option *o;
1911 uint32_t prio;
1912
1913 pa_assert(state);
1914
1915 p = state->userdata;
1916
1917 if (!(o = option_get(p, state->section))) {
1918 pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
1919 return -1;
1920 }
1921
1922 if (pa_atou(state->rvalue, &prio) < 0) {
1923 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
1924 return -1;
1925 }
1926
1927 o->priority = prio;
1928 return 0;
1929 }
1930
1931 static int option_parse_name(pa_config_parser_state *state) {
1932 pa_alsa_path *p;
1933 pa_alsa_option *o;
1934
1935 pa_assert(state);
1936
1937 p = state->userdata;
1938
1939 if (!(o = option_get(p, state->section))) {
1940 pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
1941 return -1;
1942 }
1943
1944 pa_xfree(o->name);
1945 o->name = pa_xstrdup(state->rvalue);
1946
1947 return 0;
1948 }
1949
1950 static int element_parse_required(pa_config_parser_state *state) {
1951 pa_alsa_path *p;
1952 pa_alsa_element *e;
1953 pa_alsa_option *o;
1954 pa_alsa_jack *j;
1955 pa_alsa_required_t req;
1956
1957 pa_assert(state);
1958
1959 p = state->userdata;
1960
1961 e = element_get(p, state->section, TRUE);
1962 o = option_get(p, state->section);
1963 j = jack_get(p, state->section);
1964 if (!e && !o && !j) {
1965 pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
1966 return -1;
1967 }
1968
1969 if (pa_streq(state->rvalue, "ignore"))
1970 req = PA_ALSA_REQUIRED_IGNORE;
1971 else if (pa_streq(state->rvalue, "switch") && e)
1972 req = PA_ALSA_REQUIRED_SWITCH;
1973 else if (pa_streq(state->rvalue, "volume") && e)
1974 req = PA_ALSA_REQUIRED_VOLUME;
1975 else if (pa_streq(state->rvalue, "enumeration"))
1976 req = PA_ALSA_REQUIRED_ENUMERATION;
1977 else if (pa_streq(state->rvalue, "any"))
1978 req = PA_ALSA_REQUIRED_ANY;
1979 else {
1980 pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
1981 return -1;
1982 }
1983
1984 if (pa_streq(state->lvalue, "required-absent")) {
1985 if (e)
1986 e->required_absent = req;
1987 if (o)
1988 o->required_absent = req;
1989 if (j)
1990 j->required_absent = req;
1991 }
1992 else if (pa_streq(state->lvalue, "required-any")) {
1993 if (e) {
1994 e->required_any = req;
1995 e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
1996 }
1997 if (o) {
1998 o->required_any = req;
1999 o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2000 }
2001 if (j) {
2002 j->required_any = req;
2003 j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
2004 }
2005
2006 }
2007 else {
2008 if (e)
2009 e->required = req;
2010 if (o)
2011 o->required = req;
2012 if (j)
2013 j->required = req;
2014 }
2015
2016 return 0;
2017 }
2018
2019 static int element_parse_direction(pa_config_parser_state *state) {
2020 pa_alsa_path *p;
2021 pa_alsa_element *e;
2022
2023 pa_assert(state);
2024
2025 p = state->userdata;
2026
2027 if (!(e = element_get(p, state->section, TRUE))) {
2028 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2029 return -1;
2030 }
2031
2032 if (pa_streq(state->rvalue, "playback"))
2033 e->direction = PA_ALSA_DIRECTION_OUTPUT;
2034 else if (pa_streq(state->rvalue, "capture"))
2035 e->direction = PA_ALSA_DIRECTION_INPUT;
2036 else {
2037 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2038 return -1;
2039 }
2040
2041 return 0;
2042 }
2043
2044 static int element_parse_direction_try_other(pa_config_parser_state *state) {
2045 pa_alsa_path *p;
2046 pa_alsa_element *e;
2047 int yes;
2048
2049 pa_assert(state);
2050
2051 p = state->userdata;
2052
2053 if (!(e = element_get(p, state->section, TRUE))) {
2054 pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
2055 return -1;
2056 }
2057
2058 if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
2059 pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
2060 return -1;
2061 }
2062
2063 e->direction_try_other = !!yes;
2064 return 0;
2065 }
2066
2067 static int element_parse_volume_limit(pa_config_parser_state *state) {
2068 pa_alsa_path *p;
2069 pa_alsa_element *e;
2070 long volume_limit;
2071
2072 pa_assert(state);
2073
2074 p = state->userdata;
2075
2076 if (!(e = element_get(p, state->section, TRUE))) {
2077 pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
2078 return -1;
2079 }
2080
2081 if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
2082 pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
2083 return -1;
2084 }
2085
2086 e->volume_limit = volume_limit;
2087 return 0;
2088 }
2089
2090 static pa_channel_position_mask_t parse_mask(const char *m) {
2091 pa_channel_position_mask_t v;
2092
2093 if (pa_streq(m, "all-left"))
2094 v = PA_CHANNEL_POSITION_MASK_LEFT;
2095 else if (pa_streq(m, "all-right"))
2096 v = PA_CHANNEL_POSITION_MASK_RIGHT;
2097 else if (pa_streq(m, "all-center"))
2098 v = PA_CHANNEL_POSITION_MASK_CENTER;
2099 else if (pa_streq(m, "all-front"))
2100 v = PA_CHANNEL_POSITION_MASK_FRONT;
2101 else if (pa_streq(m, "all-rear"))
2102 v = PA_CHANNEL_POSITION_MASK_REAR;
2103 else if (pa_streq(m, "all-side"))
2104 v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
2105 else if (pa_streq(m, "all-top"))
2106 v = PA_CHANNEL_POSITION_MASK_TOP;
2107 else if (pa_streq(m, "all-no-lfe"))
2108 v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
2109 else if (pa_streq(m, "all"))
2110 v = PA_CHANNEL_POSITION_MASK_ALL;
2111 else {
2112 pa_channel_position_t p;
2113
2114 if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
2115 return 0;
2116
2117 v = PA_CHANNEL_POSITION_MASK(p);
2118 }
2119
2120 return v;
2121 }
2122
2123 static int element_parse_override_map(pa_config_parser_state *state) {
2124 pa_alsa_path *p;
2125 pa_alsa_element *e;
2126 const char *split_state = NULL;
2127 unsigned i = 0;
2128 char *n;
2129
2130 pa_assert(state);
2131
2132 p = state->userdata;
2133
2134 if (!(e = element_get(p, state->section, TRUE))) {
2135 pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
2136 return -1;
2137 }
2138
2139 while ((n = pa_split(state->rvalue, ",", &split_state))) {
2140 pa_channel_position_mask_t m;
2141
2142 if (!*n)
2143 m = 0;
2144 else {
2145 if ((m = parse_mask(n)) == 0) {
2146 pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
2147 pa_xfree(n);
2148 return -1;
2149 }
2150 }
2151
2152 if (pa_streq(state->lvalue, "override-map.1"))
2153 e->masks[i++][0] = m;
2154 else
2155 e->masks[i++][1] = m;
2156
2157 /* Later on we might add override-map.3 and so on here ... */
2158
2159 pa_xfree(n);
2160 }
2161
2162 e->override_map = TRUE;
2163
2164 return 0;
2165 }
2166
2167 static int jack_parse_state(pa_config_parser_state *state) {
2168 pa_alsa_path *p;
2169 pa_alsa_jack *j;
2170 pa_port_available_t pa;
2171
2172 pa_assert(state);
2173
2174 p = state->userdata;
2175
2176 if (!(j = jack_get(p, state->section))) {
2177 pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
2178 return -1;
2179 }
2180
2181 if (pa_streq(state->rvalue, "yes"))
2182 pa = PA_PORT_AVAILABLE_YES;
2183 else if (pa_streq(state->rvalue, "no"))
2184 pa = PA_PORT_AVAILABLE_NO;
2185 else if (pa_streq(state->rvalue, "unknown"))
2186 pa = PA_PORT_AVAILABLE_UNKNOWN;
2187 else {
2188 pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
2189 return -1;
2190 }
2191
2192 if (pa_streq(state->lvalue, "state.unplugged"))
2193 j->state_unplugged = pa;
2194 else {
2195 j->state_plugged = pa;
2196 pa_assert(pa_streq(state->lvalue, "state.plugged"));
2197 }
2198
2199 return 0;
2200 }
2201
2202 static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
2203 snd_mixer_selem_id_t *sid;
2204 snd_mixer_elem_t *me;
2205 int r;
2206
2207 pa_assert(e);
2208 pa_assert(m);
2209
2210 SELEM_INIT(sid, e->alsa_name);
2211 if (!(me = snd_mixer_find_selem(m, sid))) {
2212 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2213 return -1;
2214 }
2215
2216 if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
2217
2218 if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
2219 r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
2220 else
2221 r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
2222
2223 if (r < 0)
2224 pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2225
2226 } else {
2227 pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
2228
2229 if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
2230 pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
2231 }
2232
2233 return r;
2234 }
2235
2236 static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
2237 pa_alsa_option *o;
2238 uint32_t idx;
2239
2240 pa_assert(s);
2241 pa_assert(m);
2242
2243 PA_IDXSET_FOREACH(o, s->options, idx)
2244 element_set_option(o->element, m, o->alsa_idx);
2245
2246 return 0;
2247 }
2248
2249 static int option_verify(pa_alsa_option *o) {
2250 static const struct description_map well_known_descriptions[] = {
2251 { "input", N_("Input") },
2252 { "input-docking", N_("Docking Station Input") },
2253 { "input-docking-microphone", N_("Docking Station Microphone") },
2254 { "input-docking-linein", N_("Docking Station Line In") },
2255 { "input-linein", N_("Line In") },
2256 { "input-microphone", N_("Microphone") },
2257 { "input-microphone-front", N_("Front Microphone") },
2258 { "input-microphone-rear", N_("Rear Microphone") },
2259 { "input-microphone-external", N_("External Microphone") },
2260 { "input-microphone-internal", N_("Internal Microphone") },
2261 { "input-radio", N_("Radio") },
2262 { "input-video", N_("Video") },
2263 { "input-agc-on", N_("Automatic Gain Control") },
2264 { "input-agc-off", N_("No Automatic Gain Control") },
2265 { "input-boost-on", N_("Boost") },
2266 { "input-boost-off", N_("No Boost") },
2267 { "output-amplifier-on", N_("Amplifier") },
2268 { "output-amplifier-off", N_("No Amplifier") },
2269 { "output-bass-boost-on", N_("Bass Boost") },
2270 { "output-bass-boost-off", N_("No Bass Boost") },
2271 { "output-speaker", N_("Speaker") },
2272 { "output-headphones", N_("Headphones") }
2273 };
2274
2275 pa_assert(o);
2276
2277 if (!o->name) {
2278 pa_log("No name set for option %s", o->alsa_name);
2279 return -1;
2280 }
2281
2282 if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
2283 o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
2284 pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
2285 return -1;
2286 }
2287
2288 if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
2289 !pa_streq(o->alsa_name, "on") &&
2290 !pa_streq(o->alsa_name, "off")) {
2291 pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
2292 return -1;
2293 }
2294
2295 if (!o->description)
2296 o->description = pa_xstrdup(lookup_description(o->name,
2297 well_known_descriptions,
2298 PA_ELEMENTSOF(well_known_descriptions)));
2299 if (!o->description)
2300 o->description = pa_xstrdup(o->name);
2301
2302 return 0;
2303 }
2304
2305 static int element_verify(pa_alsa_element *e) {
2306 pa_alsa_option *o;
2307
2308 pa_assert(e);
2309
2310 // 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);
2311 if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
2312 (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
2313 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
2314 (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
2315 pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
2316 return -1;
2317 }
2318
2319 if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
2320 pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
2321 return -1;
2322 }
2323
2324 PA_LLIST_FOREACH(o, e->options)
2325 if (option_verify(o) < 0)
2326 return -1;
2327
2328 return 0;
2329 }
2330
2331 static int path_verify(pa_alsa_path *p) {
2332 static const struct description_map well_known_descriptions[] = {
2333 { "analog-input", N_("Analog Input") },
2334 { "analog-input-microphone", N_("Microphone") },
2335 { "analog-input-microphone-front", N_("Front Microphone") },
2336 { "analog-input-microphone-rear", N_("Rear Microphone") },
2337 { "analog-input-microphone-dock", N_("Dock Microphone") },
2338 { "analog-input-microphone-internal", N_("Internal 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->name,
2364 well_known_descriptions,
2365 PA_ELEMENTSOF(well_known_descriptions)));
2366
2367 if (!p->description)
2368 p->description = pa_xstrdup(p->name);
2369
2370 return 0;
2371 }
2372
2373 static const char *get_default_paths_dir(void) {
2374 if (pa_run_from_build_tree())
2375 return PA_BUILDDIR "/modules/alsa/mixer/paths/";
2376 else
2377 return PA_ALSA_PATHS_DIR;
2378 }
2379
2380 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2381 pa_alsa_path *p;
2382 char *fn;
2383 int r;
2384 const char *n;
2385 bool mute_during_activation = false;
2386
2387 pa_config_item items[] = {
2388 /* [General] */
2389 { "priority", pa_config_parse_unsigned, NULL, "General" },
2390 { "description", pa_config_parse_string, NULL, "General" },
2391 { "name", pa_config_parse_string, NULL, "General" },
2392 { "mute-during-activation", pa_config_parse_bool, NULL, "General" },
2393
2394 /* [Option ...] */
2395 { "priority", option_parse_priority, NULL, NULL },
2396 { "name", option_parse_name, NULL, NULL },
2397
2398 /* [Jack ...] */
2399 { "state.plugged", jack_parse_state, NULL, NULL },
2400 { "state.unplugged", jack_parse_state, NULL, NULL },
2401
2402 /* [Element ...] */
2403 { "switch", element_parse_switch, NULL, NULL },
2404 { "volume", element_parse_volume, NULL, NULL },
2405 { "enumeration", element_parse_enumeration, NULL, NULL },
2406 { "override-map.1", element_parse_override_map, NULL, NULL },
2407 { "override-map.2", element_parse_override_map, NULL, NULL },
2408 /* ... later on we might add override-map.3 and so on here ... */
2409 { "required", element_parse_required, NULL, NULL },
2410 { "required-any", element_parse_required, NULL, NULL },
2411 { "required-absent", element_parse_required, NULL, NULL },
2412 { "direction", element_parse_direction, NULL, NULL },
2413 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2414 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2415 { NULL, NULL, NULL, NULL }
2416 };
2417
2418 pa_assert(fname);
2419
2420 p = pa_xnew0(pa_alsa_path, 1);
2421 n = pa_path_get_filename(fname);
2422 p->name = pa_xstrndup(n, strcspn(n, "."));
2423 p->proplist = pa_proplist_new();
2424 p->direction = direction;
2425
2426 items[0].data = &p->priority;
2427 items[1].data = &p->description;
2428 items[2].data = &p->name;
2429 items[3].data = &mute_during_activation;
2430
2431 if (!paths_dir)
2432 paths_dir = get_default_paths_dir();
2433
2434 fn = pa_maybe_prefix_path(fname, paths_dir);
2435
2436 r = pa_config_parse(fn, NULL, items, p->proplist, p);
2437 pa_xfree(fn);
2438
2439 if (r < 0)
2440 goto fail;
2441
2442 p->mute_during_activation = mute_during_activation;
2443
2444 if (path_verify(p) < 0)
2445 goto fail;
2446
2447 return p;
2448
2449 fail:
2450 pa_alsa_path_free(p);
2451 return NULL;
2452 }
2453
2454 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2455 pa_alsa_path *p;
2456 pa_alsa_element *e;
2457
2458 pa_assert(element);
2459
2460 p = pa_xnew0(pa_alsa_path, 1);
2461 p->name = pa_xstrdup(element);
2462 p->direction = direction;
2463
2464 e = pa_xnew0(pa_alsa_element, 1);
2465 e->path = p;
2466 e->alsa_name = pa_xstrdup(element);
2467 e->direction = direction;
2468 e->volume_limit = -1;
2469
2470 e->switch_use = PA_ALSA_SWITCH_MUTE;
2471 e->volume_use = PA_ALSA_VOLUME_MERGE;
2472
2473 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2474 p->last_element = e;
2475 return p;
2476 }
2477
2478 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2479 pa_alsa_option *o, *n;
2480
2481 pa_assert(e);
2482
2483 for (o = e->options; o; o = n) {
2484 n = o->next;
2485
2486 if (o->alsa_idx < 0) {
2487 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2488 option_free(o);
2489 }
2490 }
2491
2492 return
2493 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2494 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2495 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2496 }
2497
2498 static void path_drop_unsupported(pa_alsa_path *p) {
2499 pa_alsa_element *e, *n;
2500
2501 pa_assert(p);
2502
2503 for (e = p->elements; e; e = n) {
2504 n = e->next;
2505
2506 if (!element_drop_unsupported(e)) {
2507 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2508 element_free(e);
2509 }
2510 }
2511 }
2512
2513 static void path_make_options_unique(pa_alsa_path *p) {
2514 pa_alsa_element *e;
2515 pa_alsa_option *o, *u;
2516
2517 PA_LLIST_FOREACH(e, p->elements) {
2518 PA_LLIST_FOREACH(o, e->options) {
2519 unsigned i;
2520 char *m;
2521
2522 for (u = o->next; u; u = u->next)
2523 if (pa_streq(u->name, o->name))
2524 break;
2525
2526 if (!u)
2527 continue;
2528
2529 m = pa_xstrdup(o->name);
2530
2531 /* OK, this name is not unique, hence let's rename */
2532 for (i = 1, u = o; u; u = u->next) {
2533 char *nn, *nd;
2534
2535 if (!pa_streq(u->name, m))
2536 continue;
2537
2538 nn = pa_sprintf_malloc("%s-%u", m, i);
2539 pa_xfree(u->name);
2540 u->name = nn;
2541
2542 nd = pa_sprintf_malloc("%s %u", u->description, i);
2543 pa_xfree(u->description);
2544 u->description = nd;
2545
2546 i++;
2547 }
2548
2549 pa_xfree(m);
2550 }
2551 }
2552 }
2553
2554 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2555 pa_alsa_option *o;
2556
2557 for (; e; e = e->next)
2558 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2559 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2560 break;
2561
2562 if (!e)
2563 return FALSE;
2564
2565 for (o = e->options; o; o = o->next) {
2566 pa_alsa_setting *s;
2567
2568 if (template) {
2569 s = pa_xnewdup(pa_alsa_setting, template, 1);
2570 s->options = pa_idxset_copy(template->options);
2571 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2572 s->description =
2573 (template->description[0] && o->description[0])
2574 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2575 : (template->description[0]
2576 ? pa_xstrdup(template->description)
2577 : pa_xstrdup(o->description));
2578
2579 s->priority = PA_MAX(template->priority, o->priority);
2580 } else {
2581 s = pa_xnew0(pa_alsa_setting, 1);
2582 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2583 s->name = pa_xstrdup(o->name);
2584 s->description = pa_xstrdup(o->description);
2585 s->priority = o->priority;
2586 }
2587
2588 pa_idxset_put(s->options, o, NULL);
2589
2590 if (element_create_settings(e->next, s))
2591 /* This is not a leaf, so let's get rid of it */
2592 setting_free(s);
2593 else {
2594 /* This is a leaf, so let's add it */
2595 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2596
2597 e->path->last_setting = s;
2598 }
2599 }
2600
2601 return TRUE;
2602 }
2603
2604 static void path_create_settings(pa_alsa_path *p) {
2605 pa_assert(p);
2606
2607 element_create_settings(p->elements, NULL);
2608 }
2609
2610 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, snd_hctl_t *hctl, pa_bool_t ignore_dB) {
2611 pa_alsa_element *e;
2612 pa_alsa_jack *j;
2613 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2614 pa_channel_position_t t;
2615 pa_channel_position_mask_t path_volume_channels = 0;
2616
2617 pa_assert(p);
2618 pa_assert(m);
2619
2620 if (p->probed)
2621 return p->supported ? 0 : -1;
2622 p->probed = TRUE;
2623
2624 pa_zero(min_dB);
2625 pa_zero(max_dB);
2626
2627 pa_log_debug("Probing path '%s'", p->name);
2628
2629 PA_LLIST_FOREACH(j, p->jacks) {
2630 if (jack_probe(j, hctl) < 0) {
2631 p->supported = FALSE;
2632 pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2633 return -1;
2634 }
2635 pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2636 }
2637
2638 PA_LLIST_FOREACH(e, p->elements) {
2639 if (element_probe(e, m) < 0) {
2640 p->supported = FALSE;
2641 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2642 return -1;
2643 }
2644 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);
2645
2646 if (ignore_dB)
2647 e->has_dB = FALSE;
2648
2649 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2650
2651 if (!p->has_volume) {
2652 p->min_volume = e->min_volume;
2653 p->max_volume = e->max_volume;
2654 }
2655
2656 if (e->has_dB) {
2657 if (!p->has_volume) {
2658 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2659 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2660 min_dB[t] = e->min_dB;
2661 max_dB[t] = e->max_dB;
2662 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2663 }
2664
2665 p->has_dB = TRUE;
2666 } else {
2667
2668 if (p->has_dB) {
2669 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2670 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2671 min_dB[t] += e->min_dB;
2672 max_dB[t] += e->max_dB;
2673 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2674 }
2675 } else {
2676 /* Hmm, there's another element before us
2677 * which cannot do dB volumes, so we we need
2678 * to 'neutralize' this slider */
2679 e->volume_use = PA_ALSA_VOLUME_ZERO;
2680 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2681 }
2682 }
2683 } else if (p->has_volume) {
2684 /* We can't use this volume, so let's ignore it */
2685 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2686 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2687 }
2688 p->has_volume = TRUE;
2689 }
2690
2691 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2692 p->has_mute = TRUE;
2693 }
2694
2695 if (p->has_req_any && !p->req_any_present) {
2696 p->supported = FALSE;
2697 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2698 return -1;
2699 }
2700
2701 path_drop_unsupported(p);
2702 path_make_options_unique(p);
2703 path_create_settings(p);
2704
2705 p->supported = TRUE;
2706
2707 p->min_dB = INFINITY;
2708 p->max_dB = -INFINITY;
2709
2710 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2711 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2712 if (p->min_dB > min_dB[t])
2713 p->min_dB = min_dB[t];
2714
2715 if (p->max_dB < max_dB[t])
2716 p->max_dB = max_dB[t];
2717 }
2718 }
2719
2720 return 0;
2721 }
2722
2723 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2724 pa_assert(s);
2725
2726 pa_log_debug("Setting %s (%s) priority=%u",
2727 s->name,
2728 pa_strnull(s->description),
2729 s->priority);
2730 }
2731
2732 void pa_alsa_jack_dump(pa_alsa_jack *j) {
2733 pa_assert(j);
2734
2735 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2736 }
2737
2738 void pa_alsa_option_dump(pa_alsa_option *o) {
2739 pa_assert(o);
2740
2741 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2742 o->alsa_name,
2743 pa_strnull(o->name),
2744 pa_strnull(o->description),
2745 o->alsa_idx,
2746 o->priority);
2747 }
2748
2749 void pa_alsa_element_dump(pa_alsa_element *e) {
2750 pa_alsa_option *o;
2751 pa_assert(e);
2752
2753 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",
2754 e->alsa_name,
2755 e->direction,
2756 e->switch_use,
2757 e->volume_use,
2758 e->volume_limit,
2759 e->enumeration_use,
2760 e->required,
2761 e->required_any,
2762 e->required_absent,
2763 (long long unsigned) e->merged_mask,
2764 e->n_channels,
2765 pa_yes_no(e->override_map));
2766
2767 PA_LLIST_FOREACH(o, e->options)
2768 pa_alsa_option_dump(o);
2769 }
2770
2771 void pa_alsa_path_dump(pa_alsa_path *p) {
2772 pa_alsa_element *e;
2773 pa_alsa_jack *j;
2774 pa_alsa_setting *s;
2775 pa_assert(p);
2776
2777 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2778 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2779 p->name,
2780 pa_strnull(p->description),
2781 p->direction,
2782 p->priority,
2783 pa_yes_no(p->probed),
2784 pa_yes_no(p->supported),
2785 pa_yes_no(p->has_mute),
2786 pa_yes_no(p->has_volume),
2787 pa_yes_no(p->has_dB),
2788 p->min_volume, p->max_volume,
2789 p->min_dB, p->max_dB);
2790
2791 PA_LLIST_FOREACH(e, p->elements)
2792 pa_alsa_element_dump(e);
2793
2794 PA_LLIST_FOREACH(j, p->jacks)
2795 pa_alsa_jack_dump(j);
2796
2797 PA_LLIST_FOREACH(s, p->settings)
2798 pa_alsa_setting_dump(s);
2799 }
2800
2801 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2802 snd_mixer_selem_id_t *sid;
2803 snd_mixer_elem_t *me;
2804
2805 pa_assert(e);
2806 pa_assert(m);
2807 pa_assert(cb);
2808
2809 SELEM_INIT(sid, e->alsa_name);
2810 if (!(me = snd_mixer_find_selem(m, sid))) {
2811 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2812 return;
2813 }
2814
2815 snd_mixer_elem_set_callback(me, cb);
2816 snd_mixer_elem_set_callback_private(me, userdata);
2817 }
2818
2819 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2820 pa_alsa_element *e;
2821
2822 pa_assert(p);
2823 pa_assert(m);
2824 pa_assert(cb);
2825
2826 PA_LLIST_FOREACH(e, p->elements)
2827 element_set_callback(e, m, cb, userdata);
2828 }
2829
2830 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2831 pa_alsa_path *p;
2832 void *state;
2833
2834 pa_assert(ps);
2835 pa_assert(m);
2836 pa_assert(cb);
2837
2838 PA_HASHMAP_FOREACH(p, ps->paths, state)
2839 pa_alsa_path_set_callback(p, m, cb, userdata);
2840 }
2841
2842 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2843 pa_alsa_path_set *ps;
2844 char **pn = NULL, **en = NULL, **ie;
2845 pa_alsa_decibel_fix *db_fix;
2846 void *state, *state2;
2847 pa_hashmap *cache;
2848
2849 pa_assert(m);
2850 pa_assert(m->profile_set);
2851 pa_assert(m->profile_set->decibel_fixes);
2852 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2853
2854 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2855 return NULL;
2856
2857 ps = pa_xnew0(pa_alsa_path_set, 1);
2858 ps->direction = direction;
2859 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2860
2861 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2862 pn = m->output_path_names;
2863 cache = m->profile_set->output_paths;
2864 }
2865 else if (direction == PA_ALSA_DIRECTION_INPUT) {
2866 pn = m->input_path_names;
2867 cache = m->profile_set->input_paths;
2868 }
2869
2870 if (pn) {
2871 char **in;
2872
2873 for (in = pn; *in; in++) {
2874 pa_alsa_path *p = NULL;
2875 pa_bool_t duplicate = FALSE;
2876 char **kn;
2877
2878 for (kn = pn; kn < in; kn++)
2879 if (pa_streq(*kn, *in)) {
2880 duplicate = TRUE;
2881 break;
2882 }
2883
2884 if (duplicate)
2885 continue;
2886
2887 p = pa_hashmap_get(cache, *in);
2888 if (!p) {
2889 char *fn = pa_sprintf_malloc("%s.conf", *in);
2890 p = pa_alsa_path_new(paths_dir, fn, direction);
2891 pa_xfree(fn);
2892 if (p)
2893 pa_hashmap_put(cache, *in, p);
2894 }
2895 pa_assert(pa_hashmap_get(cache, *in) == p);
2896 if (p)
2897 pa_hashmap_put(ps->paths, p, p);
2898
2899 }
2900
2901 goto finish;
2902 }
2903
2904 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2905 en = m->output_element;
2906 else if (direction == PA_ALSA_DIRECTION_INPUT)
2907 en = m->input_element;
2908
2909 if (!en) {
2910 pa_alsa_path_set_free(ps);
2911 return NULL;
2912 }
2913
2914 for (ie = en; *ie; ie++) {
2915 char **je;
2916 pa_alsa_path *p;
2917
2918 p = pa_alsa_path_synthesize(*ie, direction);
2919
2920 /* Mark all other passed elements for require-absent */
2921 for (je = en; *je; je++) {
2922 pa_alsa_element *e;
2923
2924 if (je == ie)
2925 continue;
2926
2927 e = pa_xnew0(pa_alsa_element, 1);
2928 e->path = p;
2929 e->alsa_name = pa_xstrdup(*je);
2930 e->direction = direction;
2931 e->required_absent = PA_ALSA_REQUIRED_ANY;
2932 e->volume_limit = -1;
2933
2934 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2935 p->last_element = e;
2936 }
2937
2938 pa_hashmap_put(ps->paths, *ie, p);
2939 }
2940
2941 finish:
2942 /* Assign decibel fixes to elements. */
2943 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2944 pa_alsa_path *p;
2945
2946 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2947 pa_alsa_element *e;
2948
2949 PA_LLIST_FOREACH(e, p->elements) {
2950 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2951 /* The profile set that contains the dB fix may be freed
2952 * before the element, so we have to copy the dB fix
2953 * object. */
2954 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2955 e->db_fix->profile_set = NULL;
2956 e->db_fix->name = pa_xstrdup(db_fix->name);
2957 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2958 }
2959 }
2960 }
2961 }
2962
2963 return ps;
2964 }
2965
2966 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2967 pa_alsa_path *p;
2968 void *state;
2969 pa_assert(ps);
2970
2971 pa_log_debug("Path Set %p, direction=%i",
2972 (void*) ps,
2973 ps->direction);
2974
2975 PA_HASHMAP_FOREACH(p, ps->paths, state)
2976 pa_alsa_path_dump(p);
2977 }
2978
2979
2980 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2981 pa_alsa_option *o;
2982
2983 pa_assert(options);
2984 pa_assert(alsa_name);
2985
2986 PA_LLIST_FOREACH(o, options) {
2987 if (pa_streq(o->alsa_name, alsa_name))
2988 return TRUE;
2989 }
2990 return FALSE;
2991 }
2992
2993 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2994 pa_alsa_option *oa, *ob;
2995
2996 if (!a_options) return TRUE;
2997 if (!b_options) return FALSE;
2998
2999 /* If there is an option A offers that B does not, then A is not a subset of B. */
3000 PA_LLIST_FOREACH(oa, a_options) {
3001 pa_bool_t found = FALSE;
3002 PA_LLIST_FOREACH(ob, b_options) {
3003 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3004 found = TRUE;
3005 break;
3006 }
3007 }
3008 if (!found)
3009 return FALSE;
3010 }
3011 return TRUE;
3012 }
3013
3014 /**
3015 * Compares two elements to see if a is a subset of b
3016 */
3017 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3018 pa_assert(a);
3019 pa_assert(b);
3020 pa_assert(m);
3021
3022 /* General rules:
3023 * Every state is a subset of itself (with caveats for volume_limits and options)
3024 * IGNORE is a subset of every other state */
3025
3026 /* Check the volume_use */
3027 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3028
3029 /* "Constant" is subset of "Constant" only when their constant values are equal */
3030 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3031 return FALSE;
3032
3033 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3034 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3035 return FALSE;
3036
3037 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3038 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3039 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3040 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3041 long a_limit;
3042
3043 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3044 a_limit = a->constant_volume;
3045 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3046 long dB = 0;
3047
3048 if (a->db_fix) {
3049 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3050 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3051 } else {
3052 snd_mixer_selem_id_t *sid;
3053 snd_mixer_elem_t *me;
3054
3055 SELEM_INIT(sid, a->alsa_name);
3056 if (!(me = snd_mixer_find_selem(m, sid))) {
3057 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3058 return FALSE;
3059 }
3060
3061 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3062 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3063 return FALSE;
3064 } else {
3065 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3066 return FALSE;
3067 }
3068 }
3069 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3070 a_limit = a->min_volume;
3071 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3072 a_limit = a->volume_limit;
3073 else
3074 /* This should never be reached */
3075 pa_assert(FALSE);
3076
3077 if (a_limit > b->volume_limit)
3078 return FALSE;
3079 }
3080
3081 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3082 int s;
3083 /* If override-maps are different, they're not subsets */
3084 if (a->n_channels != b->n_channels)
3085 return FALSE;
3086 for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3087 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3088 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3089 a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3090 return FALSE;
3091 }
3092 }
3093 }
3094
3095 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3096 /* "On" is a subset of "Mute".
3097 * "Off" is a subset of "Mute".
3098 * "On" is a subset of "Select", if there is an "Option:On" in B.
3099 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3100 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3101
3102 if (a->switch_use != b->switch_use) {
3103
3104 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3105 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3106 return FALSE;
3107
3108 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3109 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3110 if (!options_have_option(b->options, "on"))
3111 return FALSE;
3112 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3113 if (!options_have_option(b->options, "off"))
3114 return FALSE;
3115 }
3116 }
3117 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3118 if (!enumeration_is_subset(a->options, b->options))
3119 return FALSE;
3120 }
3121 }
3122
3123 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3124 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3125 return FALSE;
3126 if (!enumeration_is_subset(a->options, b->options))
3127 return FALSE;
3128 }
3129
3130 return TRUE;
3131 }
3132
3133 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3134 pa_alsa_path *p;
3135 void *state;
3136
3137 pa_assert(ps);
3138 pa_assert(m);
3139
3140 /* If we only have one path, then don't bother */
3141 if (pa_hashmap_size(ps->paths) < 2)
3142 return;
3143
3144 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3145 pa_alsa_path *p2;
3146 void *state2;
3147
3148 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3149 pa_alsa_element *ea, *eb;
3150 pa_alsa_jack *ja, *jb;
3151 pa_bool_t is_subset = TRUE;
3152
3153 if (p == p2)
3154 continue;
3155
3156 /* If a has a jack that b does not have, a is not a subset */
3157 PA_LLIST_FOREACH(ja, p->jacks) {
3158 pa_bool_t exists = FALSE;
3159
3160 if (!ja->has_control)
3161 continue;
3162
3163 PA_LLIST_FOREACH(jb, p2->jacks) {
3164 if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
3165 (ja->state_plugged == jb->state_plugged) &&
3166 (ja->state_unplugged == jb->state_unplugged)) {
3167 exists = TRUE;
3168 break;
3169 }
3170 }
3171
3172 if (!exists) {
3173 is_subset = FALSE;
3174 break;
3175 }
3176 }
3177
3178 /* Compare the elements of each set... */
3179 pa_assert_se(ea = p->elements);
3180 pa_assert_se(eb = p2->elements);
3181
3182 while (is_subset) {
3183 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3184 if (element_is_subset(ea, eb, m)) {
3185 ea = ea->next;
3186 eb = eb->next;
3187 if ((ea && !eb) || (!ea && eb))
3188 is_subset = FALSE;
3189 else if (!ea && !eb)
3190 break;
3191 } else
3192 is_subset = FALSE;
3193
3194 } else
3195 is_subset = FALSE;
3196 }
3197
3198 if (is_subset) {
3199 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3200 pa_hashmap_remove(ps->paths, p);
3201 break;
3202 }
3203 }
3204 }
3205 }
3206
3207 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3208 {
3209 pa_alsa_path* p;
3210 void *state;
3211
3212 PA_HASHMAP_FOREACH(p, ps->paths, state)
3213 if (p != ignore && pa_streq(p->name, name))
3214 return p;
3215 return NULL;
3216 }
3217
3218 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3219 pa_alsa_path *p, *q;
3220 void *state, *state2;
3221
3222 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3223 unsigned i;
3224 char *m;
3225
3226 q = path_set_find_path_by_name(ps, p->name, p);
3227
3228 if (!q)
3229 continue;
3230
3231 m = pa_xstrdup(p->name);
3232
3233 /* OK, this name is not unique, hence let's rename */
3234 i = 1;
3235 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3236 char *nn, *nd;
3237
3238 if (!pa_streq(q->name, m))
3239 continue;
3240
3241 nn = pa_sprintf_malloc("%s-%u", m, i);
3242 pa_xfree(q->name);
3243 q->name = nn;
3244
3245 nd = pa_sprintf_malloc("%s %u", q->description, i);
3246 pa_xfree(q->description);
3247 q->description = nd;
3248
3249 i++;
3250 }
3251
3252 pa_xfree(m);
3253 }
3254 }
3255
3256 static void mapping_free(pa_alsa_mapping *m) {
3257 pa_assert(m);
3258
3259 pa_xfree(m->name);
3260 pa_xfree(m->description);
3261
3262 pa_proplist_free(m->proplist);
3263
3264 pa_xstrfreev(m->device_strings);
3265 pa_xstrfreev(m->input_path_names);
3266 pa_xstrfreev(m->output_path_names);
3267 pa_xstrfreev(m->input_element);
3268 pa_xstrfreev(m->output_element);
3269 if (m->input_path_set)
3270 pa_alsa_path_set_free(m->input_path_set);
3271 if (m->output_path_set)
3272 pa_alsa_path_set_free(m->output_path_set);
3273
3274 pa_assert(!m->input_pcm);
3275 pa_assert(!m->output_pcm);
3276
3277 pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3278
3279 pa_xfree(m);
3280 }
3281
3282 static void profile_free(pa_alsa_profile *p) {
3283 pa_assert(p);
3284
3285 pa_xfree(p->name);
3286 pa_xfree(p->description);
3287
3288 pa_xstrfreev(p->input_mapping_names);
3289 pa_xstrfreev(p->output_mapping_names);
3290
3291 if (p->input_mappings)
3292 pa_idxset_free(p->input_mappings, NULL, NULL);
3293
3294 if (p->output_mappings)
3295 pa_idxset_free(p->output_mappings, NULL, NULL);
3296
3297 pa_xfree(p);
3298 }
3299
3300 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3301 pa_assert(ps);
3302
3303 if (ps->input_paths) {
3304 pa_alsa_path *p;
3305
3306 while ((p = pa_hashmap_steal_first(ps->input_paths)))
3307 pa_alsa_path_free(p);
3308
3309 pa_hashmap_free(ps->input_paths, NULL, NULL);
3310 }
3311
3312 if (ps->output_paths) {
3313 pa_alsa_path *p;
3314
3315 while ((p = pa_hashmap_steal_first(ps->output_paths)))
3316 pa_alsa_path_free(p);
3317
3318 pa_hashmap_free(ps->output_paths, NULL, NULL);
3319 }
3320
3321 if (ps->profiles) {
3322 pa_alsa_profile *p;
3323
3324 while ((p = pa_hashmap_steal_first(ps->profiles)))
3325 profile_free(p);
3326
3327 pa_hashmap_free(ps->profiles, NULL, NULL);
3328 }
3329
3330 if (ps->mappings) {
3331 pa_alsa_mapping *m;
3332
3333 while ((m = pa_hashmap_steal_first(ps->mappings)))
3334 mapping_free(m);
3335
3336 pa_hashmap_free(ps->mappings, NULL, NULL);
3337 }
3338
3339 if (ps->decibel_fixes) {
3340 pa_alsa_decibel_fix *db_fix;
3341
3342 while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes)))
3343 decibel_fix_free(db_fix);
3344
3345 pa_hashmap_free(ps->decibel_fixes, NULL, NULL);
3346 }
3347
3348 pa_xfree(ps);
3349 }
3350
3351 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3352 pa_alsa_mapping *m;
3353
3354 if (!pa_startswith(name, "Mapping "))
3355 return NULL;
3356
3357 name += 8;
3358
3359 if ((m = pa_hashmap_get(ps->mappings, name)))
3360 return m;
3361
3362 m = pa_xnew0(pa_alsa_mapping, 1);
3363 m->profile_set = ps;
3364 m->name = pa_xstrdup(name);
3365 pa_channel_map_init(&m->channel_map);
3366 m->proplist = pa_proplist_new();
3367
3368 pa_hashmap_put(ps->mappings, m->name, m);
3369
3370 return m;
3371 }
3372
3373 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3374 pa_alsa_profile *p;
3375
3376 if (!pa_startswith(name, "Profile "))
3377 return NULL;
3378
3379 name += 8;
3380
3381 if ((p = pa_hashmap_get(ps->profiles, name)))
3382 return p;
3383
3384 p = pa_xnew0(pa_alsa_profile, 1);
3385 p->profile_set = ps;
3386 p->name = pa_xstrdup(name);
3387
3388 pa_hashmap_put(ps->profiles, p->name, p);
3389
3390 return p;
3391 }
3392
3393 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3394 pa_alsa_decibel_fix *db_fix;
3395
3396 if (!pa_startswith(name, "DecibelFix "))
3397 return NULL;
3398
3399 name += 11;
3400
3401 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3402 return db_fix;
3403
3404 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3405 db_fix->profile_set = ps;
3406 db_fix->name = pa_xstrdup(name);
3407
3408 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3409
3410 return db_fix;
3411 }
3412
3413 static int mapping_parse_device_strings(pa_config_parser_state *state) {
3414 pa_alsa_profile_set *ps;
3415 pa_alsa_mapping *m;
3416
3417 pa_assert(state);
3418
3419 ps = state->userdata;
3420
3421 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3422 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3423 return -1;
3424 }
3425
3426 pa_xstrfreev(m->device_strings);
3427 if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3428 pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3429 return -1;
3430 }
3431
3432 return 0;
3433 }
3434
3435 static int mapping_parse_channel_map(pa_config_parser_state *state) {
3436 pa_alsa_profile_set *ps;
3437 pa_alsa_mapping *m;
3438
3439 pa_assert(state);
3440
3441 ps = state->userdata;
3442
3443 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3444 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3445 return -1;
3446 }
3447
3448 if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3449 pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3450 return -1;
3451 }
3452
3453 return 0;
3454 }
3455
3456 static int mapping_parse_paths(pa_config_parser_state *state) {
3457 pa_alsa_profile_set *ps;
3458 pa_alsa_mapping *m;
3459
3460 pa_assert(state);
3461
3462 ps = state->userdata;
3463
3464 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3465 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3466 return -1;
3467 }
3468
3469 if (pa_streq(state->lvalue, "paths-input")) {
3470 pa_xstrfreev(m->input_path_names);
3471 m->input_path_names = pa_split_spaces_strv(state->rvalue);
3472 } else {
3473 pa_xstrfreev(m->output_path_names);
3474 m->output_path_names = pa_split_spaces_strv(state->rvalue);
3475 }
3476
3477 return 0;
3478 }
3479
3480 static int mapping_parse_element(pa_config_parser_state *state) {
3481 pa_alsa_profile_set *ps;
3482 pa_alsa_mapping *m;
3483
3484 pa_assert(state);
3485
3486 ps = state->userdata;
3487
3488 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3489 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3490 return -1;
3491 }
3492
3493 if (pa_streq(state->lvalue, "element-input")) {
3494 pa_xstrfreev(m->input_element);
3495 m->input_element = pa_split_spaces_strv(state->rvalue);
3496 } else {
3497 pa_xstrfreev(m->output_element);
3498 m->output_element = pa_split_spaces_strv(state->rvalue);
3499 }
3500
3501 return 0;
3502 }
3503
3504 static int mapping_parse_direction(pa_config_parser_state *state) {
3505 pa_alsa_profile_set *ps;
3506 pa_alsa_mapping *m;
3507
3508 pa_assert(state);
3509
3510 ps = state->userdata;
3511
3512 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3513 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3514 return -1;
3515 }
3516
3517 if (pa_streq(state->rvalue, "input"))
3518 m->direction = PA_ALSA_DIRECTION_INPUT;
3519 else if (pa_streq(state->rvalue, "output"))
3520 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3521 else if (pa_streq(state->rvalue, "any"))
3522 m->direction = PA_ALSA_DIRECTION_ANY;
3523 else {
3524 pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
3525 return -1;
3526 }
3527
3528 return 0;
3529 }
3530
3531 static int mapping_parse_description(pa_config_parser_state *state) {
3532 pa_alsa_profile_set *ps;
3533 pa_alsa_profile *p;
3534 pa_alsa_mapping *m;
3535
3536 pa_assert(state);
3537
3538 ps = state->userdata;
3539
3540 if ((m = pa_alsa_mapping_get(ps, state->section))) {
3541 pa_xfree(m->description);
3542 m->description = pa_xstrdup(state->rvalue);
3543 } else if ((p = profile_get(ps, state->section))) {
3544 pa_xfree(p->description);
3545 p->description = pa_xstrdup(state->rvalue);
3546 } else {
3547 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3548 return -1;
3549 }
3550
3551 return 0;
3552 }
3553
3554 static int mapping_parse_priority(pa_config_parser_state *state) {
3555 pa_alsa_profile_set *ps;
3556 pa_alsa_profile *p;
3557 pa_alsa_mapping *m;
3558 uint32_t prio;
3559
3560 pa_assert(state);
3561
3562 ps = state->userdata;
3563
3564 if (pa_atou(state->rvalue, &prio) < 0) {
3565 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
3566 return -1;
3567 }
3568
3569 if ((m = pa_alsa_mapping_get(ps, state->section)))
3570 m->priority = prio;
3571 else if ((p = profile_get(ps, state->section)))
3572 p->priority = prio;
3573 else {
3574 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3575 return -1;
3576 }
3577
3578 return 0;
3579 }
3580
3581 static int profile_parse_mappings(pa_config_parser_state *state) {
3582 pa_alsa_profile_set *ps;
3583 pa_alsa_profile *p;
3584
3585 pa_assert(state);
3586
3587 ps = state->userdata;
3588
3589 if (!(p = profile_get(ps, state->section))) {
3590 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3591 return -1;
3592 }
3593
3594 if (pa_streq(state->lvalue, "input-mappings")) {
3595 pa_xstrfreev(p->input_mapping_names);
3596 p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
3597 } else {
3598 pa_xstrfreev(p->output_mapping_names);
3599 p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
3600 }
3601
3602 return 0;
3603 }
3604
3605 static int profile_parse_skip_probe(pa_config_parser_state *state) {
3606 pa_alsa_profile_set *ps;
3607 pa_alsa_profile *p;
3608 int b;
3609
3610 pa_assert(state);
3611
3612 ps = state->userdata;
3613
3614 if (!(p = profile_get(ps, state->section))) {
3615 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3616 return -1;
3617 }
3618
3619 if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3620 pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
3621 return -1;
3622 }
3623
3624 p->supported = b;
3625
3626 return 0;
3627 }
3628
3629 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
3630 pa_alsa_profile_set *ps;
3631 pa_alsa_decibel_fix *db_fix;
3632 char **items;
3633 char *item;
3634 long *db_values;
3635 unsigned n = 8; /* Current size of the db_values table. */
3636 unsigned min_step = 0;
3637 unsigned max_step = 0;
3638 unsigned i = 0; /* Index to the items table. */
3639 unsigned prev_step = 0;
3640 double prev_db = 0;
3641
3642 pa_assert(state);
3643
3644 ps = state->userdata;
3645
3646 if (!(db_fix = decibel_fix_get(ps, state->section))) {
3647 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3648 return -1;
3649 }
3650
3651 if (!(items = pa_split_spaces_strv(state->rvalue))) {
3652 pa_log("[%s:%u] Value missing", state->filename, state->lineno);
3653 return -1;
3654 }
3655
3656 db_values = pa_xnew(long, n);
3657
3658 while ((item = items[i++])) {
3659 char *s = item; /* Step value string. */
3660 char *d = item; /* dB value string. */
3661 uint32_t step;
3662 double db;
3663
3664 /* Move d forward until it points to a colon or to the end of the item. */
3665 for (; *d && *d != ':'; ++d);
3666
3667 if (d == s) {
3668 /* item started with colon. */
3669 pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
3670 goto fail;
3671 }
3672
3673 if (!*d || !*(d + 1)) {
3674 /* No colon found, or it was the last character in item. */
3675 pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
3676 goto fail;
3677 }
3678
3679 /* pa_atou() needs a null-terminating string. Let's replace the colon
3680 * with a zero byte. */
3681 *d++ = '\0';
3682
3683 if (pa_atou(s, &step) < 0) {
3684 pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
3685 goto fail;
3686 }
3687
3688 if (pa_atod(d, &db) < 0) {
3689 pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
3690 goto fail;
3691 }
3692
3693 if (step <= prev_step && i != 1) {
3694 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
3695 goto fail;
3696 }
3697
3698 if (db < prev_db && i != 1) {
3699 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
3700 goto fail;
3701 }
3702
3703 if (i == 1) {
3704 min_step = step;
3705 db_values[0] = (long) (db * 100.0);
3706 prev_step = step;
3707 prev_db = db;
3708 } else {
3709 /* Interpolate linearly. */
3710 double db_increment = (db - prev_db) / (step - prev_step);
3711
3712 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3713
3714 /* Reallocate the db_values table if it's about to overflow. */
3715 if (prev_step + 1 - min_step == n) {
3716 n *= 2;
3717 db_values = pa_xrenew(long, db_values, n);
3718 }
3719
3720 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3721 }
3722 }
3723
3724 max_step = step;
3725 }
3726
3727 db_fix->min_step = min_step;
3728 db_fix->max_step = max_step;
3729 pa_xfree(db_fix->db_values);
3730 db_fix->db_values = db_values;
3731
3732 pa_xstrfreev(items);
3733
3734 return 0;
3735
3736 fail:
3737 pa_xstrfreev(items);
3738 pa_xfree(db_values);
3739
3740 return -1;
3741 }
3742
3743 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3744 pa_alsa_direction_t direction) {
3745
3746 pa_alsa_path *p;
3747 void *state;
3748 snd_pcm_t *pcm_handle;
3749 pa_alsa_path_set *ps;
3750 snd_mixer_t *mixer_handle;
3751 snd_hctl_t *hctl_handle;
3752
3753 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3754 if (m->output_path_set)
3755 return; /* Already probed */
3756 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3757 pcm_handle = m->output_pcm;
3758 } else {
3759 if (m->input_path_set)
3760 return; /* Already probed */
3761 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3762 pcm_handle = m->input_pcm;
3763 }
3764
3765 if (!ps)
3766 return; /* No paths */
3767
3768 pa_assert(pcm_handle);
3769
3770 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3771 if (!mixer_handle || !hctl_handle) {
3772 /* Cannot open mixer, remove all entries */
3773 while (pa_hashmap_steal_first(ps->paths));
3774 return;
3775 }
3776
3777
3778 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3779 if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3780 pa_hashmap_remove(ps->paths, p);
3781 }
3782 }
3783
3784 path_set_condense(ps, mixer_handle);
3785 path_set_make_paths_unique(ps);
3786
3787 if (mixer_handle)
3788 snd_mixer_close(mixer_handle);
3789
3790 pa_log_debug("Available mixer paths (after tidying):");
3791 pa_alsa_path_set_dump(ps);
3792 }
3793
3794 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3795
3796 static const struct description_map well_known_descriptions[] = {
3797 { "analog-mono", N_("Analog Mono") },
3798 { "analog-stereo", N_("Analog Stereo") },
3799 { "analog-surround-21", N_("Analog Surround 2.1") },
3800 { "analog-surround-30", N_("Analog Surround 3.0") },
3801 { "analog-surround-31", N_("Analog Surround 3.1") },
3802 { "analog-surround-40", N_("Analog Surround 4.0") },
3803 { "analog-surround-41", N_("Analog Surround 4.1") },
3804 { "analog-surround-50", N_("Analog Surround 5.0") },
3805 { "analog-surround-51", N_("Analog Surround 5.1") },
3806 { "analog-surround-61", N_("Analog Surround 6.0") },
3807 { "analog-surround-61", N_("Analog Surround 6.1") },
3808 { "analog-surround-70", N_("Analog Surround 7.0") },
3809 { "analog-surround-71", N_("Analog Surround 7.1") },
3810 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3811 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3812 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3813 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3814 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3815 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3816 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3817 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3818 };
3819
3820 pa_assert(m);
3821
3822 if (!pa_channel_map_valid(&m->channel_map)) {
3823 pa_log("Mapping %s is missing channel map.", m->name);
3824 return -1;
3825 }
3826
3827 if (!m->device_strings) {
3828 pa_log("Mapping %s is missing device strings.", m->name);
3829 return -1;
3830 }
3831
3832 if ((m->input_path_names && m->input_element) ||
3833 (m->output_path_names && m->output_element)) {
3834 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3835 return -1;
3836 }
3837
3838 if (!m->description)
3839 m->description = pa_xstrdup(lookup_description(m->name,
3840 well_known_descriptions,
3841 PA_ELEMENTSOF(well_known_descriptions)));
3842
3843 if (!m->description)
3844 m->description = pa_xstrdup(m->name);
3845
3846 if (bonus) {
3847 if (pa_channel_map_equal(&m->channel_map, bonus))
3848 m->priority += 50;
3849 else if (m->channel_map.channels == bonus->channels)
3850 m->priority += 30;
3851 }
3852
3853 return 0;
3854 }
3855
3856 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3857 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3858
3859 pa_assert(m);
3860
3861 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3862 m->name,
3863 pa_strnull(m->description),
3864 m->priority,
3865 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3866 pa_yes_no(m->supported),
3867 m->direction);
3868 }
3869
3870 static void profile_set_add_auto_pair(
3871 pa_alsa_profile_set *ps,
3872 pa_alsa_mapping *m, /* output */
3873 pa_alsa_mapping *n /* input */) {
3874
3875 char *name;
3876 pa_alsa_profile *p;
3877
3878 pa_assert(ps);
3879 pa_assert(m || n);
3880
3881 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3882 return;
3883
3884 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3885 return;
3886
3887 if (m && n)
3888 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3889 else if (m)
3890 name = pa_sprintf_malloc("output:%s", m->name);
3891 else
3892 name = pa_sprintf_malloc("input:%s", n->name);
3893
3894 if (pa_hashmap_get(ps->profiles, name)) {
3895 pa_xfree(name);
3896 return;
3897 }
3898
3899 p = pa_xnew0(pa_alsa_profile, 1);
3900 p->profile_set = ps;
3901 p->name = name;
3902
3903 if (m) {
3904 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3905 pa_idxset_put(p->output_mappings, m, NULL);
3906 p->priority += m->priority * 100;
3907 }
3908
3909 if (n) {
3910 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3911 pa_idxset_put(p->input_mappings, n, NULL);
3912 p->priority += n->priority;
3913 }
3914
3915 pa_hashmap_put(ps->profiles, p->name, p);
3916 }
3917
3918 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3919 pa_alsa_mapping *m, *n;
3920 void *m_state, *n_state;
3921
3922 pa_assert(ps);
3923
3924 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3925 profile_set_add_auto_pair(ps, m, NULL);
3926
3927 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3928 profile_set_add_auto_pair(ps, m, n);
3929 }
3930
3931 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3932 profile_set_add_auto_pair(ps, NULL, n);
3933 }
3934
3935 static int profile_verify(pa_alsa_profile *p) {
3936
3937 static const struct description_map well_known_descriptions[] = {
3938 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3939 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3940 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3941 { "off", N_("Off") }
3942 };
3943
3944 pa_assert(p);
3945
3946 /* Replace the output mapping names by the actual mappings */
3947 if (p->output_mapping_names) {
3948 char **name;
3949
3950 pa_assert(!p->output_mappings);
3951 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3952
3953 for (name = p->output_mapping_names; *name; name++) {
3954 pa_alsa_mapping *m;
3955 char **in;
3956 pa_bool_t duplicate = FALSE;
3957
3958 for (in = name + 1; *in; in++)
3959 if (pa_streq(*name, *in)) {
3960 duplicate = TRUE;
3961 break;
3962 }
3963
3964 if (duplicate)
3965 continue;
3966
3967 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3968 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3969 return -1;
3970 }
3971
3972 pa_idxset_put(p->output_mappings, m, NULL);
3973
3974 if (p->supported)
3975 m->supported++;
3976 }
3977
3978 pa_xstrfreev(p->output_mapping_names);
3979 p->output_mapping_names = NULL;
3980 }
3981
3982 /* Replace the input mapping names by the actual mappings */
3983 if (p->input_mapping_names) {
3984 char **name;
3985
3986 pa_assert(!p->input_mappings);
3987 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3988
3989 for (name = p->input_mapping_names; *name; name++) {
3990 pa_alsa_mapping *m;
3991 char **in;
3992 pa_bool_t duplicate = FALSE;
3993
3994 for (in = name + 1; *in; in++)
3995 if (pa_streq(*name, *in)) {
3996 duplicate = TRUE;
3997 break;
3998 }
3999
4000 if (duplicate)
4001 continue;
4002
4003 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4004 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4005 return -1;
4006 }
4007
4008 pa_idxset_put(p->input_mappings, m, NULL);
4009
4010 if (p->supported)
4011 m->supported++;
4012 }
4013
4014 pa_xstrfreev(p->input_mapping_names);
4015 p->input_mapping_names = NULL;
4016 }
4017
4018 if (!p->input_mappings && !p->output_mappings) {
4019 pa_log("Profile '%s' lacks mappings.", p->name);
4020 return -1;
4021 }
4022
4023 if (!p->description)
4024 p->description = pa_xstrdup(lookup_description(p->name,
4025 well_known_descriptions,
4026 PA_ELEMENTSOF(well_known_descriptions)));
4027
4028 if (!p->description) {
4029 pa_strbuf *sb;
4030 uint32_t idx;
4031 pa_alsa_mapping *m;
4032
4033 sb = pa_strbuf_new();
4034
4035 if (p->output_mappings)
4036 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4037 if (!pa_strbuf_isempty(sb))
4038 pa_strbuf_puts(sb, " + ");
4039
4040 pa_strbuf_printf(sb, _("%s Output"), m->description);
4041 }
4042
4043 if (p->input_mappings)
4044 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4045 if (!pa_strbuf_isempty(sb))
4046 pa_strbuf_puts(sb, " + ");
4047
4048 pa_strbuf_printf(sb, _("%s Input"), m->description);
4049 }
4050
4051 p->description = pa_strbuf_tostring_free(sb);
4052 }
4053
4054 return 0;
4055 }
4056
4057 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4058 uint32_t idx;
4059 pa_alsa_mapping *m;
4060 pa_assert(p);
4061
4062 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4063 p->name,
4064 pa_strnull(p->description),
4065 p->priority,
4066 pa_yes_no(p->supported),
4067 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4068 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4069
4070 if (p->input_mappings)
4071 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4072 pa_log_debug("Input %s", m->name);
4073
4074 if (p->output_mappings)
4075 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4076 pa_log_debug("Output %s", m->name);
4077 }
4078
4079 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4080 pa_assert(db_fix);
4081
4082 /* Check that the dB mapping has been configured. Since "db-values" is
4083 * currently the only option in the DecibelFix section, and decibel fix
4084 * objects don't get created if a DecibelFix section is empty, this is
4085 * actually a redundant check. Having this may prevent future bugs,
4086 * however. */
4087 if (!db_fix->db_values) {
4088 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4089 return -1;
4090 }
4091
4092 return 0;
4093 }
4094
4095 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4096 char *db_values = NULL;
4097
4098 pa_assert(db_fix);
4099
4100 if (db_fix->db_values) {
4101 pa_strbuf *buf;
4102 unsigned long i, nsteps;
4103
4104 pa_assert(db_fix->min_step <= db_fix->max_step);
4105 nsteps = db_fix->max_step - db_fix->min_step + 1;
4106
4107 buf = pa_strbuf_new();
4108 for (i = 0; i < nsteps; ++i)
4109 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4110
4111 db_values = pa_strbuf_tostring_free(buf);
4112 }
4113
4114 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4115 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4116
4117 pa_xfree(db_values);
4118 }
4119
4120 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4121 pa_alsa_profile_set *ps;
4122 pa_alsa_profile *p;
4123 pa_alsa_mapping *m;
4124 pa_alsa_decibel_fix *db_fix;
4125 char *fn;
4126 int r;
4127 void *state;
4128
4129 static pa_config_item items[] = {
4130 /* [General] */
4131 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4132
4133 /* [Mapping ...] */
4134 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4135 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4136 { "paths-input", mapping_parse_paths, NULL, NULL },
4137 { "paths-output", mapping_parse_paths, NULL, NULL },
4138 { "element-input", mapping_parse_element, NULL, NULL },
4139 { "element-output", mapping_parse_element, NULL, NULL },
4140 { "direction", mapping_parse_direction, NULL, NULL },
4141
4142 /* Shared by [Mapping ...] and [Profile ...] */
4143 { "description", mapping_parse_description, NULL, NULL },
4144 { "priority", mapping_parse_priority, NULL, NULL },
4145
4146 /* [Profile ...] */
4147 { "input-mappings", profile_parse_mappings, NULL, NULL },
4148 { "output-mappings", profile_parse_mappings, NULL, NULL },
4149 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4150
4151 /* [DecibelFix ...] */
4152 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4153 { NULL, NULL, NULL, NULL }
4154 };
4155
4156 ps = pa_xnew0(pa_alsa_profile_set, 1);
4157 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4158 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4159 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4160 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4161 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4162
4163 items[0].data = &ps->auto_profiles;
4164
4165 if (!fname)
4166 fname = "default.conf";
4167
4168 fn = pa_maybe_prefix_path(fname,
4169 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4170 PA_ALSA_PROFILE_SETS_DIR);
4171
4172 r = pa_config_parse(fn, NULL, items, NULL, ps);
4173 pa_xfree(fn);
4174
4175 if (r < 0)
4176 goto fail;
4177
4178 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4179 if (mapping_verify(m, bonus) < 0)
4180 goto fail;
4181
4182 if (ps->auto_profiles)
4183 profile_set_add_auto(ps);
4184
4185 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4186 if (profile_verify(p) < 0)
4187 goto fail;
4188
4189 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4190 if (decibel_fix_verify(db_fix) < 0)
4191 goto fail;
4192
4193 return ps;
4194
4195 fail:
4196 pa_alsa_profile_set_free(ps);
4197 return NULL;
4198 }
4199
4200 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4201 pa_alsa_mapping *m;
4202 uint32_t idx;
4203
4204 if (!to_be_finalized)
4205 return;
4206
4207 if (to_be_finalized->output_mappings)
4208 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4209
4210 if (!m->output_pcm)
4211 continue;
4212
4213 if (to_be_finalized->supported)
4214 m->supported++;
4215
4216 /* If this mapping is also in the next profile, we won't close the
4217 * pcm handle here, because it would get immediately reopened
4218 * anyway. */
4219 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4220 continue;
4221
4222 snd_pcm_close(m->output_pcm);
4223 m->output_pcm = NULL;
4224 }
4225
4226 if (to_be_finalized->input_mappings)
4227 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4228
4229 if (!m->input_pcm)
4230 continue;
4231
4232 if (to_be_finalized->supported)
4233 m->supported++;
4234
4235 /* If this mapping is also in the next profile, we won't close the
4236 * pcm handle here, because it would get immediately reopened
4237 * anyway. */
4238 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4239 continue;
4240
4241 snd_pcm_close(m->input_pcm);
4242 m->input_pcm = NULL;
4243 }
4244 }
4245
4246 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4247 const pa_sample_spec *ss,
4248 const char *dev_id,
4249 int mode,
4250 unsigned default_n_fragments,
4251 unsigned default_fragment_size_msec) {
4252
4253 pa_sample_spec try_ss = *ss;
4254 pa_channel_map try_map = m->channel_map;
4255 snd_pcm_uframes_t try_period_size, try_buffer_size;
4256
4257 try_ss.channels = try_map.channels;
4258
4259 try_period_size =
4260 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4261 pa_frame_size(&try_ss);
4262 try_buffer_size = default_n_fragments * try_period_size;
4263
4264 return pa_alsa_open_by_template(
4265 m->device_strings, dev_id, NULL, &try_ss,
4266 &try_map, mode, &try_period_size,
4267 &try_buffer_size, 0, NULL, NULL, TRUE);
4268 }
4269
4270 static void paths_drop_unsupported(pa_hashmap* h) {
4271
4272 void* state = NULL;
4273 const void* key;
4274 pa_alsa_path* p;
4275
4276 pa_assert(h);
4277 p = pa_hashmap_iterate(h, &state, &key);
4278 while (p) {
4279 if (p->supported <= 0) {
4280 pa_hashmap_remove(h, key);
4281 pa_alsa_path_free(p);
4282 }
4283 p = pa_hashmap_iterate(h, &state, &key);
4284 }
4285 }
4286
4287 void pa_alsa_profile_set_probe(
4288 pa_alsa_profile_set *ps,
4289 const char *dev_id,
4290 const pa_sample_spec *ss,
4291 unsigned default_n_fragments,
4292 unsigned default_fragment_size_msec) {
4293
4294 void *state;
4295 pa_alsa_profile *p, *last = NULL;
4296 pa_alsa_mapping *m;
4297
4298 pa_assert(ps);
4299 pa_assert(dev_id);
4300 pa_assert(ss);
4301
4302 if (ps->probed)
4303 return;
4304
4305 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4306 uint32_t idx;
4307
4308 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4309 if (!p->supported) {
4310
4311 pa_log_debug("Looking at profile %s", p->name);
4312 profile_finalize_probing(last, p);
4313 p->supported = TRUE;
4314
4315 /* Check if we can open all new ones */
4316 if (p->output_mappings)
4317 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4318
4319 if (m->output_pcm)
4320 continue;
4321
4322 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4323 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4324 SND_PCM_STREAM_PLAYBACK,
4325 default_n_fragments,
4326 default_fragment_size_msec))) {
4327 p->supported = FALSE;
4328 break;
4329 }
4330 }
4331
4332 if (p->input_mappings && p->supported)
4333 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4334
4335 if (m->input_pcm)
4336 continue;
4337
4338 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4339 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4340 SND_PCM_STREAM_CAPTURE,
4341 default_n_fragments,
4342 default_fragment_size_msec))) {
4343 p->supported = FALSE;
4344 break;
4345 }
4346 }
4347
4348 last = p;
4349
4350 if (!p->supported)
4351 continue;
4352 }
4353
4354 pa_log_debug("Profile %s supported.", p->name);
4355
4356 if (p->output_mappings)
4357 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4358 if (m->output_pcm)
4359 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4360
4361 if (p->input_mappings)
4362 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4363 if (m->input_pcm)
4364 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4365 }
4366
4367 /* Clean up */
4368 profile_finalize_probing(last, NULL);
4369
4370 pa_alsa_profile_set_drop_unsupported(ps);
4371
4372 paths_drop_unsupported(ps->input_paths);
4373 paths_drop_unsupported(ps->output_paths);
4374
4375 ps->probed = TRUE;
4376 }
4377
4378 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4379 pa_alsa_profile *p;
4380 pa_alsa_mapping *m;
4381 pa_alsa_decibel_fix *db_fix;
4382 void *state;
4383
4384 pa_assert(ps);
4385
4386 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4387 (void*)
4388 ps,
4389 pa_yes_no(ps->auto_profiles),
4390 pa_yes_no(ps->probed),
4391 pa_hashmap_size(ps->mappings),
4392 pa_hashmap_size(ps->profiles),
4393 pa_hashmap_size(ps->decibel_fixes));
4394
4395 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4396 pa_alsa_mapping_dump(m);
4397
4398 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4399 pa_alsa_profile_dump(p);
4400
4401 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4402 pa_alsa_decibel_fix_dump(db_fix);
4403 }
4404
4405 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
4406 pa_alsa_profile *p;
4407 pa_alsa_mapping *m;
4408 void *state;
4409
4410 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4411 if (!p->supported) {
4412 pa_hashmap_remove(ps->profiles, p->name);
4413 profile_free(p);
4414 }
4415 }
4416
4417 PA_HASHMAP_FOREACH(m, ps->mappings, state) {
4418 if (m->supported <= 0) {
4419 pa_hashmap_remove(ps->mappings, m->name);
4420 mapping_free(m);
4421 }
4422 }
4423 }
4424
4425 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4426 const char* name,
4427 const char* description,
4428 pa_alsa_path *path,
4429 pa_alsa_setting *setting,
4430 pa_card_profile *cp,
4431 pa_hashmap *extra,
4432 pa_core *core) {
4433
4434 pa_device_port *p;
4435
4436 pa_assert(path);
4437
4438 p = pa_hashmap_get(ports, name);
4439
4440 if (!p) {
4441 pa_alsa_port_data *data;
4442
4443 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4444 pa_assert(p);
4445 pa_hashmap_put(ports, p->name, p);
4446 pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
4447
4448 data = PA_DEVICE_PORT_DATA(p);
4449 data->path = path;
4450 data->setting = setting;
4451 path->port = p;
4452 }
4453
4454 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4455 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4456
4457 if (cp)
4458 pa_hashmap_put(p->profiles, cp->name, cp);
4459
4460 if (extra) {
4461 pa_hashmap_put(extra, p->name, p);
4462 pa_device_port_ref(p);
4463 }
4464
4465 return p;
4466 }
4467
4468 void pa_alsa_path_set_add_ports(
4469 pa_alsa_path_set *ps,
4470 pa_card_profile *cp,
4471 pa_hashmap *ports,
4472 pa_hashmap *extra,
4473 pa_core *core) {
4474
4475 pa_alsa_path *path;
4476 void *state;
4477
4478 pa_assert(ports);
4479
4480 if (!ps)
4481 return;
4482
4483 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4484 if (!path->settings || !path->settings->next) {
4485 /* If there is no or just one setting we only need a
4486 * single entry */
4487 pa_device_port *port = device_port_alsa_init(ports, path->name,
4488 path->description, path, path->settings, cp, extra, core);
4489 port->priority = path->priority * 100;
4490
4491 } else {
4492 pa_alsa_setting *s;
4493 PA_LLIST_FOREACH(s, path->settings) {
4494 pa_device_port *port;
4495 char *n, *d;
4496
4497 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4498
4499 if (s->description[0])
4500 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4501 else
4502 d = pa_xstrdup(path->description);
4503
4504 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4505 port->priority = path->priority * 100 + s->priority;
4506
4507 pa_xfree(n);
4508 pa_xfree(d);
4509 }
4510 }
4511 }
4512 }
4513
4514 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
4515 pa_hashmap *ports;
4516
4517 pa_assert(sink_or_source_new_data);
4518 pa_assert(ps);
4519
4520 if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
4521 ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
4522 else
4523 ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
4524
4525 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4526 pa_assert(card);
4527 pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
4528 }
4529
4530 pa_log_debug("Added %u ports", pa_hashmap_size(ports));
4531 }