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