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