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