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