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