• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_path_config_path(const char * paths_dir,const char * fname)2854 static char *get_path_config_path(const char *paths_dir, const char *fname) {
2855     char *path_config_path;
2856     char *dir;
2857     char *data_home;
2858     pa_dynarray *data_dirs;
2859 
2860     if (paths_dir) {
2861         path_config_path = pa_maybe_prefix_path(fname, paths_dir);
2862         if (access(path_config_path, R_OK) == 0)
2863             return path_config_path;
2864         else
2865             pa_xfree(path_config_path);
2866     }
2867 
2868 #ifdef HAVE_RUNNING_FROM_BUILD_TREE
2869     if (pa_run_from_build_tree()) {
2870         path_config_path = pa_maybe_prefix_path(fname, PA_SRCDIR "/modules/alsa/mixer/paths/");
2871         if (access(path_config_path, R_OK) == 0)
2872             return path_config_path;
2873         else
2874             pa_xfree(path_config_path);
2875     }
2876 #endif
2877 
2878     if (pa_get_data_home_dir(&data_home) == 0) {
2879         dir = pa_sprintf_malloc("%s" PA_PATH_SEP "alsa-mixer" PA_PATH_SEP "paths", data_home);
2880         pa_xfree(data_home);
2881 
2882         path_config_path = pa_maybe_prefix_path(fname, dir);
2883         pa_xfree(dir);
2884 
2885         if (access(path_config_path, R_OK) == 0)
2886             return path_config_path;
2887         else
2888             pa_xfree(path_config_path);
2889     }
2890 
2891     if (pa_get_data_dirs(&data_dirs) == 0) {
2892         int idx;
2893         const char *n;
2894 
2895         PA_DYNARRAY_FOREACH(n, data_dirs, idx) {
2896             dir = pa_sprintf_malloc("%s" PA_PATH_SEP "alsa-mixer" PA_PATH_SEP "paths", n);
2897             path_config_path = pa_maybe_prefix_path(fname, dir);
2898             pa_xfree(dir);
2899 
2900             if (access(path_config_path, R_OK) == 0) {
2901                 pa_dynarray_free(data_dirs);
2902                 return path_config_path;
2903             }
2904             else {
2905                 pa_xfree(path_config_path);
2906             }
2907         }
2908 
2909         pa_dynarray_free(data_dirs);
2910     }
2911 
2912     path_config_path = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
2913     return path_config_path;
2914 }
2915 
pa_alsa_path_new(const char * paths_dir,const char * fname,pa_alsa_direction_t direction)2916 pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
2917     pa_alsa_path *p;
2918     char *fn;
2919     int r;
2920     const char *n;
2921     bool mute_during_activation = false;
2922 
2923     pa_config_item items[] = {
2924         /* [General] */
2925         { "priority",            pa_config_parse_unsigned,          NULL, "General" },
2926         { "description-key",     pa_config_parse_string,            NULL, "General" },
2927         { "description",         pa_config_parse_string,            NULL, "General" },
2928         { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
2929         { "type",                parse_type,                        NULL, "General" },
2930         { "eld-device",          parse_eld_device,                  NULL, "General" },
2931 
2932         /* [Option ...] */
2933         { "priority",            option_parse_priority,             NULL, NULL },
2934         { "name",                option_parse_name,                 NULL, NULL },
2935 
2936         /* [Jack ...] */
2937         { "state.plugged",       jack_parse_state,                  NULL, NULL },
2938         { "state.unplugged",     jack_parse_state,                  NULL, NULL },
2939         { "append-pcm-to-name",  jack_parse_append_pcm_to_name,     NULL, NULL },
2940 
2941         /* [Element ...] */
2942         { "switch",              element_parse_switch,              NULL, NULL },
2943         { "volume",              element_parse_volume,              NULL, NULL },
2944         { "enumeration",         element_parse_enumeration,         NULL, NULL },
2945         { "override-map.1",      element_parse_override_map,        NULL, NULL },
2946         { "override-map.2",      element_parse_override_map,        NULL, NULL },
2947         { "override-map.3",      element_parse_override_map,        NULL, NULL },
2948         { "override-map.4",      element_parse_override_map,        NULL, NULL },
2949         { "override-map.5",      element_parse_override_map,        NULL, NULL },
2950         { "override-map.6",      element_parse_override_map,        NULL, NULL },
2951         { "override-map.7",      element_parse_override_map,        NULL, NULL },
2952         { "override-map.8",      element_parse_override_map,        NULL, NULL },
2953 #if POSITION_MASK_CHANNELS > 8
2954 #error "Add override-map.9+ definitions"
2955 #endif
2956         /* ... later on we might add override-map.3 and so on here ... */
2957         { "required",            element_parse_required,            NULL, NULL },
2958         { "required-any",        element_parse_required,            NULL, NULL },
2959         { "required-absent",     element_parse_required,            NULL, NULL },
2960         { "direction",           element_parse_direction,           NULL, NULL },
2961         { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
2962         { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
2963         { NULL, NULL, NULL, NULL }
2964     };
2965 
2966     pa_assert(fname);
2967 
2968     p = pa_xnew0(pa_alsa_path, 1);
2969     n = pa_path_get_filename(fname);
2970     p->name = pa_xstrndup(n, strcspn(n, "."));
2971     p->proplist = pa_proplist_new();
2972     p->direction = direction;
2973     p->eld_device = -1;
2974 
2975     items[0].data = &p->priority;
2976     items[1].data = &p->description_key;
2977     items[2].data = &p->description;
2978     items[3].data = &mute_during_activation;
2979 
2980     fn = get_path_config_path(paths_dir, fname);
2981 
2982     pa_log_info("Loading path config: %s", fn);
2983 
2984     r = pa_config_parse(fn, NULL, items, p->proplist, false, p);
2985     pa_xfree(fn);
2986 
2987     if (r < 0)
2988         goto fail;
2989 
2990     p->mute_during_activation = mute_during_activation;
2991 
2992     if (path_verify(p) < 0)
2993         goto fail;
2994 
2995     return p;
2996 
2997 fail:
2998     pa_alsa_path_free(p);
2999     return NULL;
3000 }
3001 
pa_alsa_path_synthesize(const char * element,pa_alsa_direction_t direction)3002 pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
3003     pa_alsa_path *p;
3004     pa_alsa_element *e;
3005     char *name;
3006     int index;
3007 
3008     pa_assert(element);
3009 
3010     name = alloca(strlen(element) + 1);
3011     if (alsa_id_decode(element, name, &index))
3012         return NULL;
3013 
3014     p = pa_xnew0(pa_alsa_path, 1);
3015     p->name = pa_xstrdup(element);
3016     p->direction = direction;
3017     p->proplist = pa_proplist_new();
3018 
3019     e = pa_xnew0(pa_alsa_element, 1);
3020     e->path = p;
3021     e->alsa_id.name = pa_xstrdup(name);
3022     e->alsa_id.index = index;
3023     e->direction = direction;
3024     e->volume_limit = -1;
3025 
3026     e->switch_use = PA_ALSA_SWITCH_MUTE;
3027     e->volume_use = PA_ALSA_VOLUME_MERGE;
3028 
3029     PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
3030     p->last_element = e;
3031     return p;
3032 }
3033 
element_drop_unsupported(pa_alsa_element * e)3034 static bool element_drop_unsupported(pa_alsa_element *e) {
3035     pa_alsa_option *o, *n;
3036 
3037     pa_assert(e);
3038 
3039     for (o = e->options; o; o = n) {
3040         n = o->next;
3041 
3042         if (o->alsa_idx < 0) {
3043             PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
3044             option_free(o);
3045         }
3046     }
3047 
3048     return
3049         e->switch_use != PA_ALSA_SWITCH_IGNORE ||
3050         e->volume_use != PA_ALSA_VOLUME_IGNORE ||
3051         e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
3052 }
3053 
path_drop_unsupported(pa_alsa_path * p)3054 static void path_drop_unsupported(pa_alsa_path *p) {
3055     pa_alsa_element *e, *n;
3056 
3057     pa_assert(p);
3058 
3059     for (e = p->elements; e; e = n) {
3060         n = e->next;
3061 
3062         if (!element_drop_unsupported(e)) {
3063             PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
3064             element_free(e);
3065         }
3066     }
3067 }
3068 
path_make_options_unique(pa_alsa_path * p)3069 static void path_make_options_unique(pa_alsa_path *p) {
3070     pa_alsa_element *e;
3071     pa_alsa_option *o, *u;
3072 
3073     PA_LLIST_FOREACH(e, p->elements) {
3074         PA_LLIST_FOREACH(o, e->options) {
3075             unsigned i;
3076             char *m;
3077 
3078             for (u = o->next; u; u = u->next)
3079                 if (pa_streq(u->name, o->name))
3080                     break;
3081 
3082             if (!u)
3083                 continue;
3084 
3085             m = pa_xstrdup(o->name);
3086 
3087             /* OK, this name is not unique, hence let's rename */
3088             for (i = 1, u = o; u; u = u->next) {
3089                 char *nn, *nd;
3090 
3091                 if (!pa_streq(u->name, m))
3092                     continue;
3093 
3094                 nn = pa_sprintf_malloc("%s-%u", m, i);
3095                 pa_xfree(u->name);
3096                 u->name = nn;
3097 
3098                 nd = pa_sprintf_malloc("%s %u", u->description, i);
3099                 pa_xfree(u->description);
3100                 u->description = nd;
3101 
3102                 i++;
3103             }
3104 
3105             pa_xfree(m);
3106         }
3107     }
3108 }
3109 
element_create_settings(pa_alsa_element * e,pa_alsa_setting * template)3110 static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
3111     pa_alsa_option *o;
3112 
3113     for (; e; e = e->next)
3114         if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
3115             e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
3116             break;
3117 
3118     if (!e)
3119         return false;
3120 
3121     for (o = e->options; o; o = o->next) {
3122         pa_alsa_setting *s;
3123 
3124         if (template) {
3125             s = pa_xnewdup(pa_alsa_setting, template, 1);
3126             s->options = pa_idxset_copy(template->options, NULL);
3127             s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
3128             s->description =
3129                 (template->description[0] && o->description[0])
3130                 ? pa_sprintf_malloc("%s / %s", template->description, o->description)
3131                 : (template->description[0]
3132                    ? pa_xstrdup(template->description)
3133                    : pa_xstrdup(o->description));
3134 
3135             s->priority = PA_MAX(template->priority, o->priority);
3136         } else {
3137             s = pa_xnew0(pa_alsa_setting, 1);
3138             s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3139             s->name = pa_xstrdup(o->name);
3140             s->description = pa_xstrdup(o->description);
3141             s->priority = o->priority;
3142         }
3143 
3144         pa_idxset_put(s->options, o, NULL);
3145 
3146         if (element_create_settings(e->next, s))
3147             /* This is not a leaf, so let's get rid of it */
3148             setting_free(s);
3149         else {
3150             /* This is a leaf, so let's add it */
3151             PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
3152 
3153             e->path->last_setting = s;
3154         }
3155     }
3156 
3157     return true;
3158 }
3159 
path_create_settings(pa_alsa_path * p)3160 static void path_create_settings(pa_alsa_path *p) {
3161     pa_assert(p);
3162 
3163     element_create_settings(p->elements, NULL);
3164 }
3165 
pa_alsa_path_probe(pa_alsa_path * p,pa_alsa_mapping * mapping,snd_mixer_t * m,bool ignore_dB)3166 int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB) {
3167     pa_alsa_element *e;
3168     pa_alsa_jack *j;
3169     double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
3170     pa_channel_position_t t;
3171     pa_channel_position_mask_t path_volume_channels = 0;
3172     bool min_dB_set, max_dB_set;
3173     char buf[64];
3174 
3175     pa_assert(p);
3176     pa_assert(m);
3177 
3178     if (p->probed)
3179         return p->supported ? 0 : -1;
3180     p->probed = true;
3181 
3182     pa_zero(min_dB);
3183     pa_zero(max_dB);
3184 
3185     pa_log_debug("Probing path '%s'", p->name);
3186 
3187     PA_LLIST_FOREACH(j, p->jacks) {
3188         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &j->alsa_id);
3189         if (jack_probe(j, mapping, m) < 0) {
3190             p->supported = false;
3191             pa_log_debug("Probe of jack %s failed.", buf);
3192             return -1;
3193         }
3194         pa_log_debug("Probe of jack %s succeeded (%s)", buf, j->has_control ? "found!" : "not found");
3195     }
3196 
3197     PA_LLIST_FOREACH(e, p->elements) {
3198         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3199         if (element_probe(e, m) < 0) {
3200             p->supported = false;
3201             pa_log_debug("Probe of element %s failed.", buf);
3202             return -1;
3203         }
3204         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);
3205 
3206         if (ignore_dB)
3207             e->has_dB = false;
3208 
3209         if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
3210 
3211             if (!p->has_volume) {
3212                 p->min_volume = e->min_volume;
3213                 p->max_volume = e->max_volume;
3214             }
3215 
3216             if (e->has_dB) {
3217                 if (!p->has_volume) {
3218                     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
3219                         if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
3220                             min_dB[t] = e->min_dB;
3221                             max_dB[t] = e->max_dB;
3222                             path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
3223                         }
3224 
3225                     p->has_dB = true;
3226                 } else {
3227 
3228                     if (p->has_dB) {
3229                         for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
3230                             if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
3231                                 min_dB[t] += e->min_dB;
3232                                 max_dB[t] += e->max_dB;
3233                                 path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
3234                             }
3235                     } else {
3236                         /* Hmm, there's another element before us
3237                          * which cannot do dB volumes, so we we need
3238                          * to 'neutralize' this slider */
3239                         e->volume_use = PA_ALSA_VOLUME_ZERO;
3240                         pa_log_info("Zeroing volume of %s on path '%s'", buf, p->name);
3241                     }
3242                 }
3243             } else if (p->has_volume) {
3244                 /* We can't use this volume, so let's ignore it */
3245                 e->volume_use = PA_ALSA_VOLUME_IGNORE;
3246                 pa_log_info("Ignoring volume of %s on path '%s' (missing dB info)", buf, p->name);
3247             }
3248             p->has_volume = true;
3249         }
3250 
3251         if (e->switch_use == PA_ALSA_SWITCH_MUTE)
3252             p->has_mute = true;
3253     }
3254 
3255     if (p->has_req_any && !p->req_any_present) {
3256         p->supported = false;
3257         pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
3258         return -1;
3259     }
3260 
3261     path_drop_unsupported(p);
3262     path_make_options_unique(p);
3263     path_create_settings(p);
3264 
3265     p->supported = true;
3266 
3267     p->min_dB = INFINITY;
3268     min_dB_set = false;
3269     p->max_dB = -INFINITY;
3270     max_dB_set = false;
3271 
3272     for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
3273         if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
3274             if (p->min_dB > min_dB[t]) {
3275                 p->min_dB = min_dB[t];
3276                 min_dB_set = true;
3277             }
3278 
3279             if (p->max_dB < max_dB[t]) {
3280                 p->max_dB = max_dB[t];
3281                 max_dB_set = true;
3282             }
3283         }
3284     }
3285 
3286     /* this is probably a wrong prediction, but it should be safe */
3287     if (!min_dB_set)
3288         p->min_dB = -INFINITY;
3289     if (!max_dB_set)
3290         p->max_dB = 0;
3291 
3292     return 0;
3293 }
3294 
pa_alsa_setting_dump(pa_alsa_setting * s)3295 void pa_alsa_setting_dump(pa_alsa_setting *s) {
3296     pa_assert(s);
3297 
3298     pa_log_debug("Setting %s (%s) priority=%u",
3299                  s->name,
3300                  pa_strnull(s->description),
3301                  s->priority);
3302 }
3303 
pa_alsa_jack_dump(pa_alsa_jack * j)3304 void pa_alsa_jack_dump(pa_alsa_jack *j) {
3305     pa_assert(j);
3306 
3307     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");
3308 }
3309 
pa_alsa_option_dump(pa_alsa_option * o)3310 void pa_alsa_option_dump(pa_alsa_option *o) {
3311     pa_assert(o);
3312 
3313     pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
3314                  o->alsa_name,
3315                  pa_strnull(o->name),
3316                  pa_strnull(o->description),
3317                  o->alsa_idx,
3318                  o->priority);
3319 }
3320 
pa_alsa_element_dump(pa_alsa_element * e)3321 void pa_alsa_element_dump(pa_alsa_element *e) {
3322     char buf[64];
3323 
3324     pa_alsa_option *o;
3325     pa_assert(e);
3326 
3327     pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3328     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",
3329                  buf,
3330                  e->direction,
3331                  e->switch_use,
3332                  e->volume_use,
3333                  e->volume_limit,
3334                  e->enumeration_use,
3335                  e->required,
3336                  e->required_any,
3337                  e->required_absent,
3338                  (long long unsigned) e->merged_mask,
3339                  e->n_channels,
3340                  e->override_map);
3341 
3342     PA_LLIST_FOREACH(o, e->options)
3343         pa_alsa_option_dump(o);
3344 }
3345 
pa_alsa_path_dump(pa_alsa_path * p)3346 void pa_alsa_path_dump(pa_alsa_path *p) {
3347     pa_alsa_element *e;
3348     pa_alsa_jack *j;
3349     pa_alsa_setting *s;
3350     pa_assert(p);
3351 
3352     pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
3353                  "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
3354                  p->name,
3355                  pa_strnull(p->description),
3356                  p->direction,
3357                  p->priority,
3358                  pa_yes_no(p->probed),
3359                  pa_yes_no(p->supported),
3360                  pa_yes_no(p->has_mute),
3361                  pa_yes_no(p->has_volume),
3362                  pa_yes_no(p->has_dB),
3363                  p->min_volume, p->max_volume,
3364                  p->min_dB, p->max_dB);
3365 
3366     PA_LLIST_FOREACH(e, p->elements)
3367         pa_alsa_element_dump(e);
3368 
3369     PA_LLIST_FOREACH(j, p->jacks)
3370         pa_alsa_jack_dump(j);
3371 
3372     PA_LLIST_FOREACH(s, p->settings)
3373         pa_alsa_setting_dump(s);
3374 }
3375 
element_set_callback(pa_alsa_element * e,snd_mixer_t * m,snd_mixer_elem_callback_t cb,void * userdata)3376 static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3377     snd_mixer_selem_id_t *sid;
3378     snd_mixer_elem_t *me;
3379     char buf[64];
3380 
3381     pa_assert(e);
3382     pa_assert(m);
3383     pa_assert(cb);
3384 
3385     SELEM_INIT(sid, &e->alsa_id);
3386     if (!(me = snd_mixer_find_selem(m, sid))) {
3387         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
3388         pa_log_warn("Element %s seems to have disappeared.", buf);
3389         return;
3390     }
3391 
3392     snd_mixer_elem_set_callback(me, cb);
3393     snd_mixer_elem_set_callback_private(me, userdata);
3394 }
3395 
pa_alsa_path_set_callback(pa_alsa_path * p,snd_mixer_t * m,snd_mixer_elem_callback_t cb,void * userdata)3396 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3397     pa_alsa_element *e;
3398 
3399     pa_assert(p);
3400     pa_assert(m);
3401     pa_assert(cb);
3402 
3403     PA_LLIST_FOREACH(e, p->elements)
3404         element_set_callback(e, m, cb, userdata);
3405 }
3406 
pa_alsa_path_set_set_callback(pa_alsa_path_set * ps,snd_mixer_t * m,snd_mixer_elem_callback_t cb,void * userdata)3407 void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
3408     pa_alsa_path *p;
3409     void *state;
3410 
3411     pa_assert(ps);
3412     pa_assert(m);
3413     pa_assert(cb);
3414 
3415     PA_HASHMAP_FOREACH(p, ps->paths, state)
3416         pa_alsa_path_set_callback(p, m, cb, userdata);
3417 }
3418 
profile_set_get_path(pa_alsa_profile_set * ps,const char * path_name)3419 static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
3420     pa_alsa_path *path;
3421 
3422     pa_assert(ps);
3423     pa_assert(path_name);
3424 
3425     if ((path = pa_hashmap_get(ps->output_paths, path_name)))
3426         return path;
3427 
3428     return pa_hashmap_get(ps->input_paths, path_name);
3429 }
3430 
profile_set_add_path(pa_alsa_profile_set * ps,pa_alsa_path * path)3431 static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
3432     pa_assert(ps);
3433     pa_assert(path);
3434 
3435     switch (path->direction) {
3436         case PA_ALSA_DIRECTION_OUTPUT:
3437             pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
3438             break;
3439 
3440         case PA_ALSA_DIRECTION_INPUT:
3441             pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
3442             break;
3443 
3444         default:
3445             pa_assert_not_reached();
3446     }
3447 }
3448 
pa_alsa_path_set_new(pa_alsa_mapping * m,pa_alsa_direction_t direction,const char * paths_dir)3449 pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
3450     pa_alsa_path_set *ps;
3451     char **pn = NULL, **en = NULL, **ie;
3452     pa_alsa_decibel_fix *db_fix;
3453     void *state, *state2;
3454     char name[64];
3455     int index;
3456 
3457     pa_assert(m);
3458     pa_assert(m->profile_set);
3459     pa_assert(m->profile_set->decibel_fixes);
3460     pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
3461 
3462     if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
3463         return NULL;
3464 
3465     ps = pa_xnew0(pa_alsa_path_set, 1);
3466     ps->direction = direction;
3467     ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
3468 
3469     if (direction == PA_ALSA_DIRECTION_OUTPUT)
3470         pn = m->output_path_names;
3471     else
3472         pn = m->input_path_names;
3473 
3474     if (pn) {
3475         char **in;
3476 
3477         for (in = pn; *in; in++) {
3478             pa_alsa_path *p = NULL;
3479             bool duplicate = false;
3480             char **kn;
3481 
3482             for (kn = pn; kn < in; kn++)
3483                 if (pa_streq(*kn, *in)) {
3484                     duplicate = true;
3485                     break;
3486                 }
3487 
3488             if (duplicate)
3489                 continue;
3490 
3491             p = profile_set_get_path(m->profile_set, *in);
3492 
3493             if (p && p->direction != direction) {
3494                 pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
3495                 goto fail;
3496             }
3497 
3498             if (!p) {
3499                 char *fn = pa_sprintf_malloc("%s.conf", *in);
3500                 p = pa_alsa_path_new(paths_dir, fn, direction);
3501                 pa_xfree(fn);
3502                 if (p)
3503                     profile_set_add_path(m->profile_set, p);
3504             }
3505 
3506             if (p)
3507                 pa_hashmap_put(ps->paths, p, p);
3508 
3509         }
3510 
3511         goto finish;
3512     }
3513 
3514     if (direction == PA_ALSA_DIRECTION_OUTPUT)
3515         en = m->output_element;
3516     else
3517         en = m->input_element;
3518 
3519     if (!en)
3520         goto fail;
3521 
3522     for (ie = en; *ie; ie++) {
3523         char **je;
3524         pa_alsa_path *p;
3525 
3526         p = pa_alsa_path_synthesize(*ie, direction);
3527 
3528         /* Mark all other passed elements for require-absent */
3529         for (je = en; *je; je++) {
3530             pa_alsa_element *e;
3531 
3532             if (je == ie)
3533                 continue;
3534 
3535             if (strlen(*je) + 1 >= sizeof(name)) {
3536                 pa_log("Element identifier %s is too long!", *je);
3537                 continue;
3538             }
3539 
3540             if (alsa_id_decode(*je, name, &index))
3541                 continue;
3542 
3543             e = pa_xnew0(pa_alsa_element, 1);
3544             e->path = p;
3545             e->alsa_id.name = pa_xstrdup(name);
3546             e->alsa_id.index = index;
3547             e->direction = direction;
3548             e->required_absent = PA_ALSA_REQUIRED_ANY;
3549             e->volume_limit = -1;
3550 
3551             PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
3552             p->last_element = e;
3553         }
3554 
3555         pa_hashmap_put(ps->paths, *ie, p);
3556     }
3557 
3558 finish:
3559     /* Assign decibel fixes to elements. */
3560     PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
3561         pa_alsa_path *p;
3562 
3563         PA_HASHMAP_FOREACH(p, ps->paths, state2) {
3564             pa_alsa_element *e;
3565 
3566             PA_LLIST_FOREACH(e, p->elements) {
3567                 if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_id.name) &&
3568                     db_fix->index == e->alsa_id.index) {
3569                     /* The profile set that contains the dB fix may be freed
3570                      * before the element, so we have to copy the dB fix
3571                      * object. */
3572                     e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
3573                     e->db_fix->profile_set = NULL;
3574                     e->db_fix->key = pa_xstrdup(db_fix->key);
3575                     e->db_fix->name = pa_xstrdup(db_fix->name);
3576                     e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
3577                 }
3578             }
3579         }
3580     }
3581 
3582     return ps;
3583 
3584 fail:
3585     if (ps)
3586         pa_alsa_path_set_free(ps);
3587 
3588     return NULL;
3589 }
3590 
pa_alsa_path_set_dump(pa_alsa_path_set * ps)3591 void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
3592     pa_alsa_path *p;
3593     void *state;
3594     pa_assert(ps);
3595 
3596     pa_log_debug("Path Set %p, direction=%i",
3597                  (void*) ps,
3598                  ps->direction);
3599 
3600     PA_HASHMAP_FOREACH(p, ps->paths, state)
3601         pa_alsa_path_dump(p);
3602 }
3603 
options_have_option(pa_alsa_option * options,const char * alsa_name)3604 static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
3605     pa_alsa_option *o;
3606 
3607     pa_assert(options);
3608     pa_assert(alsa_name);
3609 
3610     PA_LLIST_FOREACH(o, options) {
3611         if (pa_streq(o->alsa_name, alsa_name))
3612             return true;
3613     }
3614     return false;
3615 }
3616 
enumeration_is_subset(pa_alsa_option * a_options,pa_alsa_option * b_options)3617 static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
3618     pa_alsa_option *oa, *ob;
3619 
3620     if (!a_options) return true;
3621     if (!b_options) return false;
3622 
3623     /* If there is an option A offers that B does not, then A is not a subset of B. */
3624     PA_LLIST_FOREACH(oa, a_options) {
3625         bool found = false;
3626         PA_LLIST_FOREACH(ob, b_options) {
3627             if (pa_streq(oa->alsa_name, ob->alsa_name)) {
3628                 found = true;
3629                 break;
3630             }
3631         }
3632         if (!found)
3633             return false;
3634     }
3635     return true;
3636 }
3637 
3638 /**
3639  *  Compares two elements to see if a is a subset of b
3640  */
element_is_subset(pa_alsa_element * a,pa_alsa_element * b,snd_mixer_t * m)3641 static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
3642     char buf[64];
3643 
3644     pa_assert(a);
3645     pa_assert(b);
3646     pa_assert(m);
3647 
3648     /* General rules:
3649      * Every state is a subset of itself (with caveats for volume_limits and options)
3650      * IGNORE is a subset of every other state */
3651 
3652     /* Check the volume_use */
3653     if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
3654 
3655         /* "Constant" is subset of "Constant" only when their constant values are equal */
3656         if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
3657             return false;
3658 
3659         /* Different volume uses when b is not "Merge" means we are definitely not a subset */
3660         if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
3661             return false;
3662 
3663         /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
3664          * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
3665          * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
3666         if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
3667             long a_limit;
3668 
3669             if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
3670                 a_limit = a->constant_volume;
3671             else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
3672                 long dB = 0;
3673 
3674                 if (a->db_fix) {
3675                     int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
3676                     a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
3677                 } else {
3678                     snd_mixer_selem_id_t *sid;
3679                     snd_mixer_elem_t *me;
3680 
3681                     SELEM_INIT(sid, &a->alsa_id);
3682                     if (!(me = snd_mixer_find_selem(m, sid))) {
3683                         pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
3684                         pa_log_warn("Element %s seems to have disappeared.", buf);
3685                         return false;
3686                     }
3687 
3688                     if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
3689                         if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
3690                             return false;
3691                     } else {
3692                         if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
3693                             return false;
3694                     }
3695                 }
3696             } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
3697                 a_limit = a->min_volume;
3698             else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
3699                 a_limit = a->volume_limit;
3700             else
3701                 pa_assert_not_reached();
3702 
3703             if (a_limit > b->volume_limit)
3704                 return false;
3705         }
3706 
3707         if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
3708             int s;
3709             /* If override-maps are different, they're not subsets */
3710             if (a->n_channels != b->n_channels)
3711                 return false;
3712             for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
3713                 if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
3714                     pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
3715                     pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
3716                                  buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
3717                     return false;
3718                }
3719         }
3720     }
3721 
3722     if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
3723         /* "On" is a subset of "Mute".
3724          * "Off" is a subset of "Mute".
3725          * "On" is a subset of "Select", if there is an "Option:On" in B.
3726          * "Off" is a subset of "Select", if there is an "Option:Off" in B.
3727          * "Select" is a subset of "Select", if they have the same options (is this always true?). */
3728 
3729         if (a->switch_use != b->switch_use) {
3730 
3731             if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
3732                 || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
3733                 return false;
3734 
3735             if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
3736                 if (a->switch_use == PA_ALSA_SWITCH_ON) {
3737                     if (!options_have_option(b->options, "on"))
3738                         return false;
3739                 } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
3740                     if (!options_have_option(b->options, "off"))
3741                         return false;
3742                 }
3743             }
3744         } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
3745             if (!enumeration_is_subset(a->options, b->options))
3746                 return false;
3747         }
3748     }
3749 
3750     if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
3751         if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
3752             return false;
3753         if (!enumeration_is_subset(a->options, b->options))
3754             return false;
3755     }
3756 
3757     return true;
3758 }
3759 
path_set_condense(pa_alsa_path_set * ps,snd_mixer_t * m)3760 static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
3761     pa_alsa_path *p;
3762     void *state;
3763 
3764     pa_assert(ps);
3765     pa_assert(m);
3766 
3767     /* If we only have one path, then don't bother */
3768     if (pa_hashmap_size(ps->paths) < 2)
3769         return;
3770 
3771     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3772         pa_alsa_path *p2;
3773         void *state2;
3774 
3775         PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
3776             pa_alsa_element *ea, *eb;
3777             pa_alsa_jack *ja, *jb;
3778             bool is_subset = true;
3779 
3780             if (p == p2)
3781                 continue;
3782 
3783             /* If a has a jack that b does not have, a is not a subset */
3784             PA_LLIST_FOREACH(ja, p->jacks) {
3785                 bool exists = false;
3786 
3787                 if (!ja->has_control)
3788                     continue;
3789 
3790                 PA_LLIST_FOREACH(jb, p2->jacks) {
3791                     if (jb->has_control && pa_streq(ja->alsa_id.name, jb->alsa_id.name) &&
3792                        (ja->alsa_id.index == jb->alsa_id.index) &&
3793                        (ja->state_plugged == jb->state_plugged) &&
3794                        (ja->state_unplugged == jb->state_unplugged)) {
3795                         exists = true;
3796                         break;
3797                     }
3798                 }
3799 
3800                 if (!exists) {
3801                     is_subset = false;
3802                     break;
3803                 }
3804             }
3805 
3806             /* Compare the elements of each set... */
3807             PA_LLIST_FOREACH(ea, p->elements) {
3808                 bool found_matching_element = false;
3809 
3810                 if (!is_subset)
3811                     break;
3812 
3813                 PA_LLIST_FOREACH(eb, p2->elements) {
3814                     if (pa_streq(ea->alsa_id.name, eb->alsa_id.name) &&
3815                         ea->alsa_id.index == eb->alsa_id.index) {
3816                         found_matching_element = true;
3817                         is_subset = element_is_subset(ea, eb, m);
3818                         break;
3819                     }
3820                 }
3821 
3822                 if (!found_matching_element)
3823                     is_subset = false;
3824             }
3825 
3826             if (is_subset) {
3827                 pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
3828                 pa_hashmap_remove(ps->paths, p);
3829                 break;
3830             }
3831         }
3832     }
3833 }
3834 
path_set_find_path_by_description(pa_alsa_path_set * ps,const char * description,pa_alsa_path * ignore)3835 static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
3836     pa_alsa_path* p;
3837     void *state;
3838 
3839     PA_HASHMAP_FOREACH(p, ps->paths, state)
3840         if (p != ignore && pa_streq(p->description, description))
3841             return p;
3842 
3843     return NULL;
3844 }
3845 
path_set_make_path_descriptions_unique(pa_alsa_path_set * ps)3846 static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
3847     pa_alsa_path *p, *q;
3848     void *state, *state2;
3849 
3850     PA_HASHMAP_FOREACH(p, ps->paths, state) {
3851         unsigned i;
3852         char *old_description;
3853 
3854         q = path_set_find_path_by_description(ps, p->description, p);
3855 
3856         if (!q)
3857             continue;
3858 
3859         old_description = pa_xstrdup(p->description);
3860 
3861         /* OK, this description is not unique, hence let's rename */
3862         i = 1;
3863         PA_HASHMAP_FOREACH(q, ps->paths, state2) {
3864             char *new_description;
3865 
3866             if (!pa_streq(q->description, old_description))
3867                 continue;
3868 
3869             new_description = pa_sprintf_malloc("%s %u", q->description, i);
3870             pa_xfree(q->description);
3871             q->description = new_description;
3872 
3873             i++;
3874         }
3875 
3876         pa_xfree(old_description);
3877     }
3878 }
3879 
mapping_free(pa_alsa_mapping * m)3880 static void mapping_free(pa_alsa_mapping *m) {
3881     pa_assert(m);
3882 
3883     pa_xfree(m->name);
3884     pa_xfree(m->description);
3885     pa_xfree(m->description_key);
3886 
3887     pa_proplist_free(m->proplist);
3888 
3889     pa_xstrfreev(m->device_strings);
3890     pa_xstrfreev(m->input_path_names);
3891     pa_xstrfreev(m->output_path_names);
3892     pa_xstrfreev(m->input_element);
3893     pa_xstrfreev(m->output_element);
3894     if (m->input_path_set)
3895         pa_alsa_path_set_free(m->input_path_set);
3896     if (m->output_path_set)
3897         pa_alsa_path_set_free(m->output_path_set);
3898 
3899     pa_assert(!m->input_pcm);
3900     pa_assert(!m->output_pcm);
3901 
3902     pa_alsa_ucm_mapping_context_free(&m->ucm_context);
3903 
3904     pa_xfree(m);
3905 }
3906 
profile_free(pa_alsa_profile * p)3907 static void profile_free(pa_alsa_profile *p) {
3908     pa_assert(p);
3909 
3910     pa_xfree(p->name);
3911     pa_xfree(p->description);
3912     pa_xfree(p->description_key);
3913     pa_xfree(p->input_name);
3914     pa_xfree(p->output_name);
3915 
3916     pa_xstrfreev(p->input_mapping_names);
3917     pa_xstrfreev(p->output_mapping_names);
3918 
3919     if (p->input_mappings)
3920         pa_idxset_free(p->input_mappings, NULL);
3921 
3922     if (p->output_mappings)
3923         pa_idxset_free(p->output_mappings, NULL);
3924 
3925     pa_xfree(p);
3926 }
3927 
pa_alsa_profile_set_free(pa_alsa_profile_set * ps)3928 void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
3929     pa_assert(ps);
3930 
3931     if (ps->input_paths)
3932         pa_hashmap_free(ps->input_paths);
3933 
3934     if (ps->output_paths)
3935         pa_hashmap_free(ps->output_paths);
3936 
3937     if (ps->profiles)
3938         pa_hashmap_free(ps->profiles);
3939 
3940     if (ps->mappings)
3941         pa_hashmap_free(ps->mappings);
3942 
3943     if (ps->decibel_fixes)
3944         pa_hashmap_free(ps->decibel_fixes);
3945 
3946     pa_xfree(ps);
3947 }
3948 
pa_alsa_mapping_get(pa_alsa_profile_set * ps,const char * name)3949 pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
3950     pa_alsa_mapping *m;
3951 
3952     if (!pa_startswith(name, "Mapping "))
3953         return NULL;
3954 
3955     name += 8;
3956 
3957     if ((m = pa_hashmap_get(ps->mappings, name)))
3958         return m;
3959 
3960     m = pa_xnew0(pa_alsa_mapping, 1);
3961     m->profile_set = ps;
3962     m->exact_channels = true;
3963     m->name = pa_xstrdup(name);
3964     pa_sample_spec_init(&m->sample_spec);
3965     pa_channel_map_init(&m->channel_map);
3966     m->proplist = pa_proplist_new();
3967     m->hw_device_index = -1;
3968 
3969     pa_hashmap_put(ps->mappings, m->name, m);
3970 
3971     return m;
3972 }
3973 
profile_get(pa_alsa_profile_set * ps,const char * name)3974 static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
3975     pa_alsa_profile *p;
3976 
3977     if (!pa_startswith(name, "Profile "))
3978         return NULL;
3979 
3980     name += 8;
3981 
3982     if ((p = pa_hashmap_get(ps->profiles, name)))
3983         return p;
3984 
3985     p = pa_xnew0(pa_alsa_profile, 1);
3986     p->profile_set = ps;
3987     p->name = pa_xstrdup(name);
3988 
3989     pa_hashmap_put(ps->profiles, p->name, p);
3990 
3991     return p;
3992 }
3993 
decibel_fix_get(pa_alsa_profile_set * ps,const char * alsa_id)3994 static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *alsa_id) {
3995     pa_alsa_decibel_fix *db_fix;
3996     char *name;
3997     int index;
3998 
3999     if (!pa_startswith(alsa_id, "DecibelFix "))
4000         return NULL;
4001 
4002     alsa_id += 11;
4003 
4004     if ((db_fix = pa_hashmap_get(ps->decibel_fixes, alsa_id)))
4005         return db_fix;
4006 
4007     name = alloca(strlen(alsa_id) + 1);
4008     if (alsa_id_decode(alsa_id, name, &index))
4009         return NULL;
4010 
4011     db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
4012     db_fix->profile_set = ps;
4013     db_fix->name = pa_xstrdup(name);
4014     db_fix->index = index;
4015     db_fix->key = pa_xstrdup(alsa_id);
4016 
4017     pa_hashmap_put(ps->decibel_fixes, db_fix->key, db_fix);
4018 
4019     return db_fix;
4020 }
4021 
mapping_parse_device_strings(pa_config_parser_state * state)4022 static int mapping_parse_device_strings(pa_config_parser_state *state) {
4023     pa_alsa_profile_set *ps;
4024     pa_alsa_mapping *m;
4025 
4026     pa_assert(state);
4027 
4028     ps = state->userdata;
4029 
4030     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4031         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4032         return -1;
4033     }
4034 
4035     pa_xstrfreev(m->device_strings);
4036     if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
4037         pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
4038         return -1;
4039     }
4040 
4041     return 0;
4042 }
4043 
mapping_parse_channel_map(pa_config_parser_state * state)4044 static int mapping_parse_channel_map(pa_config_parser_state *state) {
4045     pa_alsa_profile_set *ps;
4046     pa_alsa_mapping *m;
4047 
4048     pa_assert(state);
4049 
4050     ps = state->userdata;
4051 
4052     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4053         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4054         return -1;
4055     }
4056 
4057     if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
4058         pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
4059         return -1;
4060     }
4061 
4062     return 0;
4063 }
4064 
mapping_parse_paths(pa_config_parser_state * state)4065 static int mapping_parse_paths(pa_config_parser_state *state) {
4066     pa_alsa_profile_set *ps;
4067     pa_alsa_mapping *m;
4068 
4069     pa_assert(state);
4070 
4071     ps = state->userdata;
4072 
4073     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4074         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4075         return -1;
4076     }
4077 
4078     if (pa_streq(state->lvalue, "paths-input")) {
4079         pa_xstrfreev(m->input_path_names);
4080         m->input_path_names = pa_split_spaces_strv(state->rvalue);
4081     } else {
4082         pa_xstrfreev(m->output_path_names);
4083         m->output_path_names = pa_split_spaces_strv(state->rvalue);
4084     }
4085 
4086     return 0;
4087 }
4088 
mapping_parse_exact_channels(pa_config_parser_state * state)4089 static int mapping_parse_exact_channels(pa_config_parser_state *state) {
4090     pa_alsa_profile_set *ps;
4091     pa_alsa_mapping *m;
4092     int b;
4093 
4094     pa_assert(state);
4095 
4096     ps = state->userdata;
4097 
4098     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4099         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4100         return -1;
4101     }
4102 
4103     if ((b = pa_parse_boolean(state->rvalue)) < 0) {
4104         pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
4105         return -1;
4106     }
4107 
4108     m->exact_channels = b;
4109 
4110     return 0;
4111 }
4112 
mapping_parse_element(pa_config_parser_state * state)4113 static int mapping_parse_element(pa_config_parser_state *state) {
4114     pa_alsa_profile_set *ps;
4115     pa_alsa_mapping *m;
4116 
4117     pa_assert(state);
4118 
4119     ps = state->userdata;
4120 
4121     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4122         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4123         return -1;
4124     }
4125 
4126     if (pa_streq(state->lvalue, "element-input")) {
4127         pa_xstrfreev(m->input_element);
4128         m->input_element = pa_split_spaces_strv(state->rvalue);
4129     } else {
4130         pa_xstrfreev(m->output_element);
4131         m->output_element = pa_split_spaces_strv(state->rvalue);
4132     }
4133 
4134     return 0;
4135 }
4136 
mapping_parse_direction(pa_config_parser_state * state)4137 static int mapping_parse_direction(pa_config_parser_state *state) {
4138     pa_alsa_profile_set *ps;
4139     pa_alsa_mapping *m;
4140 
4141     pa_assert(state);
4142 
4143     ps = state->userdata;
4144 
4145     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4146         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4147         return -1;
4148     }
4149 
4150     if (pa_streq(state->rvalue, "input"))
4151         m->direction = PA_ALSA_DIRECTION_INPUT;
4152     else if (pa_streq(state->rvalue, "output"))
4153         m->direction = PA_ALSA_DIRECTION_OUTPUT;
4154     else if (pa_streq(state->rvalue, "any"))
4155         m->direction = PA_ALSA_DIRECTION_ANY;
4156     else {
4157         pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
4158         return -1;
4159     }
4160 
4161     return 0;
4162 }
4163 
mapping_parse_description(pa_config_parser_state * state)4164 static int mapping_parse_description(pa_config_parser_state *state) {
4165     pa_alsa_profile_set *ps;
4166     pa_alsa_profile *p;
4167     pa_alsa_mapping *m;
4168 
4169     pa_assert(state);
4170 
4171     ps = state->userdata;
4172 
4173     if ((m = pa_alsa_mapping_get(ps, state->section))) {
4174         pa_xfree(m->description);
4175         m->description = pa_xstrdup(state->rvalue);
4176     } else if ((p = profile_get(ps, state->section))) {
4177         pa_xfree(p->description);
4178         p->description = pa_xstrdup(state->rvalue);
4179     } else {
4180         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4181         return -1;
4182     }
4183 
4184     return 0;
4185 }
4186 
mapping_parse_description_key(pa_config_parser_state * state)4187 static int mapping_parse_description_key(pa_config_parser_state *state) {
4188     pa_alsa_profile_set *ps;
4189     pa_alsa_profile *p;
4190     pa_alsa_mapping *m;
4191 
4192     pa_assert(state);
4193 
4194     ps = state->userdata;
4195 
4196     if ((m = pa_alsa_mapping_get(ps, state->section))) {
4197         pa_xfree(m->description_key);
4198         m->description_key = pa_xstrdup(state->rvalue);
4199     } else if ((p = profile_get(ps, state->section))) {
4200         pa_xfree(p->description_key);
4201         p->description_key = pa_xstrdup(state->rvalue);
4202     } else {
4203         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4204         return -1;
4205     }
4206 
4207     return 0;
4208 }
4209 
4210 
mapping_parse_priority(pa_config_parser_state * state)4211 static int mapping_parse_priority(pa_config_parser_state *state) {
4212     pa_alsa_profile_set *ps;
4213     pa_alsa_profile *p;
4214     pa_alsa_mapping *m;
4215     uint32_t prio;
4216 
4217     pa_assert(state);
4218 
4219     ps = state->userdata;
4220 
4221     if (pa_atou(state->rvalue, &prio) < 0) {
4222         pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
4223         return -1;
4224     }
4225 
4226     if ((m = pa_alsa_mapping_get(ps, state->section)))
4227         m->priority = prio;
4228     else if ((p = profile_get(ps, state->section)))
4229         p->priority = prio;
4230     else {
4231         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4232         return -1;
4233     }
4234 
4235     return 0;
4236 }
4237 
mapping_parse_fallback(pa_config_parser_state * state)4238 static int mapping_parse_fallback(pa_config_parser_state *state) {
4239     pa_alsa_profile_set *ps;
4240     pa_alsa_profile *p;
4241     pa_alsa_mapping *m;
4242     int k;
4243 
4244     pa_assert(state);
4245 
4246     ps = state->userdata;
4247 
4248     if ((k = pa_parse_boolean(state->rvalue)) < 0) {
4249         pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
4250         return -1;
4251     }
4252 
4253     if ((m = pa_alsa_mapping_get(ps, state->section)))
4254         m->fallback = k;
4255     else if ((p = profile_get(ps, state->section)))
4256         p->fallback_input = p->fallback_output = k;
4257     else {
4258         pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
4259         return -1;
4260     }
4261 
4262     return 0;
4263 }
4264 
mapping_parse_intended_roles(pa_config_parser_state * state)4265 static int mapping_parse_intended_roles(pa_config_parser_state *state) {
4266     pa_alsa_profile_set *ps;
4267     pa_alsa_mapping *m;
4268 
4269     pa_assert(state);
4270 
4271     ps = state->userdata;
4272 
4273     if (!(m = pa_alsa_mapping_get(ps, state->section))) {
4274         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4275         return -1;
4276     }
4277 
4278     pa_proplist_sets(m->proplist, PA_PROP_DEVICE_INTENDED_ROLES, state->rvalue);
4279 
4280     return 0;
4281 }
4282 
4283 
profile_parse_mappings(pa_config_parser_state * state)4284 static int profile_parse_mappings(pa_config_parser_state *state) {
4285     pa_alsa_profile_set *ps;
4286     pa_alsa_profile *p;
4287 
4288     pa_assert(state);
4289 
4290     ps = state->userdata;
4291 
4292     if (!(p = profile_get(ps, state->section))) {
4293         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4294         return -1;
4295     }
4296 
4297     if (pa_streq(state->lvalue, "input-mappings")) {
4298         pa_xstrfreev(p->input_mapping_names);
4299         p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
4300     } else {
4301         pa_xstrfreev(p->output_mapping_names);
4302         p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
4303     }
4304 
4305     return 0;
4306 }
4307 
profile_parse_skip_probe(pa_config_parser_state * state)4308 static int profile_parse_skip_probe(pa_config_parser_state *state) {
4309     pa_alsa_profile_set *ps;
4310     pa_alsa_profile *p;
4311     int b;
4312 
4313     pa_assert(state);
4314 
4315     ps = state->userdata;
4316 
4317     if (!(p = profile_get(ps, state->section))) {
4318         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4319         return -1;
4320     }
4321 
4322     if ((b = pa_parse_boolean(state->rvalue)) < 0) {
4323         pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
4324         return -1;
4325     }
4326 
4327     p->supported = b;
4328 
4329     return 0;
4330 }
4331 
decibel_fix_parse_db_values(pa_config_parser_state * state)4332 static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
4333     pa_alsa_profile_set *ps;
4334     pa_alsa_decibel_fix *db_fix;
4335     char **items;
4336     char *item;
4337     long *db_values;
4338     unsigned n = 8; /* Current size of the db_values table. */
4339     unsigned min_step = 0;
4340     unsigned max_step = 0;
4341     unsigned i = 0; /* Index to the items table. */
4342     unsigned prev_step = 0;
4343     double prev_db = 0;
4344 
4345     pa_assert(state);
4346 
4347     ps = state->userdata;
4348 
4349     if (!(db_fix = decibel_fix_get(ps, state->section))) {
4350         pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
4351         return -1;
4352     }
4353 
4354     if (!(items = pa_split_spaces_strv(state->rvalue))) {
4355         pa_log("[%s:%u] Value missing", state->filename, state->lineno);
4356         return -1;
4357     }
4358 
4359     db_values = pa_xnew(long, n);
4360 
4361     while ((item = items[i++])) {
4362         char *s = item; /* Step value string. */
4363         char *d = item; /* dB value string. */
4364         uint32_t step;
4365         double db;
4366 
4367         /* Move d forward until it points to a colon or to the end of the item. */
4368         for (; *d && *d != ':'; ++d);
4369 
4370         if (d == s) {
4371             /* item started with colon. */
4372             pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
4373             goto fail;
4374         }
4375 
4376         if (!*d || !*(d + 1)) {
4377             /* No colon found, or it was the last character in item. */
4378             pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
4379             goto fail;
4380         }
4381 
4382         /* pa_atou() needs a null-terminating string. Let's replace the colon
4383          * with a zero byte. */
4384         *d++ = '\0';
4385 
4386         if (pa_atou(s, &step) < 0) {
4387             pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
4388             goto fail;
4389         }
4390 
4391         if (pa_atod(d, &db) < 0) {
4392             pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
4393             goto fail;
4394         }
4395 
4396         if (step <= prev_step && i != 1) {
4397             pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
4398             goto fail;
4399         }
4400 
4401         if (db < prev_db && i != 1) {
4402             pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
4403             goto fail;
4404         }
4405 
4406         if (i == 1) {
4407             min_step = step;
4408             db_values[0] = (long) (db * 100.0);
4409             prev_step = step;
4410             prev_db = db;
4411         } else {
4412             /* Interpolate linearly. */
4413             double db_increment = (db - prev_db) / (step - prev_step);
4414 
4415             for (; prev_step < step; ++prev_step, prev_db += db_increment) {
4416 
4417                 /* Reallocate the db_values table if it's about to overflow. */
4418                 if (prev_step + 1 - min_step == n) {
4419                     n *= 2;
4420                     db_values = pa_xrenew(long, db_values, n);
4421                 }
4422 
4423                 db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
4424             }
4425         }
4426 
4427         max_step = step;
4428     }
4429 
4430     db_fix->min_step = min_step;
4431     db_fix->max_step = max_step;
4432     pa_xfree(db_fix->db_values);
4433     db_fix->db_values = db_values;
4434 
4435     pa_xstrfreev(items);
4436 
4437     return 0;
4438 
4439 fail:
4440     pa_xstrfreev(items);
4441     pa_xfree(db_values);
4442 
4443     return -1;
4444 }
4445 
4446 /* the logic is simple: if we see the jack in multiple paths */
4447 /* assign all those paths to one availability_group */
profile_set_set_availability_groups(pa_alsa_profile_set * ps)4448 static void profile_set_set_availability_groups(pa_alsa_profile_set *ps) {
4449     pa_dynarray *paths;
4450     pa_alsa_path *p;
4451     void *state;
4452     unsigned idx1;
4453     uint32_t num = 1;
4454 
4455     /* Merge ps->input_paths and ps->output_paths into one dynarray. */
4456     paths = pa_dynarray_new(NULL);
4457     PA_HASHMAP_FOREACH(p, ps->input_paths, state)
4458         pa_dynarray_append(paths, p);
4459     PA_HASHMAP_FOREACH(p, ps->output_paths, state)
4460         pa_dynarray_append(paths, p);
4461 
4462     PA_DYNARRAY_FOREACH(p, paths, idx1) {
4463         pa_alsa_jack *j;
4464         const char *found = NULL;
4465         bool has_control = false;
4466 
4467         PA_LLIST_FOREACH(j, p->jacks) {
4468             pa_alsa_path *p2;
4469             unsigned idx2;
4470 
4471             if (!j->has_control || j->state_plugged == PA_AVAILABLE_NO)
4472                 continue;
4473             has_control = true;
4474             PA_DYNARRAY_FOREACH(p2, paths, idx2) {
4475                 pa_alsa_jack *j2;
4476 
4477                 if (p2 == p)
4478                     break;
4479                 PA_LLIST_FOREACH(j2, p2->jacks) {
4480                     if (!j2->has_control || j2->state_plugged == PA_AVAILABLE_NO)
4481                         continue;
4482                     if (pa_streq(j->alsa_id.name, j2->alsa_id.name) &&
4483                         j->alsa_id.index == j2->alsa_id.index) {
4484                         j->state_plugged = PA_AVAILABLE_UNKNOWN;
4485                         j2->state_plugged = PA_AVAILABLE_UNKNOWN;
4486                         found = p2->availability_group;
4487                         break;
4488                     }
4489                 }
4490             }
4491             if (found)
4492                 break;
4493         }
4494         if (!has_control)
4495             continue;
4496         if (!found) {
4497             p->availability_group = pa_sprintf_malloc("Legacy %d", num);
4498         } else {
4499             p->availability_group = pa_xstrdup(found);
4500         }
4501         if (!found)
4502             num++;
4503     }
4504 
4505     pa_dynarray_free(paths);
4506 }
4507 
mapping_paths_probe(pa_alsa_mapping * m,pa_alsa_profile * profile,pa_alsa_direction_t direction,pa_hashmap * used_paths,pa_hashmap * mixers)4508 static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
4509                                 pa_alsa_direction_t direction, pa_hashmap *used_paths,
4510                                 pa_hashmap *mixers) {
4511 
4512     pa_alsa_path *p;
4513     void *state;
4514     snd_pcm_t *pcm_handle;
4515     pa_alsa_path_set *ps;
4516     snd_mixer_t *mixer_handle;
4517 
4518     if (direction == PA_ALSA_DIRECTION_OUTPUT) {
4519         if (m->output_path_set)
4520             return; /* Already probed */
4521         m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
4522         pcm_handle = m->output_pcm;
4523     } else {
4524         if (m->input_path_set)
4525             return; /* Already probed */
4526         m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
4527         pcm_handle = m->input_pcm;
4528     }
4529 
4530     if (!ps)
4531         return; /* No paths */
4532 
4533     pa_assert(pcm_handle);
4534 
4535     mixer_handle = pa_alsa_open_mixer_for_pcm(mixers, pcm_handle, true);
4536     if (!mixer_handle) {
4537         /* Cannot open mixer, remove all entries */
4538         pa_hashmap_remove_all(ps->paths);
4539         return;
4540     }
4541 
4542     PA_HASHMAP_FOREACH(p, ps->paths, state) {
4543         if (p->autodetect_eld_device)
4544             p->eld_device = m->hw_device_index;
4545 
4546         if (pa_alsa_path_probe(p, m, mixer_handle, m->profile_set->ignore_dB) < 0)
4547             pa_hashmap_remove(ps->paths, p);
4548     }
4549 
4550     path_set_condense(ps, mixer_handle);
4551     path_set_make_path_descriptions_unique(ps);
4552 
4553     PA_HASHMAP_FOREACH(p, ps->paths, state)
4554         pa_hashmap_put(used_paths, p, p);
4555 
4556     pa_log_debug("Available mixer paths (after tidying):");
4557     pa_alsa_path_set_dump(ps);
4558 }
4559 
mapping_verify(pa_alsa_mapping * m,const pa_channel_map * bonus)4560 static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
4561 
4562     static const struct description_map well_known_descriptions[] = {
4563         { "analog-mono",            N_("Analog Mono") },
4564         { "analog-mono-left",       N_("Analog Mono (Left)") },
4565         { "analog-mono-right",      N_("Analog Mono (Right)") },
4566         { "analog-stereo",          N_("Analog Stereo") },
4567         { "mono-fallback",          N_("Mono") },
4568         { "stereo-fallback",        N_("Stereo") },
4569         /* Note: Not translated to "Analog Stereo Input", because the source
4570          * name gets "Input" appended to it automatically, so adding "Input"
4571          * here would lead to the source name to become "Analog Stereo Input
4572          * Input". The same logic applies to analog-stereo-output,
4573          * multichannel-input and multichannel-output. */
4574         { "analog-stereo-input",    N_("Analog Stereo") },
4575         { "analog-stereo-output",   N_("Analog Stereo") },
4576         { "analog-stereo-headset",  N_("Headset") },
4577         { "analog-stereo-speakerphone",  N_("Speakerphone") },
4578         { "multichannel-input",     N_("Multichannel") },
4579         { "multichannel-output",    N_("Multichannel") },
4580         { "analog-surround-21",     N_("Analog Surround 2.1") },
4581         { "analog-surround-30",     N_("Analog Surround 3.0") },
4582         { "analog-surround-31",     N_("Analog Surround 3.1") },
4583         { "analog-surround-40",     N_("Analog Surround 4.0") },
4584         { "analog-surround-41",     N_("Analog Surround 4.1") },
4585         { "analog-surround-50",     N_("Analog Surround 5.0") },
4586         { "analog-surround-51",     N_("Analog Surround 5.1") },
4587         { "analog-surround-61",     N_("Analog Surround 6.0") },
4588         { "analog-surround-61",     N_("Analog Surround 6.1") },
4589         { "analog-surround-70",     N_("Analog Surround 7.0") },
4590         { "analog-surround-71",     N_("Analog Surround 7.1") },
4591         { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
4592         { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
4593         { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
4594         { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
4595         { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
4596         { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") },
4597         { "gaming-headset-chat",    N_("Chat") },
4598         { "gaming-headset-game",    N_("Game") },
4599     };
4600     const char *description_key = m->description_key ? m->description_key : m->name;
4601 
4602     pa_assert(m);
4603 
4604     if (!pa_channel_map_valid(&m->channel_map)) {
4605         pa_log("Mapping %s is missing channel map.", m->name);
4606         return -1;
4607     }
4608 
4609     if (!m->device_strings) {
4610         pa_log("Mapping %s is missing device strings.", m->name);
4611         return -1;
4612     }
4613 
4614     if ((m->input_path_names && m->input_element) ||
4615         (m->output_path_names && m->output_element)) {
4616         pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
4617         return -1;
4618     }
4619 
4620     if (!m->description)
4621         m->description = pa_xstrdup(lookup_description(description_key,
4622                                                        well_known_descriptions,
4623                                                        PA_ELEMENTSOF(well_known_descriptions)));
4624 
4625     if (!m->description)
4626         m->description = pa_xstrdup(m->name);
4627 
4628     if (bonus) {
4629         if (pa_channel_map_equal(&m->channel_map, bonus))
4630             m->priority += 50;
4631         else if (m->channel_map.channels == bonus->channels)
4632             m->priority += 30;
4633     }
4634 
4635     return 0;
4636 }
4637 
pa_alsa_mapping_dump(pa_alsa_mapping * m)4638 void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
4639     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
4640 
4641     pa_assert(m);
4642 
4643     pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
4644                  m->name,
4645                  pa_strnull(m->description),
4646                  m->priority,
4647                  pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
4648                  pa_yes_no(m->supported),
4649                  m->direction);
4650 }
4651 
profile_set_add_auto_pair(pa_alsa_profile_set * ps,pa_alsa_mapping * m,pa_alsa_mapping * n)4652 static void profile_set_add_auto_pair(
4653         pa_alsa_profile_set *ps,
4654         pa_alsa_mapping *m, /* output */
4655         pa_alsa_mapping *n  /* input */) {
4656 
4657     char *name;
4658     pa_alsa_profile *p;
4659 
4660     pa_assert(ps);
4661     pa_assert(m || n);
4662 
4663     if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
4664         return;
4665 
4666     if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
4667         return;
4668 
4669     if (m && n)
4670         name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
4671     else if (m)
4672         name = pa_sprintf_malloc("output:%s", m->name);
4673     else
4674         name = pa_sprintf_malloc("input:%s", n->name);
4675 
4676     if (pa_hashmap_get(ps->profiles, name)) {
4677         pa_xfree(name);
4678         return;
4679     }
4680 
4681     p = pa_xnew0(pa_alsa_profile, 1);
4682     p->profile_set = ps;
4683     p->name = name;
4684 
4685     if (m) {
4686         p->output_name = pa_xstrdup(m->name);
4687         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4688         pa_idxset_put(p->output_mappings, m, NULL);
4689         p->priority += m->priority * 100;
4690         p->fallback_output = m->fallback;
4691     }
4692 
4693     if (n) {
4694         p->input_name = pa_xstrdup(n->name);
4695         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4696         pa_idxset_put(p->input_mappings, n, NULL);
4697         p->priority += n->priority;
4698         p->fallback_input = n->fallback;
4699     }
4700 
4701     pa_hashmap_put(ps->profiles, p->name, p);
4702 }
4703 
profile_set_add_auto(pa_alsa_profile_set * ps)4704 static void profile_set_add_auto(pa_alsa_profile_set *ps) {
4705     pa_alsa_mapping *m, *n;
4706     void *m_state, *n_state;
4707 
4708     pa_assert(ps);
4709 
4710     /* The order is important here:
4711        1) try single inputs and outputs before trying their
4712           combination, because if the half-duplex test failed, we don't have
4713           to try full duplex.
4714        2) try the output right before the input combinations with
4715           that output, because then the output_pcm is not closed between tests.
4716     */
4717     PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4718         profile_set_add_auto_pair(ps, NULL, n);
4719 
4720     PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
4721         profile_set_add_auto_pair(ps, m, NULL);
4722 
4723         PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
4724             profile_set_add_auto_pair(ps, m, n);
4725     }
4726 
4727 }
4728 
profile_verify(pa_alsa_profile * p)4729 static int profile_verify(pa_alsa_profile *p) {
4730 
4731     static const struct description_map well_known_descriptions[] = {
4732         { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
4733         { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
4734         { "output:analog-stereo-headset+input:analog-stereo-headset", N_("Headset") },
4735         { "output:analog-stereo-speakerphone+input:analog-stereo-speakerphone", N_("Speakerphone") },
4736         { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
4737         { "output:multichannel-output+input:multichannel-input", N_("Multichannel Duplex") },
4738         { "output:unknown-stereo+input:unknown-stereo", N_("Stereo Duplex") },
4739         { "output:analog-output-surround71+output:analog-output-chat+input:analog-input", N_("Mono Chat + 7.1 Surround") },
4740         { "off",                                      N_("Off") }
4741     };
4742     const char *description_key = p->description_key ? p->description_key : p->name;
4743 
4744     pa_assert(p);
4745 
4746     /* Replace the output mapping names by the actual mappings */
4747     if (p->output_mapping_names) {
4748         char **name;
4749 
4750         pa_assert(!p->output_mappings);
4751         p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4752 
4753         for (name = p->output_mapping_names; *name; name++) {
4754             pa_alsa_mapping *m;
4755             char **in;
4756             bool duplicate = false;
4757 
4758             for (in = name + 1; *in; in++)
4759                 if (pa_streq(*name, *in)) {
4760                     duplicate = true;
4761                     break;
4762                 }
4763 
4764             if (duplicate)
4765                 continue;
4766 
4767             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
4768                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4769                 return -1;
4770             }
4771 
4772             pa_idxset_put(p->output_mappings, m, NULL);
4773 
4774             if (p->supported)
4775                 m->supported++;
4776         }
4777 
4778         pa_xstrfreev(p->output_mapping_names);
4779         p->output_mapping_names = NULL;
4780     }
4781 
4782     /* Replace the input mapping names by the actual mappings */
4783     if (p->input_mapping_names) {
4784         char **name;
4785 
4786         pa_assert(!p->input_mappings);
4787         p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
4788 
4789         for (name = p->input_mapping_names; *name; name++) {
4790             pa_alsa_mapping *m;
4791             char **in;
4792             bool duplicate = false;
4793 
4794             for (in = name + 1; *in; in++)
4795                 if (pa_streq(*name, *in)) {
4796                     duplicate = true;
4797                     break;
4798                 }
4799 
4800             if (duplicate)
4801                 continue;
4802 
4803             if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
4804                 pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
4805                 return -1;
4806             }
4807 
4808             pa_idxset_put(p->input_mappings, m, NULL);
4809 
4810             if (p->supported)
4811                 m->supported++;
4812         }
4813 
4814         pa_xstrfreev(p->input_mapping_names);
4815         p->input_mapping_names = NULL;
4816     }
4817 
4818     if (!p->input_mappings && !p->output_mappings) {
4819         pa_log("Profile '%s' lacks mappings.", p->name);
4820         return -1;
4821     }
4822 
4823     if (!p->description)
4824         p->description = pa_xstrdup(lookup_description(description_key,
4825                                                        well_known_descriptions,
4826                                                        PA_ELEMENTSOF(well_known_descriptions)));
4827 
4828     if (!p->description) {
4829         pa_strbuf *sb;
4830         uint32_t idx;
4831         pa_alsa_mapping *m;
4832 
4833         sb = pa_strbuf_new();
4834 
4835         if (p->output_mappings)
4836             PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
4837                 if (!pa_strbuf_isempty(sb))
4838                     pa_strbuf_puts(sb, " + ");
4839 
4840                 pa_strbuf_printf(sb, _("%s Output"), m->description);
4841             }
4842 
4843         if (p->input_mappings)
4844             PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
4845                 if (!pa_strbuf_isempty(sb))
4846                     pa_strbuf_puts(sb, " + ");
4847 
4848                 pa_strbuf_printf(sb, _("%s Input"), m->description);
4849             }
4850 
4851         p->description = pa_strbuf_to_string_free(sb);
4852     }
4853 
4854     return 0;
4855 }
4856 
pa_alsa_profile_dump(pa_alsa_profile * p)4857 void pa_alsa_profile_dump(pa_alsa_profile *p) {
4858     uint32_t idx;
4859     pa_alsa_mapping *m;
4860     pa_assert(p);
4861 
4862     pa_log_debug("Profile %s (%s), input=%s, output=%s priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
4863                  p->name,
4864                  pa_strnull(p->description),
4865                  pa_strnull(p->input_name),
4866                  pa_strnull(p->output_name),
4867                  p->priority,
4868                  pa_yes_no(p->supported),
4869                  p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
4870                  p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
4871 
4872     if (p->input_mappings)
4873         PA_IDXSET_FOREACH(m, p->input_mappings, idx)
4874             pa_log_debug("Input %s", m->name);
4875 
4876     if (p->output_mappings)
4877         PA_IDXSET_FOREACH(m, p->output_mappings, idx)
4878             pa_log_debug("Output %s", m->name);
4879 }
4880 
decibel_fix_verify(pa_alsa_decibel_fix * db_fix)4881 static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
4882     pa_assert(db_fix);
4883 
4884     /* Check that the dB mapping has been configured. Since "db-values" is
4885      * currently the only option in the DecibelFix section, and decibel fix
4886      * objects don't get created if a DecibelFix section is empty, this is
4887      * actually a redundant check. Having this may prevent future bugs,
4888      * however. */
4889     if (!db_fix->db_values) {
4890         pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
4891         return -1;
4892     }
4893 
4894     return 0;
4895 }
4896 
pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix * db_fix)4897 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
4898     char *db_values = NULL;
4899 
4900     pa_assert(db_fix);
4901 
4902     if (db_fix->db_values) {
4903         pa_strbuf *buf;
4904         unsigned long i, nsteps;
4905 
4906         pa_assert(db_fix->min_step <= db_fix->max_step);
4907         nsteps = db_fix->max_step - db_fix->min_step + 1;
4908 
4909         buf = pa_strbuf_new();
4910         for (i = 0; i < nsteps; ++i)
4911             pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
4912 
4913         db_values = pa_strbuf_to_string_free(buf);
4914     }
4915 
4916     pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
4917                  db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
4918 
4919     pa_xfree(db_values);
4920 }
4921 
pa_alsa_profile_set_new(const char * fname,const pa_channel_map * bonus)4922 pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
4923     pa_alsa_profile_set *ps;
4924     pa_alsa_profile *p;
4925     pa_alsa_mapping *m;
4926     pa_alsa_decibel_fix *db_fix;
4927     char *fn;
4928     int r;
4929     void *state;
4930 
4931     static pa_config_item items[] = {
4932         /* [General] */
4933         { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
4934 
4935         /* [Mapping ...] */
4936         { "device-strings",         mapping_parse_device_strings, NULL, NULL },
4937         { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
4938         { "paths-input",            mapping_parse_paths,          NULL, NULL },
4939         { "paths-output",           mapping_parse_paths,          NULL, NULL },
4940         { "element-input",          mapping_parse_element,        NULL, NULL },
4941         { "element-output",         mapping_parse_element,        NULL, NULL },
4942         { "direction",              mapping_parse_direction,      NULL, NULL },
4943         { "exact-channels",         mapping_parse_exact_channels, NULL, NULL },
4944         { "intended-roles",         mapping_parse_intended_roles, NULL, NULL },
4945 
4946         /* Shared by [Mapping ...] and [Profile ...] */
4947         { "description",            mapping_parse_description,    NULL, NULL },
4948         { "description-key",        mapping_parse_description_key,NULL, NULL },
4949         { "priority",               mapping_parse_priority,       NULL, NULL },
4950         { "fallback",               mapping_parse_fallback,       NULL, NULL },
4951 
4952         /* [Profile ...] */
4953         { "input-mappings",         profile_parse_mappings,       NULL, NULL },
4954         { "output-mappings",        profile_parse_mappings,       NULL, NULL },
4955         { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
4956 
4957         /* [DecibelFix ...] */
4958         { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
4959         { NULL, NULL, NULL, NULL }
4960     };
4961 
4962     ps = pa_xnew0(pa_alsa_profile_set, 1);
4963     ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
4964     ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
4965     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);
4966     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);
4967     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);
4968 
4969     items[0].data = &ps->auto_profiles;
4970 
4971     if (!fname)
4972         fname = "default.conf";
4973 
4974     fn = pa_maybe_prefix_path(fname,
4975 #ifdef HAVE_RUNNING_FROM_BUILD_TREE
4976                               pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
4977 #endif
4978                               PA_ALSA_PROFILE_SETS_DIR);
4979 
4980     r = pa_config_parse(fn, NULL, items, NULL, false, ps);
4981     pa_xfree(fn);
4982 
4983     if (r < 0)
4984         goto fail;
4985 
4986     PA_HASHMAP_FOREACH(m, ps->mappings, state)
4987         if (mapping_verify(m, bonus) < 0)
4988             goto fail;
4989 
4990     if (ps->auto_profiles)
4991         profile_set_add_auto(ps);
4992 
4993     PA_HASHMAP_FOREACH(p, ps->profiles, state)
4994         if (profile_verify(p) < 0)
4995             goto fail;
4996 
4997     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
4998         if (decibel_fix_verify(db_fix) < 0)
4999             goto fail;
5000 
5001     return ps;
5002 
5003 fail:
5004     pa_alsa_profile_set_free(ps);
5005     return NULL;
5006 }
5007 
profile_finalize_probing(pa_alsa_profile * to_be_finalized,pa_alsa_profile * next)5008 static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
5009     pa_alsa_mapping *m;
5010     uint32_t idx;
5011 
5012     if (!to_be_finalized)
5013         return;
5014 
5015     if (to_be_finalized->output_mappings)
5016         PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
5017 
5018             if (!m->output_pcm)
5019                 continue;
5020 
5021             if (to_be_finalized->supported)
5022                 m->supported++;
5023 
5024             /* If this mapping is also in the next profile, we won't close the
5025              * pcm handle here, because it would get immediately reopened
5026              * anyway. */
5027             if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
5028                 continue;
5029 
5030             snd_pcm_close(m->output_pcm);
5031             m->output_pcm = NULL;
5032         }
5033 
5034     if (to_be_finalized->input_mappings)
5035         PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
5036 
5037             if (!m->input_pcm)
5038                 continue;
5039 
5040             if (to_be_finalized->supported)
5041                 m->supported++;
5042 
5043             /* If this mapping is also in the next profile, we won't close the
5044              * pcm handle here, because it would get immediately reopened
5045              * anyway. */
5046             if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
5047                 continue;
5048 
5049             snd_pcm_close(m->input_pcm);
5050             m->input_pcm = NULL;
5051         }
5052 }
5053 
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)5054 static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
5055                                    const pa_sample_spec *ss,
5056                                    const char *dev_id,
5057                                    bool exact_channels,
5058                                    int mode,
5059                                    unsigned default_n_fragments,
5060                                    unsigned default_fragment_size_msec) {
5061 
5062     snd_pcm_t* handle;
5063     pa_sample_spec try_ss = *ss;
5064     pa_channel_map try_map = m->channel_map;
5065     snd_pcm_uframes_t try_period_size, try_buffer_size;
5066 
5067     try_ss.channels = try_map.channels;
5068 
5069     try_period_size =
5070         pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
5071         pa_frame_size(&try_ss);
5072     try_buffer_size = default_n_fragments * try_period_size;
5073 
5074     handle = pa_alsa_open_by_template(
5075                               m->device_strings, dev_id, NULL, &try_ss,
5076                               &try_map, mode, &try_period_size,
5077                               &try_buffer_size, 0, NULL, NULL, exact_channels);
5078     if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
5079         char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
5080         pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
5081                      pa_channel_map_snprint(buf, sizeof(buf), &try_map));
5082         m->channel_map = try_map;
5083     }
5084     return handle;
5085 }
5086 
paths_drop_unused(pa_hashmap * h,pa_hashmap * keep)5087 static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) {
5088 
5089     void* state = NULL;
5090     const void* key;
5091     pa_alsa_path* p;
5092 
5093     pa_assert(h);
5094     pa_assert(keep);
5095 
5096     p = pa_hashmap_iterate(h, &state, &key);
5097     while (p) {
5098         if (pa_hashmap_get(keep, p) == NULL)
5099             pa_hashmap_remove_and_free(h, key);
5100         p = pa_hashmap_iterate(h, &state, &key);
5101     }
5102 }
5103 
add_profiles_to_probe(pa_alsa_profile ** list,pa_hashmap * profiles,bool fallback_output,bool fallback_input)5104 static int add_profiles_to_probe(
5105         pa_alsa_profile **list,
5106         pa_hashmap *profiles,
5107         bool fallback_output,
5108         bool fallback_input) {
5109 
5110     int i = 0;
5111     void *state;
5112     pa_alsa_profile *p;
5113     PA_HASHMAP_FOREACH(p, profiles, state)
5114         if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) {
5115             *list = p;
5116             list++;
5117             i++;
5118         }
5119     return i;
5120 }
5121 
mapping_query_hw_device(pa_alsa_mapping * mapping,snd_pcm_t * pcm)5122 static void mapping_query_hw_device(pa_alsa_mapping *mapping, snd_pcm_t *pcm) {
5123     int r;
5124     snd_pcm_info_t* pcm_info;
5125     snd_pcm_info_alloca(&pcm_info);
5126 
5127     r = snd_pcm_info(pcm, pcm_info);
5128     if (r < 0) {
5129         pa_log("Mapping %s: snd_pcm_info() failed %s: ", mapping->name, pa_alsa_strerror(r));
5130         return;
5131     }
5132 
5133     /* XXX: It's not clear what snd_pcm_info_get_device() does if the device is
5134      * not backed by a hw device or if it's backed by multiple hw devices. We
5135      * only use hw_device_index for HDMI devices, however, and for those the
5136      * return value is expected to be always valid, so this shouldn't be a
5137      * significant problem. */
5138     mapping->hw_device_index = snd_pcm_info_get_device(pcm_info);
5139 }
5140 
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)5141 void pa_alsa_profile_set_probe(
5142         pa_alsa_profile_set *ps,
5143         pa_hashmap *mixers,
5144         const char *dev_id,
5145         const pa_sample_spec *ss,
5146         unsigned default_n_fragments,
5147         unsigned default_fragment_size_msec) {
5148 
5149     bool found_output = false, found_input = false;
5150 
5151     pa_alsa_profile *p, *last = NULL;
5152     pa_alsa_profile **pp, **probe_order;
5153     pa_alsa_mapping *m;
5154     pa_hashmap *broken_inputs, *broken_outputs, *used_paths;
5155     pa_alsa_mapping *selected_fallback_input = NULL, *selected_fallback_output = NULL;
5156 
5157     pa_assert(ps);
5158     pa_assert(dev_id);
5159     pa_assert(ss);
5160 
5161     if (ps->probed)
5162         return;
5163 
5164     broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5165     broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5166     used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
5167     pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1);
5168 
5169     pp += add_profiles_to_probe(pp, ps->profiles, false, false);
5170     pp += add_profiles_to_probe(pp, ps->profiles, false, true);
5171     pp += add_profiles_to_probe(pp, ps->profiles, true, false);
5172     pp += add_profiles_to_probe(pp, ps->profiles, true, true);
5173 
5174     for (pp = probe_order; *pp; pp++) {
5175         uint32_t idx;
5176         p = *pp;
5177 
5178         /* Skip if fallback and already found something, but still probe already selected fallbacks.
5179          * If UCM is used then both fallback_input and fallback_output flags are false.
5180          * If UCM is not used then there will be only a single entry in mappings.
5181          */
5182         if (found_input && p->fallback_input)
5183             if (selected_fallback_input == NULL || pa_idxset_get_by_index(p->input_mappings, 0) != selected_fallback_input)
5184                 continue;
5185         if (found_output && p->fallback_output)
5186             if (selected_fallback_output == NULL || pa_idxset_get_by_index(p->output_mappings, 0) != selected_fallback_output)
5187                 continue;
5188 
5189         /* Skip if this is already marked that it is supported (i.e. from the config file) */
5190         if (!p->supported) {
5191 
5192             profile_finalize_probing(last, p);
5193             p->supported = true;
5194 
5195             if (p->output_mappings) {
5196                 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
5197                     if (pa_hashmap_get(broken_outputs, m) == m) {
5198                         pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
5199                         p->supported = false;
5200                         break;
5201                     }
5202                 }
5203             }
5204 
5205             if (p->input_mappings && p->supported) {
5206                 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
5207                     if (pa_hashmap_get(broken_inputs, m) == m) {
5208                         pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
5209                         p->supported = false;
5210                         break;
5211                     }
5212                 }
5213             }
5214 
5215             if (p->supported)
5216                 pa_log_debug("Looking at profile %s", p->name);
5217 
5218             /* Check if we can open all new ones */
5219             if (p->output_mappings && p->supported)
5220                 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
5221 
5222                     if (m->output_pcm)
5223                         continue;
5224 
5225                     pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
5226                     if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
5227                                                            SND_PCM_STREAM_PLAYBACK,
5228                                                            default_n_fragments,
5229                                                            default_fragment_size_msec))) {
5230                         p->supported = false;
5231                         if (pa_idxset_size(p->output_mappings) == 1 &&
5232                             ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
5233                             pa_log_debug("Caching failure to open output:%s", m->name);
5234                             pa_hashmap_put(broken_outputs, m, m);
5235                         }
5236                         break;
5237                     }
5238 
5239                     if (m->hw_device_index < 0)
5240                         mapping_query_hw_device(m, m->output_pcm);
5241                 }
5242 
5243             if (p->input_mappings && p->supported)
5244                 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
5245 
5246                     if (m->input_pcm)
5247                         continue;
5248 
5249                     pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
5250                     if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
5251                                                           SND_PCM_STREAM_CAPTURE,
5252                                                           default_n_fragments,
5253                                                           default_fragment_size_msec))) {
5254                         p->supported = false;
5255                         if (pa_idxset_size(p->input_mappings) == 1 &&
5256                             ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
5257                             pa_log_debug("Caching failure to open input:%s", m->name);
5258                             pa_hashmap_put(broken_inputs, m, m);
5259                         }
5260                         break;
5261                     }
5262 
5263                     if (m->hw_device_index < 0)
5264                         mapping_query_hw_device(m, m->input_pcm);
5265                 }
5266 
5267             last = p;
5268 
5269             if (!p->supported)
5270                 continue;
5271         }
5272 
5273         pa_log_debug("Profile %s supported.", p->name);
5274 
5275         if (p->output_mappings)
5276             PA_IDXSET_FOREACH(m, p->output_mappings, idx)
5277                 if (m->output_pcm) {
5278                     found_output = true;
5279                     if (p->fallback_output && selected_fallback_output == NULL) {
5280                         selected_fallback_output = m;
5281                     }
5282                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths, mixers);
5283                 }
5284 
5285         if (p->input_mappings)
5286             PA_IDXSET_FOREACH(m, p->input_mappings, idx)
5287                 if (m->input_pcm) {
5288                     found_input = true;
5289                     if (p->fallback_input && selected_fallback_input == NULL) {
5290                         selected_fallback_input = m;
5291                     }
5292                     mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths, mixers);
5293                 }
5294     }
5295 
5296     /* Clean up */
5297     profile_finalize_probing(last, NULL);
5298 
5299     pa_alsa_profile_set_drop_unsupported(ps);
5300 
5301     paths_drop_unused(ps->input_paths, used_paths);
5302     paths_drop_unused(ps->output_paths, used_paths);
5303     pa_hashmap_free(broken_inputs);
5304     pa_hashmap_free(broken_outputs);
5305     pa_hashmap_free(used_paths);
5306     pa_xfree(probe_order);
5307 
5308     profile_set_set_availability_groups(ps);
5309 
5310     ps->probed = true;
5311 }
5312 
pa_alsa_profile_set_dump(pa_alsa_profile_set * ps)5313 void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
5314     pa_alsa_profile *p;
5315     pa_alsa_mapping *m;
5316     pa_alsa_decibel_fix *db_fix;
5317     void *state;
5318 
5319     pa_assert(ps);
5320 
5321     pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
5322                  (void*)
5323                  ps,
5324                  pa_yes_no(ps->auto_profiles),
5325                  pa_yes_no(ps->probed),
5326                  pa_hashmap_size(ps->mappings),
5327                  pa_hashmap_size(ps->profiles),
5328                  pa_hashmap_size(ps->decibel_fixes));
5329 
5330     PA_HASHMAP_FOREACH(m, ps->mappings, state)
5331         pa_alsa_mapping_dump(m);
5332 
5333     PA_HASHMAP_FOREACH(p, ps->profiles, state)
5334         pa_alsa_profile_dump(p);
5335 
5336     PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
5337         pa_alsa_decibel_fix_dump(db_fix);
5338 }
5339 
pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set * ps)5340 void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
5341     pa_alsa_profile *p;
5342     pa_alsa_mapping *m;
5343     void *state;
5344 
5345     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
5346         if (!p->supported)
5347             pa_hashmap_remove_and_free(ps->profiles, p->name);
5348     }
5349 
5350     PA_HASHMAP_FOREACH(m, ps->mappings, state) {
5351         if (m->supported <= 0)
5352             pa_hashmap_remove_and_free(ps->mappings, m->name);
5353     }
5354 }
5355 
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)5356 static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
5357     const char* name,
5358     const char* description,
5359     pa_alsa_path *path,
5360     pa_alsa_setting *setting,
5361     pa_card_profile *cp,
5362     pa_hashmap *extra, /* sink/source ports */
5363     pa_core *core) {
5364 
5365     pa_device_port *p;
5366 
5367     pa_assert(path);
5368 
5369     p = pa_hashmap_get(ports, name);
5370 
5371     if (!p) {
5372         pa_alsa_port_data *data;
5373         pa_device_port_new_data port_data;
5374 
5375         pa_device_port_new_data_init(&port_data);
5376         pa_device_port_new_data_set_name(&port_data, name);
5377         pa_device_port_new_data_set_description(&port_data, description);
5378         pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
5379         pa_device_port_new_data_set_type(&port_data, path->device_port_type);
5380         pa_device_port_new_data_set_availability_group(&port_data, path->availability_group);
5381 
5382         p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
5383         pa_device_port_new_data_done(&port_data);
5384         pa_assert(p);
5385         pa_hashmap_put(ports, p->name, p);
5386         pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
5387 
5388         data = PA_DEVICE_PORT_DATA(p);
5389         /* Ownership of the path and setting is not transferred to the port data, so we don't deal with freeing them */
5390         data->path = path;
5391         data->setting = setting;
5392         path->port = p;
5393     }
5394 
5395     if (cp)
5396         pa_hashmap_put(p->profiles, cp->name, cp);
5397 
5398     if (extra) {
5399         pa_hashmap_put(extra, p->name, p);
5400         pa_device_port_ref(p);
5401     }
5402 
5403     return p;
5404 }
5405 
pa_alsa_path_set_add_ports(pa_alsa_path_set * ps,pa_card_profile * cp,pa_hashmap * ports,pa_hashmap * extra,pa_core * core)5406 void pa_alsa_path_set_add_ports(
5407         pa_alsa_path_set *ps,
5408         pa_card_profile *cp,
5409         pa_hashmap *ports, /* card ports */
5410         pa_hashmap *extra, /* sink/source ports */
5411         pa_core *core) {
5412 
5413     pa_alsa_path *path;
5414     void *state;
5415 
5416     pa_assert(ports);
5417 
5418     if (!ps)
5419         return;
5420 
5421     PA_HASHMAP_FOREACH(path, ps->paths, state) {
5422         if (!path->settings || !path->settings->next) {
5423             /* If there is no or just one setting we only need a
5424              * single entry */
5425             pa_device_port *port = device_port_alsa_init(ports, path->name,
5426                 path->description, path, path->settings, cp, extra, core);
5427             port->priority = path->priority * 100;
5428 
5429         } else {
5430             pa_alsa_setting *s;
5431             PA_LLIST_FOREACH(s, path->settings) {
5432                 pa_device_port *port;
5433                 char *n, *d;
5434 
5435                 n = pa_sprintf_malloc("%s;%s", path->name, s->name);
5436 
5437                 if (s->description[0])
5438                     d = pa_sprintf_malloc("%s / %s", path->description, s->description);
5439                 else
5440                     d = pa_xstrdup(path->description);
5441 
5442                 port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
5443                 port->priority = path->priority * 100 + s->priority;
5444 
5445                 pa_xfree(n);
5446                 pa_xfree(d);
5447             }
5448         }
5449     }
5450 }
5451 
pa_alsa_add_ports(void * sink_or_source_new_data,pa_alsa_path_set * ps,pa_card * card)5452 void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
5453     pa_hashmap *ports;
5454 
5455     pa_assert(sink_or_source_new_data);
5456     pa_assert(ps);
5457 
5458     if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
5459         ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
5460     else
5461         ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
5462 
5463     if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
5464         pa_assert(card);
5465         pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
5466     }
5467 
5468     pa_log_debug("Added %u ports", pa_hashmap_size(ports));
5469 }
5470