]> code.delx.au - pulseaudio/blob - src/modules/alsa/alsa-mixer.c
alsa-mixer: Add path for Headset Microphone
[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);
453
454 pa_xfree(s->name);
455 pa_xfree(s->description);
456 pa_xfree(s);
457 }
458
459 static void option_free(pa_alsa_option *o) {
460 pa_assert(o);
461
462 pa_xfree(o->alsa_name);
463 pa_xfree(o->name);
464 pa_xfree(o->description);
465 pa_xfree(o);
466 }
467
468 static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
469 pa_assert(db_fix);
470
471 pa_xfree(db_fix->name);
472 pa_xfree(db_fix->db_values);
473
474 pa_xfree(db_fix);
475 }
476
477 static void jack_free(pa_alsa_jack *j) {
478 pa_assert(j);
479
480 pa_xfree(j->alsa_name);
481 pa_xfree(j->name);
482 pa_xfree(j);
483 }
484
485 static void element_free(pa_alsa_element *e) {
486 pa_alsa_option *o;
487 pa_assert(e);
488
489 while ((o = e->options)) {
490 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
491 option_free(o);
492 }
493
494 if (e->db_fix)
495 decibel_fix_free(e->db_fix);
496
497 pa_xfree(e->alsa_name);
498 pa_xfree(e);
499 }
500
501 void pa_alsa_path_free(pa_alsa_path *p) {
502 pa_alsa_jack *j;
503 pa_alsa_element *e;
504 pa_alsa_setting *s;
505
506 pa_assert(p);
507
508 while ((j = p->jacks)) {
509 PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
510 jack_free(j);
511 }
512
513 while ((e = p->elements)) {
514 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
515 element_free(e);
516 }
517
518 while ((s = p->settings)) {
519 PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
520 setting_free(s);
521 }
522
523 pa_proplist_free(p->proplist);
524 pa_xfree(p->name);
525 pa_xfree(p->description);
526 pa_xfree(p);
527 }
528
529 void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
530 pa_assert(ps);
531
532 if (ps->paths)
533 pa_hashmap_free(ps->paths, NULL);
534
535 pa_xfree(ps);
536 }
537
538 static long to_alsa_dB(pa_volume_t v) {
539 return (long) (pa_sw_volume_to_dB(v) * 100.0);
540 }
541
542 static pa_volume_t from_alsa_dB(long v) {
543 return pa_sw_volume_from_dB((double) v / 100.0);
544 }
545
546 static long to_alsa_volume(pa_volume_t v, long min, long max) {
547 long w;
548
549 w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
550 return PA_CLAMP_UNLIKELY(w, min, max);
551 }
552
553 static pa_volume_t from_alsa_volume(long v, long min, long max) {
554 return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
555 }
556
557 #define SELEM_INIT(sid, name) \
558 do { \
559 snd_mixer_selem_id_alloca(&(sid)); \
560 snd_mixer_selem_id_set_name((sid), (name)); \
561 snd_mixer_selem_id_set_index((sid), 0); \
562 } while(FALSE)
563
564 static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
565 snd_mixer_selem_id_t *sid;
566 snd_mixer_elem_t *me;
567 snd_mixer_selem_channel_id_t c;
568 pa_channel_position_mask_t mask = 0;
569 unsigned k;
570
571 pa_assert(m);
572 pa_assert(e);
573 pa_assert(cm);
574 pa_assert(v);
575
576 SELEM_INIT(sid, e->alsa_name);
577 if (!(me = snd_mixer_find_selem(m, sid))) {
578 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
579 return -1;
580 }
581
582 pa_cvolume_mute(v, cm->channels);
583
584 /* We take the highest volume of all channels that match */
585
586 for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
587 int r;
588 pa_volume_t f;
589
590 if (e->has_dB) {
591 long value = 0;
592
593 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
594 if (snd_mixer_selem_has_playback_channel(me, c)) {
595 if (e->db_fix) {
596 if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
597 /* If the channel volume is outside the limits set
598 * by the dB fix, we clamp the hw volume to be
599 * within the limits. */
600 if (value < e->db_fix->min_step) {
601 value = e->db_fix->min_step;
602 snd_mixer_selem_set_playback_volume(me, c, value);
603 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
604 "Volume reset to %0.2f dB.", e->alsa_name, c,
605 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
606 } else if (value > e->db_fix->max_step) {
607 value = e->db_fix->max_step;
608 snd_mixer_selem_set_playback_volume(me, c, value);
609 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
610 "Volume reset to %0.2f dB.", e->alsa_name, c,
611 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
612 }
613
614 /* Volume step -> dB value conversion. */
615 value = e->db_fix->db_values[value - e->db_fix->min_step];
616 }
617 } else
618 r = snd_mixer_selem_get_playback_dB(me, c, &value);
619 } else
620 r = -1;
621 } else {
622 if (snd_mixer_selem_has_capture_channel(me, c)) {
623 if (e->db_fix) {
624 if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
625 /* If the channel volume is outside the limits set
626 * by the dB fix, we clamp the hw volume to be
627 * within the limits. */
628 if (value < e->db_fix->min_step) {
629 value = e->db_fix->min_step;
630 snd_mixer_selem_set_capture_volume(me, c, value);
631 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
632 "Volume reset to %0.2f dB.", e->alsa_name, c,
633 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
634 } else if (value > e->db_fix->max_step) {
635 value = e->db_fix->max_step;
636 snd_mixer_selem_set_capture_volume(me, c, value);
637 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
638 "Volume reset to %0.2f dB.", e->alsa_name, c,
639 e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
640 }
641
642 /* Volume step -> dB value conversion. */
643 value = e->db_fix->db_values[value - e->db_fix->min_step];
644 }
645 } else
646 r = snd_mixer_selem_get_capture_dB(me, c, &value);
647 } else
648 r = -1;
649 }
650
651 if (r < 0)
652 continue;
653
654 #ifdef HAVE_VALGRIND_MEMCHECK_H
655 VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
656 #endif
657
658 f = from_alsa_dB(value);
659
660 } else {
661 long value = 0;
662
663 if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
664 if (snd_mixer_selem_has_playback_channel(me, c))
665 r = snd_mixer_selem_get_playback_volume(me, c, &value);
666 else
667 r = -1;
668 } else {
669 if (snd_mixer_selem_has_capture_channel(me, c))
670 r = snd_mixer_selem_get_capture_volume(me, c, &value);
671 else
672 r = -1;
673 }
674
675 if (r < 0)
676 continue;
677
678 f = from_alsa_volume(value, e->min_volume, e->max_volume);
679 }
680
681 for (k = 0; k < cm->channels; k++)
682 if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
683 if (v->values[k] < f)
684 v->values[k] = f;
685
686 mask |= e->masks[c][e->n_channels-1];
687 }
688
689 for (k = 0; k < cm->channels; k++)
690 if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
691 v->values[k] = PA_VOLUME_NORM;
692
693 return 0;
694 }
695
696 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
697 pa_alsa_element *e;
698
699 pa_assert(m);
700 pa_assert(p);
701 pa_assert(cm);
702 pa_assert(v);
703
704 if (!p->has_volume)
705 return -1;
706
707 pa_cvolume_reset(v, cm->channels);
708
709 PA_LLIST_FOREACH(e, p->elements) {
710 pa_cvolume ev;
711
712 if (e->volume_use != PA_ALSA_VOLUME_MERGE)
713 continue;
714
715 pa_assert(!p->has_dB || e->has_dB);
716
717 if (element_get_volume(e, m, cm, &ev) < 0)
718 return -1;
719
720 /* If we have no dB information all we can do is take the first element and leave */
721 if (!p->has_dB) {
722 *v = ev;
723 return 0;
724 }
725
726 pa_sw_cvolume_multiply(v, v, &ev);
727 }
728
729 return 0;
730 }
731
732 static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, 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_AVAILABLE_NO;
1757 j->state_plugged = PA_AVAILABLE_YES;
1758 j->path = p;
1759 j->name = pa_xstrdup(section);
1760 j->alsa_name = pa_sprintf_malloc("%s Jack", section);
1761 PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
1762
1763 finish:
1764 p->last_jack = j;
1765 return j;
1766 }
1767
1768
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_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_AVAILABLE_YES;
2183 else if (pa_streq(state->rvalue, "no"))
2184 pa = PA_AVAILABLE_NO;
2185 else if (pa_streq(state->rvalue, "unknown"))
2186 pa = PA_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-microphone-headset", N_("Headset Microphone") },
2340 { "analog-input-linein", N_("Line In") },
2341 { "analog-input-radio", N_("Radio") },
2342 { "analog-input-video", N_("Video") },
2343 { "analog-output", N_("Analog Output") },
2344 { "analog-output-headphones", N_("Headphones") },
2345 { "analog-output-lfe-on-mono", N_("LFE on Separate Mono Output") },
2346 { "analog-output-lineout", N_("Line Out") },
2347 { "analog-output-mono", N_("Analog Mono Output") },
2348 { "analog-output-speaker", N_("Speakers") },
2349 { "hdmi-output", N_("HDMI / DisplayPort") },
2350 { "iec958-stereo-output", N_("Digital Output (S/PDIF)") },
2351 { "iec958-stereo-input", N_("Digital Input (S/PDIF)") },
2352 { "iec958-passthrough-output", N_("Digital Passthrough (S/PDIF)") }
2353 };
2354
2355 pa_alsa_element *e;
2356
2357 pa_assert(p);
2358
2359 PA_LLIST_FOREACH(e, p->elements)
2360 if (element_verify(e) < 0)
2361 return -1;
2362
2363 if (!p->description)
2364 p->description = pa_xstrdup(lookup_description(p->name,
2365 well_known_descriptions,
2366 PA_ELEMENTSOF(well_known_descriptions)));
2367
2368 if (!p->description)
2369 p->description = pa_xstrdup(p->name);
2370
2371 return 0;
2372 }
2373
2374 static const char *get_default_paths_dir(void) {
2375 if (pa_run_from_build_tree())
2376 return PA_BUILDDIR "/modules/alsa/mixer/paths/";
2377 else
2378 return PA_ALSA_PATHS_DIR;
2379 }
2380
2381 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2382 pa_alsa_path *p;
2383 char *fn;
2384 int r;
2385 const char *n;
2386 bool mute_during_activation = false;
2387
2388 pa_config_item items[] = {
2389 /* [General] */
2390 { "priority", pa_config_parse_unsigned, NULL, "General" },
2391 { "description", pa_config_parse_string, NULL, "General" },
2392 { "name", pa_config_parse_string, NULL, "General" },
2393 { "mute-during-activation", pa_config_parse_bool, NULL, "General" },
2394 { "eld-device", pa_config_parse_int, NULL, "General" },
2395
2396 /* [Option ...] */
2397 { "priority", option_parse_priority, NULL, NULL },
2398 { "name", option_parse_name, NULL, NULL },
2399
2400 /* [Jack ...] */
2401 { "state.plugged", jack_parse_state, NULL, NULL },
2402 { "state.unplugged", jack_parse_state, NULL, NULL },
2403
2404 /* [Element ...] */
2405 { "switch", element_parse_switch, NULL, NULL },
2406 { "volume", element_parse_volume, NULL, NULL },
2407 { "enumeration", element_parse_enumeration, NULL, NULL },
2408 { "override-map.1", element_parse_override_map, NULL, NULL },
2409 { "override-map.2", element_parse_override_map, NULL, NULL },
2410 /* ... later on we might add override-map.3 and so on here ... */
2411 { "required", element_parse_required, NULL, NULL },
2412 { "required-any", element_parse_required, NULL, NULL },
2413 { "required-absent", element_parse_required, NULL, NULL },
2414 { "direction", element_parse_direction, NULL, NULL },
2415 { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2416 { "volume-limit", element_parse_volume_limit, NULL, NULL },
2417 { NULL, NULL, NULL, NULL }
2418 };
2419
2420 pa_assert(fname);
2421
2422 p = pa_xnew0(pa_alsa_path, 1);
2423 n = pa_path_get_filename(fname);
2424 p->name = pa_xstrndup(n, strcspn(n, "."));
2425 p->proplist = pa_proplist_new();
2426 p->direction = direction;
2427 p->eld_device = -1;
2428
2429 items[0].data = &p->priority;
2430 items[1].data = &p->description;
2431 items[2].data = &p->name;
2432 items[3].data = &mute_during_activation;
2433 items[4].data = &p->eld_device;
2434
2435 if (!paths_dir)
2436 paths_dir = get_default_paths_dir();
2437
2438 fn = pa_maybe_prefix_path(fname, paths_dir);
2439
2440 r = pa_config_parse(fn, NULL, items, p->proplist, p);
2441 pa_xfree(fn);
2442
2443 if (r < 0)
2444 goto fail;
2445
2446 p->mute_during_activation = mute_during_activation;
2447
2448 if (path_verify(p) < 0)
2449 goto fail;
2450
2451 return p;
2452
2453 fail:
2454 pa_alsa_path_free(p);
2455 return NULL;
2456 }
2457
2458 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
2459 pa_alsa_path *p;
2460 pa_alsa_element *e;
2461
2462 pa_assert(element);
2463
2464 p = pa_xnew0(pa_alsa_path, 1);
2465 p->name = pa_xstrdup(element);
2466 p->direction = direction;
2467
2468 e = pa_xnew0(pa_alsa_element, 1);
2469 e->path = p;
2470 e->alsa_name = pa_xstrdup(element);
2471 e->direction = direction;
2472 e->volume_limit = -1;
2473
2474 e->switch_use = PA_ALSA_SWITCH_MUTE;
2475 e->volume_use = PA_ALSA_VOLUME_MERGE;
2476
2477 PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
2478 p->last_element = e;
2479 return p;
2480 }
2481
2482 static pa_bool_t element_drop_unsupported(pa_alsa_element *e) {
2483 pa_alsa_option *o, *n;
2484
2485 pa_assert(e);
2486
2487 for (o = e->options; o; o = n) {
2488 n = o->next;
2489
2490 if (o->alsa_idx < 0) {
2491 PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
2492 option_free(o);
2493 }
2494 }
2495
2496 return
2497 e->switch_use != PA_ALSA_SWITCH_IGNORE ||
2498 e->volume_use != PA_ALSA_VOLUME_IGNORE ||
2499 e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
2500 }
2501
2502 static void path_drop_unsupported(pa_alsa_path *p) {
2503 pa_alsa_element *e, *n;
2504
2505 pa_assert(p);
2506
2507 for (e = p->elements; e; e = n) {
2508 n = e->next;
2509
2510 if (!element_drop_unsupported(e)) {
2511 PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
2512 element_free(e);
2513 }
2514 }
2515 }
2516
2517 static void path_make_options_unique(pa_alsa_path *p) {
2518 pa_alsa_element *e;
2519 pa_alsa_option *o, *u;
2520
2521 PA_LLIST_FOREACH(e, p->elements) {
2522 PA_LLIST_FOREACH(o, e->options) {
2523 unsigned i;
2524 char *m;
2525
2526 for (u = o->next; u; u = u->next)
2527 if (pa_streq(u->name, o->name))
2528 break;
2529
2530 if (!u)
2531 continue;
2532
2533 m = pa_xstrdup(o->name);
2534
2535 /* OK, this name is not unique, hence let's rename */
2536 for (i = 1, u = o; u; u = u->next) {
2537 char *nn, *nd;
2538
2539 if (!pa_streq(u->name, m))
2540 continue;
2541
2542 nn = pa_sprintf_malloc("%s-%u", m, i);
2543 pa_xfree(u->name);
2544 u->name = nn;
2545
2546 nd = pa_sprintf_malloc("%s %u", u->description, i);
2547 pa_xfree(u->description);
2548 u->description = nd;
2549
2550 i++;
2551 }
2552
2553 pa_xfree(m);
2554 }
2555 }
2556 }
2557
2558 static pa_bool_t element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
2559 pa_alsa_option *o;
2560
2561 for (; e; e = e->next)
2562 if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
2563 e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
2564 break;
2565
2566 if (!e)
2567 return FALSE;
2568
2569 for (o = e->options; o; o = o->next) {
2570 pa_alsa_setting *s;
2571
2572 if (template) {
2573 s = pa_xnewdup(pa_alsa_setting, template, 1);
2574 s->options = pa_idxset_copy(template->options);
2575 s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
2576 s->description =
2577 (template->description[0] && o->description[0])
2578 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
2579 : (template->description[0]
2580 ? pa_xstrdup(template->description)
2581 : pa_xstrdup(o->description));
2582
2583 s->priority = PA_MAX(template->priority, o->priority);
2584 } else {
2585 s = pa_xnew0(pa_alsa_setting, 1);
2586 s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2587 s->name = pa_xstrdup(o->name);
2588 s->description = pa_xstrdup(o->description);
2589 s->priority = o->priority;
2590 }
2591
2592 pa_idxset_put(s->options, o, NULL);
2593
2594 if (element_create_settings(e->next, s))
2595 /* This is not a leaf, so let's get rid of it */
2596 setting_free(s);
2597 else {
2598 /* This is a leaf, so let's add it */
2599 PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
2600
2601 e->path->last_setting = s;
2602 }
2603 }
2604
2605 return TRUE;
2606 }
2607
2608 static void path_create_settings(pa_alsa_path *p) {
2609 pa_assert(p);
2610
2611 element_create_settings(p->elements, NULL);
2612 }
2613
2614 int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, snd_hctl_t *hctl, pa_bool_t ignore_dB) {
2615 pa_alsa_element *e;
2616 pa_alsa_jack *j;
2617 double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
2618 pa_channel_position_t t;
2619 pa_channel_position_mask_t path_volume_channels = 0;
2620
2621 pa_assert(p);
2622 pa_assert(m);
2623
2624 if (p->probed)
2625 return p->supported ? 0 : -1;
2626 p->probed = TRUE;
2627
2628 pa_zero(min_dB);
2629 pa_zero(max_dB);
2630
2631 pa_log_debug("Probing path '%s'", p->name);
2632
2633 PA_LLIST_FOREACH(j, p->jacks) {
2634 if (jack_probe(j, hctl) < 0) {
2635 p->supported = FALSE;
2636 pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
2637 return -1;
2638 }
2639 pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
2640 }
2641
2642 PA_LLIST_FOREACH(e, p->elements) {
2643 if (element_probe(e, m) < 0) {
2644 p->supported = FALSE;
2645 pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
2646 return -1;
2647 }
2648 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);
2649
2650 if (ignore_dB)
2651 e->has_dB = FALSE;
2652
2653 if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
2654
2655 if (!p->has_volume) {
2656 p->min_volume = e->min_volume;
2657 p->max_volume = e->max_volume;
2658 }
2659
2660 if (e->has_dB) {
2661 if (!p->has_volume) {
2662 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2663 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2664 min_dB[t] = e->min_dB;
2665 max_dB[t] = e->max_dB;
2666 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2667 }
2668
2669 p->has_dB = TRUE;
2670 } else {
2671
2672 if (p->has_dB) {
2673 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
2674 if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
2675 min_dB[t] += e->min_dB;
2676 max_dB[t] += e->max_dB;
2677 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
2678 }
2679 } else {
2680 /* Hmm, there's another element before us
2681 * which cannot do dB volumes, so we we need
2682 * to 'neutralize' this slider */
2683 e->volume_use = PA_ALSA_VOLUME_ZERO;
2684 pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
2685 }
2686 }
2687 } else if (p->has_volume) {
2688 /* We can't use this volume, so let's ignore it */
2689 e->volume_use = PA_ALSA_VOLUME_IGNORE;
2690 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
2691 }
2692 p->has_volume = TRUE;
2693 }
2694
2695 if (e->switch_use == PA_ALSA_SWITCH_MUTE)
2696 p->has_mute = TRUE;
2697 }
2698
2699 if (p->has_req_any && !p->req_any_present) {
2700 p->supported = FALSE;
2701 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
2702 return -1;
2703 }
2704
2705 path_drop_unsupported(p);
2706 path_make_options_unique(p);
2707 path_create_settings(p);
2708
2709 p->supported = TRUE;
2710
2711 p->min_dB = INFINITY;
2712 p->max_dB = -INFINITY;
2713
2714 for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
2715 if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
2716 if (p->min_dB > min_dB[t])
2717 p->min_dB = min_dB[t];
2718
2719 if (p->max_dB < max_dB[t])
2720 p->max_dB = max_dB[t];
2721 }
2722 }
2723
2724 return 0;
2725 }
2726
2727 void pa_alsa_setting_dump(pa_alsa_setting *s) {
2728 pa_assert(s);
2729
2730 pa_log_debug("Setting %s (%s) priority=%u",
2731 s->name,
2732 pa_strnull(s->description),
2733 s->priority);
2734 }
2735
2736 void pa_alsa_jack_dump(pa_alsa_jack *j) {
2737 pa_assert(j);
2738
2739 pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
2740 }
2741
2742 void pa_alsa_option_dump(pa_alsa_option *o) {
2743 pa_assert(o);
2744
2745 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2746 o->alsa_name,
2747 pa_strnull(o->name),
2748 pa_strnull(o->description),
2749 o->alsa_idx,
2750 o->priority);
2751 }
2752
2753 void pa_alsa_element_dump(pa_alsa_element *e) {
2754 pa_alsa_option *o;
2755 pa_assert(e);
2756
2757 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",
2758 e->alsa_name,
2759 e->direction,
2760 e->switch_use,
2761 e->volume_use,
2762 e->volume_limit,
2763 e->enumeration_use,
2764 e->required,
2765 e->required_any,
2766 e->required_absent,
2767 (long long unsigned) e->merged_mask,
2768 e->n_channels,
2769 pa_yes_no(e->override_map));
2770
2771 PA_LLIST_FOREACH(o, e->options)
2772 pa_alsa_option_dump(o);
2773 }
2774
2775 void pa_alsa_path_dump(pa_alsa_path *p) {
2776 pa_alsa_element *e;
2777 pa_alsa_jack *j;
2778 pa_alsa_setting *s;
2779 pa_assert(p);
2780
2781 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2782 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2783 p->name,
2784 pa_strnull(p->description),
2785 p->direction,
2786 p->priority,
2787 pa_yes_no(p->probed),
2788 pa_yes_no(p->supported),
2789 pa_yes_no(p->has_mute),
2790 pa_yes_no(p->has_volume),
2791 pa_yes_no(p->has_dB),
2792 p->min_volume, p->max_volume,
2793 p->min_dB, p->max_dB);
2794
2795 PA_LLIST_FOREACH(e, p->elements)
2796 pa_alsa_element_dump(e);
2797
2798 PA_LLIST_FOREACH(j, p->jacks)
2799 pa_alsa_jack_dump(j);
2800
2801 PA_LLIST_FOREACH(s, p->settings)
2802 pa_alsa_setting_dump(s);
2803 }
2804
2805 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2806 snd_mixer_selem_id_t *sid;
2807 snd_mixer_elem_t *me;
2808
2809 pa_assert(e);
2810 pa_assert(m);
2811 pa_assert(cb);
2812
2813 SELEM_INIT(sid, e->alsa_name);
2814 if (!(me = snd_mixer_find_selem(m, sid))) {
2815 pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
2816 return;
2817 }
2818
2819 snd_mixer_elem_set_callback(me, cb);
2820 snd_mixer_elem_set_callback_private(me, userdata);
2821 }
2822
2823 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2824 pa_alsa_element *e;
2825
2826 pa_assert(p);
2827 pa_assert(m);
2828 pa_assert(cb);
2829
2830 PA_LLIST_FOREACH(e, p->elements)
2831 element_set_callback(e, m, cb, userdata);
2832 }
2833
2834 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
2835 pa_alsa_path *p;
2836 void *state;
2837
2838 pa_assert(ps);
2839 pa_assert(m);
2840 pa_assert(cb);
2841
2842 PA_HASHMAP_FOREACH(p, ps->paths, state)
2843 pa_alsa_path_set_callback(p, m, cb, userdata);
2844 }
2845
2846 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
2847 pa_alsa_path_set *ps;
2848 char **pn = NULL, **en = NULL, **ie;
2849 pa_alsa_decibel_fix *db_fix;
2850 void *state, *state2;
2851 pa_hashmap *cache;
2852
2853 pa_assert(m);
2854 pa_assert(m->profile_set);
2855 pa_assert(m->profile_set->decibel_fixes);
2856 pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
2857
2858 if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
2859 return NULL;
2860
2861 ps = pa_xnew0(pa_alsa_path_set, 1);
2862 ps->direction = direction;
2863 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
2864
2865 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
2866 pn = m->output_path_names;
2867 cache = m->profile_set->output_paths;
2868 }
2869 else if (direction == PA_ALSA_DIRECTION_INPUT) {
2870 pn = m->input_path_names;
2871 cache = m->profile_set->input_paths;
2872 }
2873
2874 if (pn) {
2875 char **in;
2876
2877 for (in = pn; *in; in++) {
2878 pa_alsa_path *p = NULL;
2879 pa_bool_t duplicate = FALSE;
2880 char **kn;
2881
2882 for (kn = pn; kn < in; kn++)
2883 if (pa_streq(*kn, *in)) {
2884 duplicate = TRUE;
2885 break;
2886 }
2887
2888 if (duplicate)
2889 continue;
2890
2891 p = pa_hashmap_get(cache, *in);
2892 if (!p) {
2893 char *fn = pa_sprintf_malloc("%s.conf", *in);
2894 p = pa_alsa_path_new(paths_dir, fn, direction);
2895 pa_xfree(fn);
2896 if (p)
2897 pa_hashmap_put(cache, *in, p);
2898 }
2899 pa_assert(pa_hashmap_get(cache, *in) == p);
2900 if (p)
2901 pa_hashmap_put(ps->paths, p, p);
2902
2903 }
2904
2905 goto finish;
2906 }
2907
2908 if (direction == PA_ALSA_DIRECTION_OUTPUT)
2909 en = m->output_element;
2910 else if (direction == PA_ALSA_DIRECTION_INPUT)
2911 en = m->input_element;
2912
2913 if (!en) {
2914 pa_alsa_path_set_free(ps);
2915 return NULL;
2916 }
2917
2918 for (ie = en; *ie; ie++) {
2919 char **je;
2920 pa_alsa_path *p;
2921
2922 p = pa_alsa_path_synthesize(*ie, direction);
2923
2924 /* Mark all other passed elements for require-absent */
2925 for (je = en; *je; je++) {
2926 pa_alsa_element *e;
2927
2928 if (je == ie)
2929 continue;
2930
2931 e = pa_xnew0(pa_alsa_element, 1);
2932 e->path = p;
2933 e->alsa_name = pa_xstrdup(*je);
2934 e->direction = direction;
2935 e->required_absent = PA_ALSA_REQUIRED_ANY;
2936 e->volume_limit = -1;
2937
2938 PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
2939 p->last_element = e;
2940 }
2941
2942 pa_hashmap_put(ps->paths, *ie, p);
2943 }
2944
2945 finish:
2946 /* Assign decibel fixes to elements. */
2947 PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
2948 pa_alsa_path *p;
2949
2950 PA_HASHMAP_FOREACH(p, ps->paths, state2) {
2951 pa_alsa_element *e;
2952
2953 PA_LLIST_FOREACH(e, p->elements) {
2954 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
2955 /* The profile set that contains the dB fix may be freed
2956 * before the element, so we have to copy the dB fix
2957 * object. */
2958 e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
2959 e->db_fix->profile_set = NULL;
2960 e->db_fix->name = pa_xstrdup(db_fix->name);
2961 e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
2962 }
2963 }
2964 }
2965 }
2966
2967 return ps;
2968 }
2969
2970 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
2971 pa_alsa_path *p;
2972 void *state;
2973 pa_assert(ps);
2974
2975 pa_log_debug("Path Set %p, direction=%i",
2976 (void*) ps,
2977 ps->direction);
2978
2979 PA_HASHMAP_FOREACH(p, ps->paths, state)
2980 pa_alsa_path_dump(p);
2981 }
2982
2983
2984 static pa_bool_t options_have_option(pa_alsa_option *options, const char *alsa_name) {
2985 pa_alsa_option *o;
2986
2987 pa_assert(options);
2988 pa_assert(alsa_name);
2989
2990 PA_LLIST_FOREACH(o, options) {
2991 if (pa_streq(o->alsa_name, alsa_name))
2992 return TRUE;
2993 }
2994 return FALSE;
2995 }
2996
2997 static pa_bool_t enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
2998 pa_alsa_option *oa, *ob;
2999
3000 if (!a_options) return TRUE;
3001 if (!b_options) return FALSE;
3002
3003 /* If there is an option A offers that B does not, then A is not a subset of B. */
3004 PA_LLIST_FOREACH(oa, a_options) {
3005 pa_bool_t found = FALSE;
3006 PA_LLIST_FOREACH(ob, b_options) {
3007 if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3008 found = TRUE;
3009 break;
3010 }
3011 }
3012 if (!found)
3013 return FALSE;
3014 }
3015 return TRUE;
3016 }
3017
3018 /**
3019 * Compares two elements to see if a is a subset of b
3020 */
3021 static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3022 pa_assert(a);
3023 pa_assert(b);
3024 pa_assert(m);
3025
3026 /* General rules:
3027 * Every state is a subset of itself (with caveats for volume_limits and options)
3028 * IGNORE is a subset of every other state */
3029
3030 /* Check the volume_use */
3031 if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3032
3033 /* "Constant" is subset of "Constant" only when their constant values are equal */
3034 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3035 return FALSE;
3036
3037 /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3038 if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3039 return FALSE;
3040
3041 /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3042 * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3043 * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3044 if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3045 long a_limit;
3046
3047 if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3048 a_limit = a->constant_volume;
3049 else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3050 long dB = 0;
3051
3052 if (a->db_fix) {
3053 int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3054 a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3055 } else {
3056 snd_mixer_selem_id_t *sid;
3057 snd_mixer_elem_t *me;
3058
3059 SELEM_INIT(sid, a->alsa_name);
3060 if (!(me = snd_mixer_find_selem(m, sid))) {
3061 pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
3062 return FALSE;
3063 }
3064
3065 if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3066 if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3067 return FALSE;
3068 } else {
3069 if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3070 return FALSE;
3071 }
3072 }
3073 } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3074 a_limit = a->min_volume;
3075 else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3076 a_limit = a->volume_limit;
3077 else
3078 /* This should never be reached */
3079 pa_assert(FALSE);
3080
3081 if (a_limit > b->volume_limit)
3082 return FALSE;
3083 }
3084
3085 if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3086 int s;
3087 /* If override-maps are different, they're not subsets */
3088 if (a->n_channels != b->n_channels)
3089 return FALSE;
3090 for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3091 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3092 pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3093 a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3094 return FALSE;
3095 }
3096 }
3097 }
3098
3099 if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3100 /* "On" is a subset of "Mute".
3101 * "Off" is a subset of "Mute".
3102 * "On" is a subset of "Select", if there is an "Option:On" in B.
3103 * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3104 * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3105
3106 if (a->switch_use != b->switch_use) {
3107
3108 if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3109 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3110 return FALSE;
3111
3112 if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3113 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3114 if (!options_have_option(b->options, "on"))
3115 return FALSE;
3116 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3117 if (!options_have_option(b->options, "off"))
3118 return FALSE;
3119 }
3120 }
3121 } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3122 if (!enumeration_is_subset(a->options, b->options))
3123 return FALSE;
3124 }
3125 }
3126
3127 if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3128 if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3129 return FALSE;
3130 if (!enumeration_is_subset(a->options, b->options))
3131 return FALSE;
3132 }
3133
3134 return TRUE;
3135 }
3136
3137 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3138 pa_alsa_path *p;
3139 void *state;
3140
3141 pa_assert(ps);
3142 pa_assert(m);
3143
3144 /* If we only have one path, then don't bother */
3145 if (pa_hashmap_size(ps->paths) < 2)
3146 return;
3147
3148 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3149 pa_alsa_path *p2;
3150 void *state2;
3151
3152 PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3153 pa_alsa_element *ea, *eb;
3154 pa_alsa_jack *ja, *jb;
3155 pa_bool_t is_subset = TRUE;
3156
3157 if (p == p2)
3158 continue;
3159
3160 /* If a has a jack that b does not have, a is not a subset */
3161 PA_LLIST_FOREACH(ja, p->jacks) {
3162 pa_bool_t exists = FALSE;
3163
3164 if (!ja->has_control)
3165 continue;
3166
3167 PA_LLIST_FOREACH(jb, p2->jacks) {
3168 if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
3169 (ja->state_plugged == jb->state_plugged) &&
3170 (ja->state_unplugged == jb->state_unplugged)) {
3171 exists = TRUE;
3172 break;
3173 }
3174 }
3175
3176 if (!exists) {
3177 is_subset = FALSE;
3178 break;
3179 }
3180 }
3181
3182 /* Compare the elements of each set... */
3183 pa_assert_se(ea = p->elements);
3184 pa_assert_se(eb = p2->elements);
3185
3186 while (is_subset) {
3187 if (pa_streq(ea->alsa_name, eb->alsa_name)) {
3188 if (element_is_subset(ea, eb, m)) {
3189 ea = ea->next;
3190 eb = eb->next;
3191 if ((ea && !eb) || (!ea && eb))
3192 is_subset = FALSE;
3193 else if (!ea && !eb)
3194 break;
3195 } else
3196 is_subset = FALSE;
3197
3198 } else
3199 is_subset = FALSE;
3200 }
3201
3202 if (is_subset) {
3203 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3204 pa_hashmap_remove(ps->paths, p);
3205 break;
3206 }
3207 }
3208 }
3209 }
3210
3211 static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
3212 {
3213 pa_alsa_path* p;
3214 void *state;
3215
3216 PA_HASHMAP_FOREACH(p, ps->paths, state)
3217 if (p != ignore && pa_streq(p->name, name))
3218 return p;
3219 return NULL;
3220 }
3221
3222 static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
3223 pa_alsa_path *p, *q;
3224 void *state, *state2;
3225
3226 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3227 unsigned i;
3228 char *m;
3229
3230 q = path_set_find_path_by_name(ps, p->name, p);
3231
3232 if (!q)
3233 continue;
3234
3235 m = pa_xstrdup(p->name);
3236
3237 /* OK, this name is not unique, hence let's rename */
3238 i = 1;
3239 PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3240 char *nn, *nd;
3241
3242 if (!pa_streq(q->name, m))
3243 continue;
3244
3245 nn = pa_sprintf_malloc("%s-%u", m, i);
3246 pa_xfree(q->name);
3247 q->name = nn;
3248
3249 nd = pa_sprintf_malloc("%s %u", q->description, i);
3250 pa_xfree(q->description);
3251 q->description = nd;
3252
3253 i++;
3254 }
3255
3256 pa_xfree(m);
3257 }
3258 }
3259
3260 static void mapping_free(pa_alsa_mapping *m) {
3261 pa_assert(m);
3262
3263 pa_xfree(m->name);
3264 pa_xfree(m->description);
3265
3266 pa_proplist_free(m->proplist);
3267
3268 pa_xstrfreev(m->device_strings);
3269 pa_xstrfreev(m->input_path_names);
3270 pa_xstrfreev(m->output_path_names);
3271 pa_xstrfreev(m->input_element);
3272 pa_xstrfreev(m->output_element);
3273 if (m->input_path_set)
3274 pa_alsa_path_set_free(m->input_path_set);
3275 if (m->output_path_set)
3276 pa_alsa_path_set_free(m->output_path_set);
3277
3278 pa_assert(!m->input_pcm);
3279 pa_assert(!m->output_pcm);
3280
3281 pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3282
3283 pa_xfree(m);
3284 }
3285
3286 static void profile_free(pa_alsa_profile *p) {
3287 pa_assert(p);
3288
3289 pa_xfree(p->name);
3290 pa_xfree(p->description);
3291
3292 pa_xstrfreev(p->input_mapping_names);
3293 pa_xstrfreev(p->output_mapping_names);
3294
3295 if (p->input_mappings)
3296 pa_idxset_free(p->input_mappings, NULL);
3297
3298 if (p->output_mappings)
3299 pa_idxset_free(p->output_mappings, NULL);
3300
3301 pa_xfree(p);
3302 }
3303
3304 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3305 pa_assert(ps);
3306
3307 if (ps->input_paths)
3308 pa_hashmap_free(ps->input_paths, (pa_free_cb_t) pa_alsa_path_free);
3309
3310 if (ps->output_paths)
3311 pa_hashmap_free(ps->output_paths, (pa_free_cb_t) pa_alsa_path_free);
3312
3313 if (ps->profiles)
3314 pa_hashmap_free(ps->profiles, (pa_free_cb_t) profile_free);
3315
3316 if (ps->mappings)
3317 pa_hashmap_free(ps->mappings, (pa_free_cb_t) mapping_free);
3318
3319 if (ps->decibel_fixes)
3320 pa_hashmap_free(ps->decibel_fixes, (pa_free_cb_t) decibel_fix_free);
3321
3322 pa_xfree(ps);
3323 }
3324
3325 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3326 pa_alsa_mapping *m;
3327
3328 if (!pa_startswith(name, "Mapping "))
3329 return NULL;
3330
3331 name += 8;
3332
3333 if ((m = pa_hashmap_get(ps->mappings, name)))
3334 return m;
3335
3336 m = pa_xnew0(pa_alsa_mapping, 1);
3337 m->profile_set = ps;
3338 m->name = pa_xstrdup(name);
3339 pa_channel_map_init(&m->channel_map);
3340 m->proplist = pa_proplist_new();
3341
3342 pa_hashmap_put(ps->mappings, m->name, m);
3343
3344 return m;
3345 }
3346
3347 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3348 pa_alsa_profile *p;
3349
3350 if (!pa_startswith(name, "Profile "))
3351 return NULL;
3352
3353 name += 8;
3354
3355 if ((p = pa_hashmap_get(ps->profiles, name)))
3356 return p;
3357
3358 p = pa_xnew0(pa_alsa_profile, 1);
3359 p->profile_set = ps;
3360 p->name = pa_xstrdup(name);
3361
3362 pa_hashmap_put(ps->profiles, p->name, p);
3363
3364 return p;
3365 }
3366
3367 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
3368 pa_alsa_decibel_fix *db_fix;
3369
3370 if (!pa_startswith(name, "DecibelFix "))
3371 return NULL;
3372
3373 name += 11;
3374
3375 if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
3376 return db_fix;
3377
3378 db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
3379 db_fix->profile_set = ps;
3380 db_fix->name = pa_xstrdup(name);
3381
3382 pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
3383
3384 return db_fix;
3385 }
3386
3387 static int mapping_parse_device_strings(pa_config_parser_state *state) {
3388 pa_alsa_profile_set *ps;
3389 pa_alsa_mapping *m;
3390
3391 pa_assert(state);
3392
3393 ps = state->userdata;
3394
3395 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3396 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3397 return -1;
3398 }
3399
3400 pa_xstrfreev(m->device_strings);
3401 if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
3402 pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
3403 return -1;
3404 }
3405
3406 return 0;
3407 }
3408
3409 static int mapping_parse_channel_map(pa_config_parser_state *state) {
3410 pa_alsa_profile_set *ps;
3411 pa_alsa_mapping *m;
3412
3413 pa_assert(state);
3414
3415 ps = state->userdata;
3416
3417 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3418 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3419 return -1;
3420 }
3421
3422 if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
3423 pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
3424 return -1;
3425 }
3426
3427 return 0;
3428 }
3429
3430 static int mapping_parse_paths(pa_config_parser_state *state) {
3431 pa_alsa_profile_set *ps;
3432 pa_alsa_mapping *m;
3433
3434 pa_assert(state);
3435
3436 ps = state->userdata;
3437
3438 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3439 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3440 return -1;
3441 }
3442
3443 if (pa_streq(state->lvalue, "paths-input")) {
3444 pa_xstrfreev(m->input_path_names);
3445 m->input_path_names = pa_split_spaces_strv(state->rvalue);
3446 } else {
3447 pa_xstrfreev(m->output_path_names);
3448 m->output_path_names = pa_split_spaces_strv(state->rvalue);
3449 }
3450
3451 return 0;
3452 }
3453
3454 static int mapping_parse_element(pa_config_parser_state *state) {
3455 pa_alsa_profile_set *ps;
3456 pa_alsa_mapping *m;
3457
3458 pa_assert(state);
3459
3460 ps = state->userdata;
3461
3462 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3463 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3464 return -1;
3465 }
3466
3467 if (pa_streq(state->lvalue, "element-input")) {
3468 pa_xstrfreev(m->input_element);
3469 m->input_element = pa_split_spaces_strv(state->rvalue);
3470 } else {
3471 pa_xstrfreev(m->output_element);
3472 m->output_element = pa_split_spaces_strv(state->rvalue);
3473 }
3474
3475 return 0;
3476 }
3477
3478 static int mapping_parse_direction(pa_config_parser_state *state) {
3479 pa_alsa_profile_set *ps;
3480 pa_alsa_mapping *m;
3481
3482 pa_assert(state);
3483
3484 ps = state->userdata;
3485
3486 if (!(m = pa_alsa_mapping_get(ps, state->section))) {
3487 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3488 return -1;
3489 }
3490
3491 if (pa_streq(state->rvalue, "input"))
3492 m->direction = PA_ALSA_DIRECTION_INPUT;
3493 else if (pa_streq(state->rvalue, "output"))
3494 m->direction = PA_ALSA_DIRECTION_OUTPUT;
3495 else if (pa_streq(state->rvalue, "any"))
3496 m->direction = PA_ALSA_DIRECTION_ANY;
3497 else {
3498 pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
3499 return -1;
3500 }
3501
3502 return 0;
3503 }
3504
3505 static int mapping_parse_description(pa_config_parser_state *state) {
3506 pa_alsa_profile_set *ps;
3507 pa_alsa_profile *p;
3508 pa_alsa_mapping *m;
3509
3510 pa_assert(state);
3511
3512 ps = state->userdata;
3513
3514 if ((m = pa_alsa_mapping_get(ps, state->section))) {
3515 pa_xfree(m->description);
3516 m->description = pa_xstrdup(state->rvalue);
3517 } else if ((p = profile_get(ps, state->section))) {
3518 pa_xfree(p->description);
3519 p->description = pa_xstrdup(state->rvalue);
3520 } else {
3521 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3522 return -1;
3523 }
3524
3525 return 0;
3526 }
3527
3528 static int mapping_parse_priority(pa_config_parser_state *state) {
3529 pa_alsa_profile_set *ps;
3530 pa_alsa_profile *p;
3531 pa_alsa_mapping *m;
3532 uint32_t prio;
3533
3534 pa_assert(state);
3535
3536 ps = state->userdata;
3537
3538 if (pa_atou(state->rvalue, &prio) < 0) {
3539 pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
3540 return -1;
3541 }
3542
3543 if ((m = pa_alsa_mapping_get(ps, state->section)))
3544 m->priority = prio;
3545 else if ((p = profile_get(ps, state->section)))
3546 p->priority = prio;
3547 else {
3548 pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
3549 return -1;
3550 }
3551
3552 return 0;
3553 }
3554
3555 static int profile_parse_mappings(pa_config_parser_state *state) {
3556 pa_alsa_profile_set *ps;
3557 pa_alsa_profile *p;
3558
3559 pa_assert(state);
3560
3561 ps = state->userdata;
3562
3563 if (!(p = profile_get(ps, state->section))) {
3564 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3565 return -1;
3566 }
3567
3568 if (pa_streq(state->lvalue, "input-mappings")) {
3569 pa_xstrfreev(p->input_mapping_names);
3570 p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
3571 } else {
3572 pa_xstrfreev(p->output_mapping_names);
3573 p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
3574 }
3575
3576 return 0;
3577 }
3578
3579 static int profile_parse_skip_probe(pa_config_parser_state *state) {
3580 pa_alsa_profile_set *ps;
3581 pa_alsa_profile *p;
3582 int b;
3583
3584 pa_assert(state);
3585
3586 ps = state->userdata;
3587
3588 if (!(p = profile_get(ps, state->section))) {
3589 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3590 return -1;
3591 }
3592
3593 if ((b = pa_parse_boolean(state->rvalue)) < 0) {
3594 pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
3595 return -1;
3596 }
3597
3598 p->supported = b;
3599
3600 return 0;
3601 }
3602
3603 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
3604 pa_alsa_profile_set *ps;
3605 pa_alsa_decibel_fix *db_fix;
3606 char **items;
3607 char *item;
3608 long *db_values;
3609 unsigned n = 8; /* Current size of the db_values table. */
3610 unsigned min_step = 0;
3611 unsigned max_step = 0;
3612 unsigned i = 0; /* Index to the items table. */
3613 unsigned prev_step = 0;
3614 double prev_db = 0;
3615
3616 pa_assert(state);
3617
3618 ps = state->userdata;
3619
3620 if (!(db_fix = decibel_fix_get(ps, state->section))) {
3621 pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
3622 return -1;
3623 }
3624
3625 if (!(items = pa_split_spaces_strv(state->rvalue))) {
3626 pa_log("[%s:%u] Value missing", state->filename, state->lineno);
3627 return -1;
3628 }
3629
3630 db_values = pa_xnew(long, n);
3631
3632 while ((item = items[i++])) {
3633 char *s = item; /* Step value string. */
3634 char *d = item; /* dB value string. */
3635 uint32_t step;
3636 double db;
3637
3638 /* Move d forward until it points to a colon or to the end of the item. */
3639 for (; *d && *d != ':'; ++d);
3640
3641 if (d == s) {
3642 /* item started with colon. */
3643 pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
3644 goto fail;
3645 }
3646
3647 if (!*d || !*(d + 1)) {
3648 /* No colon found, or it was the last character in item. */
3649 pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
3650 goto fail;
3651 }
3652
3653 /* pa_atou() needs a null-terminating string. Let's replace the colon
3654 * with a zero byte. */
3655 *d++ = '\0';
3656
3657 if (pa_atou(s, &step) < 0) {
3658 pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
3659 goto fail;
3660 }
3661
3662 if (pa_atod(d, &db) < 0) {
3663 pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
3664 goto fail;
3665 }
3666
3667 if (step <= prev_step && i != 1) {
3668 pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
3669 goto fail;
3670 }
3671
3672 if (db < prev_db && i != 1) {
3673 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
3674 goto fail;
3675 }
3676
3677 if (i == 1) {
3678 min_step = step;
3679 db_values[0] = (long) (db * 100.0);
3680 prev_step = step;
3681 prev_db = db;
3682 } else {
3683 /* Interpolate linearly. */
3684 double db_increment = (db - prev_db) / (step - prev_step);
3685
3686 for (; prev_step < step; ++prev_step, prev_db += db_increment) {
3687
3688 /* Reallocate the db_values table if it's about to overflow. */
3689 if (prev_step + 1 - min_step == n) {
3690 n *= 2;
3691 db_values = pa_xrenew(long, db_values, n);
3692 }
3693
3694 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
3695 }
3696 }
3697
3698 max_step = step;
3699 }
3700
3701 db_fix->min_step = min_step;
3702 db_fix->max_step = max_step;
3703 pa_xfree(db_fix->db_values);
3704 db_fix->db_values = db_values;
3705
3706 pa_xstrfreev(items);
3707
3708 return 0;
3709
3710 fail:
3711 pa_xstrfreev(items);
3712 pa_xfree(db_values);
3713
3714 return -1;
3715 }
3716
3717 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
3718 pa_alsa_direction_t direction) {
3719
3720 pa_alsa_path *p;
3721 void *state;
3722 snd_pcm_t *pcm_handle;
3723 pa_alsa_path_set *ps;
3724 snd_mixer_t *mixer_handle;
3725 snd_hctl_t *hctl_handle;
3726
3727 if (direction == PA_ALSA_DIRECTION_OUTPUT) {
3728 if (m->output_path_set)
3729 return; /* Already probed */
3730 m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3731 pcm_handle = m->output_pcm;
3732 } else {
3733 if (m->input_path_set)
3734 return; /* Already probed */
3735 m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
3736 pcm_handle = m->input_pcm;
3737 }
3738
3739 if (!ps)
3740 return; /* No paths */
3741
3742 pa_assert(pcm_handle);
3743
3744 mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
3745 if (!mixer_handle || !hctl_handle) {
3746 /* Cannot open mixer, remove all entries */
3747 pa_hashmap_remove_all(ps->paths, NULL);
3748 return;
3749 }
3750
3751
3752 PA_HASHMAP_FOREACH(p, ps->paths, state) {
3753 if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
3754 pa_hashmap_remove(ps->paths, p);
3755 }
3756 }
3757
3758 path_set_condense(ps, mixer_handle);
3759 path_set_make_paths_unique(ps);
3760
3761 if (mixer_handle)
3762 snd_mixer_close(mixer_handle);
3763
3764 pa_log_debug("Available mixer paths (after tidying):");
3765 pa_alsa_path_set_dump(ps);
3766 }
3767
3768 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
3769
3770 static const struct description_map well_known_descriptions[] = {
3771 { "analog-mono", N_("Analog Mono") },
3772 { "analog-stereo", N_("Analog Stereo") },
3773 { "analog-surround-21", N_("Analog Surround 2.1") },
3774 { "analog-surround-30", N_("Analog Surround 3.0") },
3775 { "analog-surround-31", N_("Analog Surround 3.1") },
3776 { "analog-surround-40", N_("Analog Surround 4.0") },
3777 { "analog-surround-41", N_("Analog Surround 4.1") },
3778 { "analog-surround-50", N_("Analog Surround 5.0") },
3779 { "analog-surround-51", N_("Analog Surround 5.1") },
3780 { "analog-surround-61", N_("Analog Surround 6.0") },
3781 { "analog-surround-61", N_("Analog Surround 6.1") },
3782 { "analog-surround-70", N_("Analog Surround 7.0") },
3783 { "analog-surround-71", N_("Analog Surround 7.1") },
3784 { "analog-4-channel-input", N_("Analog 4-channel Input") },
3785 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3786 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3787 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3788 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3789 { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
3790 { "hdmi-stereo", N_("Digital Stereo (HDMI)") },
3791 { "hdmi-surround-51", N_("Digital Surround 5.1 (HDMI)") }
3792 };
3793
3794 pa_assert(m);
3795
3796 if (!pa_channel_map_valid(&m->channel_map)) {
3797 pa_log("Mapping %s is missing channel map.", m->name);
3798 return -1;
3799 }
3800
3801 if (!m->device_strings) {
3802 pa_log("Mapping %s is missing device strings.", m->name);
3803 return -1;
3804 }
3805
3806 if ((m->input_path_names && m->input_element) ||
3807 (m->output_path_names && m->output_element)) {
3808 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
3809 return -1;
3810 }
3811
3812 if (!m->description)
3813 m->description = pa_xstrdup(lookup_description(m->name,
3814 well_known_descriptions,
3815 PA_ELEMENTSOF(well_known_descriptions)));
3816
3817 if (!m->description)
3818 m->description = pa_xstrdup(m->name);
3819
3820 if (bonus) {
3821 if (pa_channel_map_equal(&m->channel_map, bonus))
3822 m->priority += 50;
3823 else if (m->channel_map.channels == bonus->channels)
3824 m->priority += 30;
3825 }
3826
3827 return 0;
3828 }
3829
3830 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
3831 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
3832
3833 pa_assert(m);
3834
3835 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3836 m->name,
3837 pa_strnull(m->description),
3838 m->priority,
3839 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
3840 pa_yes_no(m->supported),
3841 m->direction);
3842 }
3843
3844 static void profile_set_add_auto_pair(
3845 pa_alsa_profile_set *ps,
3846 pa_alsa_mapping *m, /* output */
3847 pa_alsa_mapping *n /* input */) {
3848
3849 char *name;
3850 pa_alsa_profile *p;
3851
3852 pa_assert(ps);
3853 pa_assert(m || n);
3854
3855 if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
3856 return;
3857
3858 if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
3859 return;
3860
3861 if (m && n)
3862 name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
3863 else if (m)
3864 name = pa_sprintf_malloc("output:%s", m->name);
3865 else
3866 name = pa_sprintf_malloc("input:%s", n->name);
3867
3868 if (pa_hashmap_get(ps->profiles, name)) {
3869 pa_xfree(name);
3870 return;
3871 }
3872
3873 p = pa_xnew0(pa_alsa_profile, 1);
3874 p->profile_set = ps;
3875 p->name = name;
3876
3877 if (m) {
3878 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3879 pa_idxset_put(p->output_mappings, m, NULL);
3880 p->priority += m->priority * 100;
3881 }
3882
3883 if (n) {
3884 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3885 pa_idxset_put(p->input_mappings, n, NULL);
3886 p->priority += n->priority;
3887 }
3888
3889 pa_hashmap_put(ps->profiles, p->name, p);
3890 }
3891
3892 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
3893 pa_alsa_mapping *m, *n;
3894 void *m_state, *n_state;
3895
3896 pa_assert(ps);
3897
3898 /* The order is important here:
3899 1) try single inputs and outputs before trying their
3900 combination, because if the half-duplex test failed, we don't have
3901 to try full duplex.
3902 2) try the output right before the input combinations with
3903 that output, because then the output_pcm is not closed between tests.
3904 */
3905 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3906 profile_set_add_auto_pair(ps, NULL, n);
3907
3908 PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
3909 profile_set_add_auto_pair(ps, m, NULL);
3910
3911 PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
3912 profile_set_add_auto_pair(ps, m, n);
3913 }
3914
3915 }
3916
3917 static int profile_verify(pa_alsa_profile *p) {
3918
3919 static const struct description_map well_known_descriptions[] = {
3920 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3921 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3922 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3923 { "off", N_("Off") }
3924 };
3925
3926 pa_assert(p);
3927
3928 /* Replace the output mapping names by the actual mappings */
3929 if (p->output_mapping_names) {
3930 char **name;
3931
3932 pa_assert(!p->output_mappings);
3933 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3934
3935 for (name = p->output_mapping_names; *name; name++) {
3936 pa_alsa_mapping *m;
3937 char **in;
3938 pa_bool_t duplicate = FALSE;
3939
3940 for (in = name + 1; *in; in++)
3941 if (pa_streq(*name, *in)) {
3942 duplicate = TRUE;
3943 break;
3944 }
3945
3946 if (duplicate)
3947 continue;
3948
3949 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
3950 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3951 return -1;
3952 }
3953
3954 pa_idxset_put(p->output_mappings, m, NULL);
3955
3956 if (p->supported)
3957 m->supported++;
3958 }
3959
3960 pa_xstrfreev(p->output_mapping_names);
3961 p->output_mapping_names = NULL;
3962 }
3963
3964 /* Replace the input mapping names by the actual mappings */
3965 if (p->input_mapping_names) {
3966 char **name;
3967
3968 pa_assert(!p->input_mappings);
3969 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3970
3971 for (name = p->input_mapping_names; *name; name++) {
3972 pa_alsa_mapping *m;
3973 char **in;
3974 pa_bool_t duplicate = FALSE;
3975
3976 for (in = name + 1; *in; in++)
3977 if (pa_streq(*name, *in)) {
3978 duplicate = TRUE;
3979 break;
3980 }
3981
3982 if (duplicate)
3983 continue;
3984
3985 if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
3986 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
3987 return -1;
3988 }
3989
3990 pa_idxset_put(p->input_mappings, m, NULL);
3991
3992 if (p->supported)
3993 m->supported++;
3994 }
3995
3996 pa_xstrfreev(p->input_mapping_names);
3997 p->input_mapping_names = NULL;
3998 }
3999
4000 if (!p->input_mappings && !p->output_mappings) {
4001 pa_log("Profile '%s' lacks mappings.", p->name);
4002 return -1;
4003 }
4004
4005 if (!p->description)
4006 p->description = pa_xstrdup(lookup_description(p->name,
4007 well_known_descriptions,
4008 PA_ELEMENTSOF(well_known_descriptions)));
4009
4010 if (!p->description) {
4011 pa_strbuf *sb;
4012 uint32_t idx;
4013 pa_alsa_mapping *m;
4014
4015 sb = pa_strbuf_new();
4016
4017 if (p->output_mappings)
4018 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4019 if (!pa_strbuf_isempty(sb))
4020 pa_strbuf_puts(sb, " + ");
4021
4022 pa_strbuf_printf(sb, _("%s Output"), m->description);
4023 }
4024
4025 if (p->input_mappings)
4026 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4027 if (!pa_strbuf_isempty(sb))
4028 pa_strbuf_puts(sb, " + ");
4029
4030 pa_strbuf_printf(sb, _("%s Input"), m->description);
4031 }
4032
4033 p->description = pa_strbuf_tostring_free(sb);
4034 }
4035
4036 return 0;
4037 }
4038
4039 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4040 uint32_t idx;
4041 pa_alsa_mapping *m;
4042 pa_assert(p);
4043
4044 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4045 p->name,
4046 pa_strnull(p->description),
4047 p->priority,
4048 pa_yes_no(p->supported),
4049 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4050 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4051
4052 if (p->input_mappings)
4053 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4054 pa_log_debug("Input %s", m->name);
4055
4056 if (p->output_mappings)
4057 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4058 pa_log_debug("Output %s", m->name);
4059 }
4060
4061 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4062 pa_assert(db_fix);
4063
4064 /* Check that the dB mapping has been configured. Since "db-values" is
4065 * currently the only option in the DecibelFix section, and decibel fix
4066 * objects don't get created if a DecibelFix section is empty, this is
4067 * actually a redundant check. Having this may prevent future bugs,
4068 * however. */
4069 if (!db_fix->db_values) {
4070 pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4071 return -1;
4072 }
4073
4074 return 0;
4075 }
4076
4077 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4078 char *db_values = NULL;
4079
4080 pa_assert(db_fix);
4081
4082 if (db_fix->db_values) {
4083 pa_strbuf *buf;
4084 unsigned long i, nsteps;
4085
4086 pa_assert(db_fix->min_step <= db_fix->max_step);
4087 nsteps = db_fix->max_step - db_fix->min_step + 1;
4088
4089 buf = pa_strbuf_new();
4090 for (i = 0; i < nsteps; ++i)
4091 pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4092
4093 db_values = pa_strbuf_tostring_free(buf);
4094 }
4095
4096 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4097 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4098
4099 pa_xfree(db_values);
4100 }
4101
4102 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4103 pa_alsa_profile_set *ps;
4104 pa_alsa_profile *p;
4105 pa_alsa_mapping *m;
4106 pa_alsa_decibel_fix *db_fix;
4107 char *fn;
4108 int r;
4109 void *state;
4110
4111 static pa_config_item items[] = {
4112 /* [General] */
4113 { "auto-profiles", pa_config_parse_bool, NULL, "General" },
4114
4115 /* [Mapping ...] */
4116 { "device-strings", mapping_parse_device_strings, NULL, NULL },
4117 { "channel-map", mapping_parse_channel_map, NULL, NULL },
4118 { "paths-input", mapping_parse_paths, NULL, NULL },
4119 { "paths-output", mapping_parse_paths, NULL, NULL },
4120 { "element-input", mapping_parse_element, NULL, NULL },
4121 { "element-output", mapping_parse_element, NULL, NULL },
4122 { "direction", mapping_parse_direction, NULL, NULL },
4123
4124 /* Shared by [Mapping ...] and [Profile ...] */
4125 { "description", mapping_parse_description, NULL, NULL },
4126 { "priority", mapping_parse_priority, NULL, NULL },
4127
4128 /* [Profile ...] */
4129 { "input-mappings", profile_parse_mappings, NULL, NULL },
4130 { "output-mappings", profile_parse_mappings, NULL, NULL },
4131 { "skip-probe", profile_parse_skip_probe, NULL, NULL },
4132
4133 /* [DecibelFix ...] */
4134 { "db-values", decibel_fix_parse_db_values, NULL, NULL },
4135 { NULL, NULL, NULL, NULL }
4136 };
4137
4138 ps = pa_xnew0(pa_alsa_profile_set, 1);
4139 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4140 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4141 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4142 ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4143 ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
4144
4145 items[0].data = &ps->auto_profiles;
4146
4147 if (!fname)
4148 fname = "default.conf";
4149
4150 fn = pa_maybe_prefix_path(fname,
4151 pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
4152 PA_ALSA_PROFILE_SETS_DIR);
4153
4154 r = pa_config_parse(fn, NULL, items, NULL, ps);
4155 pa_xfree(fn);
4156
4157 if (r < 0)
4158 goto fail;
4159
4160 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4161 if (mapping_verify(m, bonus) < 0)
4162 goto fail;
4163
4164 if (ps->auto_profiles)
4165 profile_set_add_auto(ps);
4166
4167 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4168 if (profile_verify(p) < 0)
4169 goto fail;
4170
4171 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4172 if (decibel_fix_verify(db_fix) < 0)
4173 goto fail;
4174
4175 return ps;
4176
4177 fail:
4178 pa_alsa_profile_set_free(ps);
4179 return NULL;
4180 }
4181
4182 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
4183 pa_alsa_mapping *m;
4184 uint32_t idx;
4185
4186 if (!to_be_finalized)
4187 return;
4188
4189 if (to_be_finalized->output_mappings)
4190 PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
4191
4192 if (!m->output_pcm)
4193 continue;
4194
4195 if (to_be_finalized->supported)
4196 m->supported++;
4197
4198 /* If this mapping is also in the next profile, we won't close the
4199 * pcm handle here, because it would get immediately reopened
4200 * anyway. */
4201 if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
4202 continue;
4203
4204 snd_pcm_close(m->output_pcm);
4205 m->output_pcm = NULL;
4206 }
4207
4208 if (to_be_finalized->input_mappings)
4209 PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
4210
4211 if (!m->input_pcm)
4212 continue;
4213
4214 if (to_be_finalized->supported)
4215 m->supported++;
4216
4217 /* If this mapping is also in the next profile, we won't close the
4218 * pcm handle here, because it would get immediately reopened
4219 * anyway. */
4220 if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
4221 continue;
4222
4223 snd_pcm_close(m->input_pcm);
4224 m->input_pcm = NULL;
4225 }
4226 }
4227
4228 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
4229 const pa_sample_spec *ss,
4230 const char *dev_id,
4231 int mode,
4232 unsigned default_n_fragments,
4233 unsigned default_fragment_size_msec) {
4234
4235 pa_sample_spec try_ss = *ss;
4236 pa_channel_map try_map = m->channel_map;
4237 snd_pcm_uframes_t try_period_size, try_buffer_size;
4238
4239 try_ss.channels = try_map.channels;
4240
4241 try_period_size =
4242 pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
4243 pa_frame_size(&try_ss);
4244 try_buffer_size = default_n_fragments * try_period_size;
4245
4246 return pa_alsa_open_by_template(
4247 m->device_strings, dev_id, NULL, &try_ss,
4248 &try_map, mode, &try_period_size,
4249 &try_buffer_size, 0, NULL, NULL, TRUE);
4250 }
4251
4252 static void paths_drop_unsupported(pa_hashmap* h) {
4253
4254 void* state = NULL;
4255 const void* key;
4256 pa_alsa_path* p;
4257
4258 pa_assert(h);
4259 p = pa_hashmap_iterate(h, &state, &key);
4260 while (p) {
4261 if (p->supported <= 0) {
4262 pa_hashmap_remove(h, key);
4263 pa_alsa_path_free(p);
4264 }
4265 p = pa_hashmap_iterate(h, &state, &key);
4266 }
4267 }
4268
4269 void pa_alsa_profile_set_probe(
4270 pa_alsa_profile_set *ps,
4271 const char *dev_id,
4272 const pa_sample_spec *ss,
4273 unsigned default_n_fragments,
4274 unsigned default_fragment_size_msec) {
4275
4276 void *state;
4277 pa_alsa_profile *p, *last = NULL;
4278 pa_alsa_mapping *m;
4279 pa_hashmap *broken_inputs, *broken_outputs;
4280
4281 pa_assert(ps);
4282 pa_assert(dev_id);
4283 pa_assert(ss);
4284
4285 if (ps->probed)
4286 return;
4287
4288 broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4289 broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4290
4291 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4292 uint32_t idx;
4293
4294 /* Skip if this is already marked that it is supported (i.e. from the config file) */
4295 if (!p->supported) {
4296
4297 profile_finalize_probing(last, p);
4298 p->supported = TRUE;
4299
4300 if (p->output_mappings) {
4301 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4302 if (pa_hashmap_get(broken_outputs, m) == m) {
4303 pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
4304 p->supported = FALSE;
4305 break;
4306 }
4307 }
4308 }
4309
4310 if (p->input_mappings && p->supported) {
4311 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4312 if (pa_hashmap_get(broken_inputs, m) == m) {
4313 pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
4314 p->supported = FALSE;
4315 break;
4316 }
4317 }
4318 }
4319
4320 if (p->supported)
4321 pa_log_debug("Looking at profile %s", p->name);
4322
4323 /* Check if we can open all new ones */
4324 if (p->output_mappings && p->supported)
4325 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4326
4327 if (m->output_pcm)
4328 continue;
4329
4330 pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
4331 if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id,
4332 SND_PCM_STREAM_PLAYBACK,
4333 default_n_fragments,
4334 default_fragment_size_msec))) {
4335 p->supported = FALSE;
4336 if (pa_idxset_size(p->output_mappings) == 1 &&
4337 ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
4338 pa_log_debug("Caching failure to open output:%s", m->name);
4339 pa_hashmap_put(broken_outputs, m, m);
4340 }
4341 break;
4342 }
4343 }
4344
4345 if (p->input_mappings && p->supported)
4346 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4347
4348 if (m->input_pcm)
4349 continue;
4350
4351 pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
4352 if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id,
4353 SND_PCM_STREAM_CAPTURE,
4354 default_n_fragments,
4355 default_fragment_size_msec))) {
4356 p->supported = FALSE;
4357 if (pa_idxset_size(p->input_mappings) == 1 &&
4358 ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
4359 pa_log_debug("Caching failure to open input:%s", m->name);
4360 pa_hashmap_put(broken_inputs, m, m);
4361 }
4362 break;
4363 }
4364 }
4365
4366 last = p;
4367
4368 if (!p->supported)
4369 continue;
4370 }
4371
4372 pa_log_debug("Profile %s supported.", p->name);
4373
4374 if (p->output_mappings)
4375 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4376 if (m->output_pcm)
4377 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
4378
4379 if (p->input_mappings)
4380 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4381 if (m->input_pcm)
4382 mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
4383 }
4384
4385 /* Clean up */
4386 profile_finalize_probing(last, NULL);
4387
4388 pa_alsa_profile_set_drop_unsupported(ps);
4389
4390 paths_drop_unsupported(ps->input_paths);
4391 paths_drop_unsupported(ps->output_paths);
4392 pa_hashmap_free(broken_inputs, NULL);
4393 pa_hashmap_free(broken_outputs, NULL);
4394
4395 ps->probed = TRUE;
4396 }
4397
4398 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
4399 pa_alsa_profile *p;
4400 pa_alsa_mapping *m;
4401 pa_alsa_decibel_fix *db_fix;
4402 void *state;
4403
4404 pa_assert(ps);
4405
4406 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4407 (void*)
4408 ps,
4409 pa_yes_no(ps->auto_profiles),
4410 pa_yes_no(ps->probed),
4411 pa_hashmap_size(ps->mappings),
4412 pa_hashmap_size(ps->profiles),
4413 pa_hashmap_size(ps->decibel_fixes));
4414
4415 PA_HASHMAP_FOREACH(m, ps->mappings, state)
4416 pa_alsa_mapping_dump(m);
4417
4418 PA_HASHMAP_FOREACH(p, ps->profiles, state)
4419 pa_alsa_profile_dump(p);
4420
4421 PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4422 pa_alsa_decibel_fix_dump(db_fix);
4423 }
4424
4425 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
4426 pa_alsa_profile *p;
4427 pa_alsa_mapping *m;
4428 void *state;
4429
4430 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
4431 if (!p->supported) {
4432 pa_hashmap_remove(ps->profiles, p->name);
4433 profile_free(p);
4434 }
4435 }
4436
4437 PA_HASHMAP_FOREACH(m, ps->mappings, state) {
4438 if (m->supported <= 0) {
4439 pa_hashmap_remove(ps->mappings, m->name);
4440 mapping_free(m);
4441 }
4442 }
4443 }
4444
4445 static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
4446 const char* name,
4447 const char* description,
4448 pa_alsa_path *path,
4449 pa_alsa_setting *setting,
4450 pa_card_profile *cp,
4451 pa_hashmap *extra,
4452 pa_core *core) {
4453
4454 pa_device_port *p;
4455
4456 pa_assert(path);
4457
4458 p = pa_hashmap_get(ports, name);
4459
4460 if (!p) {
4461 pa_alsa_port_data *data;
4462
4463 p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
4464 pa_assert(p);
4465 pa_hashmap_put(ports, p->name, p);
4466 pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
4467
4468 data = PA_DEVICE_PORT_DATA(p);
4469 data->path = path;
4470 data->setting = setting;
4471 path->port = p;
4472 }
4473
4474 p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
4475 p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
4476
4477 if (cp)
4478 pa_hashmap_put(p->profiles, cp->name, cp);
4479
4480 if (extra) {
4481 pa_hashmap_put(extra, p->name, p);
4482 pa_device_port_ref(p);
4483 }
4484
4485 return p;
4486 }
4487
4488 void pa_alsa_path_set_add_ports(
4489 pa_alsa_path_set *ps,
4490 pa_card_profile *cp,
4491 pa_hashmap *ports,
4492 pa_hashmap *extra,
4493 pa_core *core) {
4494
4495 pa_alsa_path *path;
4496 void *state;
4497
4498 pa_assert(ports);
4499
4500 if (!ps)
4501 return;
4502
4503 PA_HASHMAP_FOREACH(path, ps->paths, state) {
4504 if (!path->settings || !path->settings->next) {
4505 /* If there is no or just one setting we only need a
4506 * single entry */
4507 pa_device_port *port = device_port_alsa_init(ports, path->name,
4508 path->description, path, path->settings, cp, extra, core);
4509 port->priority = path->priority * 100;
4510
4511 } else {
4512 pa_alsa_setting *s;
4513 PA_LLIST_FOREACH(s, path->settings) {
4514 pa_device_port *port;
4515 char *n, *d;
4516
4517 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
4518
4519 if (s->description[0])
4520 d = pa_sprintf_malloc("%s / %s", path->description, s->description);
4521 else
4522 d = pa_xstrdup(path->description);
4523
4524 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
4525 port->priority = path->priority * 100 + s->priority;
4526
4527 pa_xfree(n);
4528 pa_xfree(d);
4529 }
4530 }
4531 }
4532 }
4533
4534 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
4535 pa_hashmap *ports;
4536
4537 pa_assert(sink_or_source_new_data);
4538 pa_assert(ps);
4539
4540 if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
4541 ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
4542 else
4543 ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
4544
4545 if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
4546 pa_assert(card);
4547 pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
4548 }
4549
4550 pa_log_debug("Added %u ports", pa_hashmap_size(ports));
4551 }