1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2011 Wolfson Microelectronics PLC 5 Author Margarita Olaya <magi@slimlogic.co.uk> 6 Copyright 2012 Feng Wei <wei.feng@freescale.com>, Freescale Ltd. 7 8 PulseAudio is free software; you can redistribute it and/or modify 9 it under the terms of the GNU Lesser General Public License as published 10 by the Free Software Foundation; either version 2.1 of the License, 11 or (at your option) any later version. 12 13 PulseAudio is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public License 19 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 20 21***/ 22 23#ifdef HAVE_CONFIG_H 24#include <config.h> 25#endif 26 27#include <ctype.h> 28#include <sys/types.h> 29#include <limits.h> 30#include <alsa/asoundlib.h> 31 32#ifdef HAVE_VALGRIND_MEMCHECK_H 33#include <valgrind/memcheck.h> 34#endif 35 36#include <pulse/sample.h> 37#include <pulse/xmalloc.h> 38#include <pulse/timeval.h> 39#include <pulse/util.h> 40 41#include <pulsecore/log.h> 42#include <pulsecore/macro.h> 43#include <pulsecore/core-util.h> 44#include <pulsecore/atomic.h> 45#include <pulsecore/core-error.h> 46#include <pulsecore/once.h> 47#include <pulsecore/thread.h> 48#include <pulsecore/conf-parser.h> 49#include <pulsecore/strbuf.h> 50 51#include "alsa-mixer.h" 52#include "alsa-util.h" 53#include "alsa-ucm.h" 54 55#define PA_UCM_PRE_TAG_OUTPUT "[Out] " 56#define PA_UCM_PRE_TAG_INPUT "[In] " 57 58#define PA_UCM_PLAYBACK_PRIORITY_UNSET(device) ((device)->playback_channels && !(device)->playback_priority) 59#define PA_UCM_CAPTURE_PRIORITY_UNSET(device) ((device)->capture_channels && !(device)->capture_priority) 60#define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \ 61 do { \ 62 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \ 63 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \ 64 } while (0) 65#define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL) 66 67#ifdef HAVE_ALSA_UCM 68 69struct ucm_type { 70 const char *prefix; 71 pa_device_port_type_t type; 72}; 73 74struct ucm_items { 75 const char *id; 76 const char *property; 77}; 78 79struct ucm_info { 80 const char *id; 81 unsigned priority; 82}; 83 84static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device); 85static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack); 86static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack); 87 88static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name); 89 90 91static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, 92 pa_alsa_ucm_device **devices, unsigned n_devices); 93static void ucm_port_data_free(pa_device_port *port); 94static void ucm_port_update_available(pa_alsa_ucm_port_data *port); 95 96static struct ucm_type types[] = { 97 {"None", PA_DEVICE_PORT_TYPE_UNKNOWN}, 98 {"Speaker", PA_DEVICE_PORT_TYPE_SPEAKER}, 99 {"Line", PA_DEVICE_PORT_TYPE_LINE}, 100 {"Mic", PA_DEVICE_PORT_TYPE_MIC}, 101 {"Headphones", PA_DEVICE_PORT_TYPE_HEADPHONES}, 102 {"Headset", PA_DEVICE_PORT_TYPE_HEADSET}, 103 {"Handset", PA_DEVICE_PORT_TYPE_HANDSET}, 104 {"Bluetooth", PA_DEVICE_PORT_TYPE_BLUETOOTH}, 105 {"Earpiece", PA_DEVICE_PORT_TYPE_EARPIECE}, 106 {"SPDIF", PA_DEVICE_PORT_TYPE_SPDIF}, 107 {"HDMI", PA_DEVICE_PORT_TYPE_HDMI}, 108 {NULL, 0} 109}; 110 111static struct ucm_items item[] = { 112 {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK}, 113 {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE}, 114 {"PlaybackCTL", PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE}, 115 {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME}, 116 {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH}, 117 {"PlaybackMixer", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE}, 118 {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM}, 119 {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM}, 120 {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE}, 121 {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY}, 122 {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE}, 123 {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS}, 124 {"CaptureCTL", PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE}, 125 {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME}, 126 {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH}, 127 {"CaptureMixer", PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE}, 128 {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM}, 129 {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM}, 130 {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE}, 131 {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY}, 132 {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE}, 133 {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS}, 134 {"TQ", PA_ALSA_PROP_UCM_QOS}, 135 {"JackCTL", PA_ALSA_PROP_UCM_JACK_DEVICE}, 136 {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL}, 137 {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE}, 138 {NULL, NULL}, 139}; 140 141/* UCM verb info - this should eventually be part of policy manangement */ 142static struct ucm_info verb_info[] = { 143 {SND_USE_CASE_VERB_INACTIVE, 0}, 144 {SND_USE_CASE_VERB_HIFI, 8000}, 145 {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000}, 146 {SND_USE_CASE_VERB_VOICE, 6000}, 147 {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000}, 148 {SND_USE_CASE_VERB_VOICECALL, 4000}, 149 {SND_USE_CASE_VERB_IP_VOICECALL, 4000}, 150 {SND_USE_CASE_VERB_ANALOG_RADIO, 3000}, 151 {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000}, 152 {NULL, 0} 153}; 154 155/* UCM device info - should be overwritten by ucm property */ 156static struct ucm_info dev_info[] = { 157 {SND_USE_CASE_DEV_SPEAKER, 100}, 158 {SND_USE_CASE_DEV_LINE, 100}, 159 {SND_USE_CASE_DEV_HEADPHONES, 100}, 160 {SND_USE_CASE_DEV_HEADSET, 300}, 161 {SND_USE_CASE_DEV_HANDSET, 200}, 162 {SND_USE_CASE_DEV_BLUETOOTH, 400}, 163 {SND_USE_CASE_DEV_EARPIECE, 100}, 164 {SND_USE_CASE_DEV_SPDIF, 100}, 165 {SND_USE_CASE_DEV_HDMI, 100}, 166 {SND_USE_CASE_DEV_NONE, 100}, 167 {NULL, 0} 168}; 169 170 171static char *ucm_verb_value( 172 snd_use_case_mgr_t *uc_mgr, 173 const char *verb_name, 174 const char *id) { 175 176 const char *value; 177 char *_id = pa_sprintf_malloc("=%s//%s", id, verb_name); 178 int err = snd_use_case_get(uc_mgr, _id, &value); 179 pa_xfree(_id); 180 if (err < 0) 181 return NULL; 182 pa_log_debug("Got %s for verb %s: %s", id, verb_name, value); 183 /* Use the cast here to allow free() call without casting for callers. 184 * The snd_use_case_get() returns mallocated string. 185 * See the Note: in use-case.h for snd_use_case_get(). 186 */ 187 return (char *)value; 188} 189 190static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) { 191 pa_alsa_ucm_device *d; 192 uint32_t idx; 193 194 PA_IDXSET_FOREACH(d, idxset, idx) 195 if (d == dev) 196 return 1; 197 198 return 0; 199} 200 201static void ucm_add_devices_to_idxset( 202 pa_idxset *idxset, 203 pa_alsa_ucm_device *me, 204 pa_alsa_ucm_device *devices, 205 const char **dev_names, 206 int n) { 207 208 pa_alsa_ucm_device *d; 209 210 PA_LLIST_FOREACH(d, devices) { 211 const char *name; 212 int i; 213 214 if (d == me) 215 continue; 216 217 name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME); 218 219 for (i = 0; i < n; i++) 220 if (pa_streq(dev_names[i], name)) 221 pa_idxset_put(idxset, d, NULL); 222 } 223} 224 225/* Split a string into words. Like pa_split_spaces() but handle '' and "". */ 226static char *ucm_split_devnames(const char *c, const char **state) { 227 const char *current = *state ? *state : c; 228 char h; 229 size_t l; 230 231 if (!*current || *c == 0) 232 return NULL; 233 234 current += strspn(current, "\n\r \t"); 235 h = *current; 236 if (h == '\'' || h =='"') { 237 c = ++current; 238 for (l = 0; *c && *c != h; l++) c++; 239 if (*c != h) 240 return NULL; 241 *state = c + 1; 242 } else { 243 l = strcspn(current, "\n\r \t"); 244 *state = current+l; 245 } 246 247 return pa_xstrndup(current, l); 248} 249 250 251static void ucm_volume_free(pa_alsa_ucm_volume *vol) { 252 pa_assert(vol); 253 pa_xfree(vol->mixer_elem); 254 pa_xfree(vol->master_elem); 255 pa_xfree(vol->master_type); 256 pa_xfree(vol); 257} 258 259/* Get the volume identifier */ 260static char *ucm_get_mixer_id( 261 pa_alsa_ucm_device *device, 262 const char *mprop, 263 const char *cprop, 264 const char *cid) 265{ 266#if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */ 267 snd_ctl_elem_id_t *ctl; 268 int err; 269#endif 270 const char *value; 271 char *value2; 272 int index; 273 274 /* mixer element as first, if it's found, return it without modifications */ 275 value = pa_proplist_gets(device->proplist, mprop); 276 if (value) 277 return pa_xstrdup(value); 278 /* fallback, get the control element identifier */ 279 /* and try to do some heuristic to determine the mixer element name */ 280 value = pa_proplist_gets(device->proplist, cprop); 281 if (value == NULL) 282 return NULL; 283#if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */ 284 /* The new parser may return also element index. */ 285 snd_ctl_elem_id_alloca(&ctl); 286 err = snd_use_case_parse_ctl_elem_id(ctl, cid, value); 287 if (err < 0) 288 return NULL; 289 value = snd_ctl_elem_id_get_name(ctl); 290 index = snd_ctl_elem_id_get_index(ctl); 291#else 292#warning "Upgrade to alsa-lib 1.2.1!" 293 index = 0; 294#endif 295 if (!(value2 = pa_str_strip_suffix(value, " Playback Volume"))) 296 if (!(value2 = pa_str_strip_suffix(value, " Capture Volume"))) 297 if (!(value2 = pa_str_strip_suffix(value, " Volume"))) 298 value2 = pa_xstrdup(value); 299 if (index > 0) { 300 char *mix = pa_sprintf_malloc("'%s',%d", value2, index); 301 pa_xfree(value2); 302 return mix; 303 } 304 return value2; 305} 306 307/* Get the volume identifier */ 308static pa_alsa_ucm_volume *ucm_get_mixer_volume( 309 pa_alsa_ucm_device *device, 310 const char *mprop, 311 const char *cprop, 312 const char *cid, 313 const char *masterid, 314 const char *mastertype) 315{ 316 pa_alsa_ucm_volume *vol; 317 char *mixer_elem; 318 319 mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid); 320 if (mixer_elem == NULL) 321 return NULL; 322 vol = pa_xnew0(pa_alsa_ucm_volume, 1); 323 if (vol == NULL) { 324 pa_xfree(mixer_elem); 325 return NULL; 326 } 327 vol->mixer_elem = mixer_elem; 328 vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, masterid)); 329 vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, mastertype)); 330 return vol; 331} 332 333/* Get the ALSA mixer device for the UCM device */ 334static const char *get_mixer_device(pa_alsa_ucm_device *dev, bool is_sink) 335{ 336 const char *dev_name; 337 338 if (is_sink) { 339 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE); 340 if (!dev_name) 341 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE); 342 } else { 343 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE); 344 if (!dev_name) 345 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE); 346 } 347 return dev_name; 348} 349 350/* Get the ALSA mixer device for the UCM jack */ 351static const char *get_jack_mixer_device(pa_alsa_ucm_device *dev, bool is_sink) { 352 const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_DEVICE); 353 if (!dev_name) 354 return get_mixer_device(dev, is_sink); 355 return dev_name; 356} 357 358/* Create a property list for this ucm device */ 359static int ucm_get_device_property( 360 pa_alsa_ucm_device *device, 361 snd_use_case_mgr_t *uc_mgr, 362 pa_alsa_ucm_verb *verb, 363 const char *device_name) { 364 365 const char *value; 366 const char **devices; 367 char *id, *s; 368 int i; 369 int err; 370 uint32_t ui; 371 int n_confdev, n_suppdev; 372 pa_alsa_ucm_volume *vol; 373 374 /* determine the device type */ 375 device->type = PA_DEVICE_PORT_TYPE_UNKNOWN; 376 id = s = pa_xstrdup(device_name); 377 while (s && *s && isalpha(*s)) s++; 378 if (s) 379 *s = '\0'; 380 for (i = 0; types[i].prefix; i++) 381 if (pa_streq(id, types[i].prefix)) { 382 device->type = types[i].type; 383 break; 384 } 385 pa_xfree(id); 386 387 /* set properties */ 388 for (i = 0; item[i].id; i++) { 389 id = pa_sprintf_malloc("%s/%s", item[i].id, device_name); 390 err = snd_use_case_get(uc_mgr, id, &value); 391 pa_xfree(id); 392 if (err < 0) 393 continue; 394 395 pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value); 396 pa_proplist_sets(device->proplist, item[i].property, value); 397 free((void*)value); 398 } 399 400 /* get direction and channels */ 401 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS); 402 if (value) { /* output */ 403 /* get channels */ 404 if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui)) 405 device->playback_channels = ui; 406 else 407 pa_log("UCM playback channels %s for device %s out of range", value, device_name); 408 409 /* get pcm */ 410 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK); 411 if (!value) /* take pcm from verb playback default */ 412 pa_log("UCM playback device %s fetch pcm failed", device_name); 413 } 414 415 if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK) && 416 device->playback_channels == 0) { 417 pa_log_info("UCM file does not specify 'PlaybackChannels' " 418 "for device %s, assuming stereo.", device_name); 419 device->playback_channels = 2; 420 } 421 422 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS); 423 if (value) { /* input */ 424 /* get channels */ 425 if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui)) 426 device->capture_channels = ui; 427 else 428 pa_log("UCM capture channels %s for device %s out of range", value, device_name); 429 430 /* get pcm */ 431 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE); 432 if (!value) /* take pcm from verb capture default */ 433 pa_log("UCM capture device %s fetch pcm failed", device_name); 434 } 435 436 if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE) && 437 device->capture_channels == 0) { 438 pa_log_info("UCM file does not specify 'CaptureChannels' " 439 "for device %s, assuming stereo.", device_name); 440 device->capture_channels = 2; 441 } 442 443 /* get rate and priority of device */ 444 if (device->playback_channels) { /* sink device */ 445 /* get rate */ 446 if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) { 447 if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) { 448 pa_log_debug("UCM playback device %s rate %d", device_name, ui); 449 device->playback_rate = ui; 450 } else 451 pa_log_debug("UCM playback device %s has bad rate %s", device_name, value); 452 } 453 454 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY); 455 if (value) { 456 /* get priority from ucm config */ 457 if (pa_atou(value, &ui) == 0) 458 device->playback_priority = ui; 459 else 460 pa_log_debug("UCM playback priority %s for device %s error", value, device_name); 461 } 462 463 vol = ucm_get_mixer_volume(device, 464 PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM, 465 PA_ALSA_PROP_UCM_PLAYBACK_VOLUME, 466 "PlaybackVolume", 467 PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM, 468 PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE); 469 if (vol) 470 pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol); 471 } 472 473 if (device->capture_channels) { /* source device */ 474 /* get rate */ 475 if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) { 476 if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) { 477 pa_log_debug("UCM capture device %s rate %d", device_name, ui); 478 device->capture_rate = ui; 479 } else 480 pa_log_debug("UCM capture device %s has bad rate %s", device_name, value); 481 } 482 483 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY); 484 if (value) { 485 /* get priority from ucm config */ 486 if (pa_atou(value, &ui) == 0) 487 device->capture_priority = ui; 488 else 489 pa_log_debug("UCM capture priority %s for device %s error", value, device_name); 490 } 491 492 vol = ucm_get_mixer_volume(device, 493 PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM, 494 PA_ALSA_PROP_UCM_CAPTURE_VOLUME, 495 "CaptureVolume", 496 PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM, 497 PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE); 498 if (vol) 499 pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol); 500 } 501 502 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) { 503 /* get priority from static table */ 504 for (i = 0; dev_info[i].id; i++) { 505 if (strcasecmp(dev_info[i].id, device_name) == 0) { 506 PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority); 507 break; 508 } 509 } 510 } 511 512 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) { 513 /* fall through to default priority */ 514 device->playback_priority = 100; 515 } 516 517 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) { 518 /* fall through to default priority */ 519 device->capture_priority = 100; 520 } 521 522 id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name); 523 n_confdev = snd_use_case_get_list(uc_mgr, id, &devices); 524 pa_xfree(id); 525 526 if (n_confdev <= 0) 527 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name); 528 else { 529 device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 530 ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev); 531 snd_use_case_free_list(devices, n_confdev); 532 } 533 534 id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name); 535 n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices); 536 pa_xfree(id); 537 538 if (n_suppdev <= 0) 539 pa_log_debug("No %s for device %s", "_supporteddevs", device_name); 540 else { 541 device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 542 ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev); 543 snd_use_case_free_list(devices, n_suppdev); 544 } 545 546 return 0; 547}; 548 549/* Create a property list for this ucm modifier */ 550static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) { 551 const char *value; 552 char *id; 553 int i; 554 555 for (i = 0; item[i].id; i++) { 556 int err; 557 558 id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name); 559 err = snd_use_case_get(uc_mgr, id, &value); 560 pa_xfree(id); 561 if (err < 0) 562 continue; 563 564 pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value); 565 pa_proplist_sets(modifier->proplist, item[i].property, value); 566 free((void*)value); 567 } 568 569 id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name); 570 modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices); 571 pa_xfree(id); 572 if (modifier->n_confdev < 0) 573 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name); 574 575 id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name); 576 modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices); 577 pa_xfree(id); 578 if (modifier->n_suppdev < 0) 579 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name); 580 581 return 0; 582}; 583 584/* Create a list of devices for this verb */ 585static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { 586 const char **dev_list; 587 int num_dev, i; 588 589 num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list); 590 if (num_dev < 0) 591 return num_dev; 592 593 for (i = 0; i < num_dev; i += 2) { 594 pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1); 595 596 d->proplist = pa_proplist_new(); 597 pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i])); 598 pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1])); 599 d->ucm_ports = pa_dynarray_new(NULL); 600 d->hw_mute_jacks = pa_dynarray_new(NULL); 601 d->available = PA_AVAILABLE_UNKNOWN; 602 603 d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, 604 (pa_free_cb_t) ucm_volume_free); 605 d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, 606 (pa_free_cb_t) ucm_volume_free); 607 608 PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d); 609 } 610 611 snd_use_case_free_list(dev_list, num_dev); 612 613 return 0; 614}; 615 616static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { 617 const char **mod_list; 618 int num_mod, i; 619 620 num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list); 621 if (num_mod < 0) 622 return num_mod; 623 624 for (i = 0; i < num_mod; i += 2) { 625 pa_alsa_ucm_modifier *m; 626 627 if (!mod_list[i]) { 628 pa_log_warn("Got a modifier with a null name. Skipping."); 629 continue; 630 } 631 632 m = pa_xnew0(pa_alsa_ucm_modifier, 1); 633 m->proplist = pa_proplist_new(); 634 635 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]); 636 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1])); 637 638 PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m); 639 } 640 641 snd_use_case_free_list(mod_list, num_mod); 642 643 return 0; 644}; 645 646static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) { 647 const char *cur = pa_proplist_gets(dev->proplist, role_name); 648 649 if (!cur) 650 pa_proplist_sets(dev->proplist, role_name, role); 651 else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */ 652 char *value = pa_sprintf_malloc("%s %s", cur, role); 653 654 pa_proplist_sets(dev->proplist, role_name, value); 655 pa_xfree(value); 656 } 657 658 pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist, 659 role_name)); 660} 661 662static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) { 663 pa_alsa_ucm_device *d; 664 665 PA_LLIST_FOREACH(d, list) { 666 const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME); 667 668 if (pa_streq(dev_name, name)) { 669 const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK); 670 const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE); 671 672 if (is_sink && sink) 673 add_role_to_device(d, dev_name, role_name, role); 674 else if (!is_sink && source) 675 add_role_to_device(d, dev_name, role_name, role); 676 break; 677 } 678 } 679} 680 681static char *modifier_name_to_role(const char *mod_name, bool *is_sink) { 682 char *sub = NULL, *tmp; 683 684 *is_sink = false; 685 686 if (pa_startswith(mod_name, "Play")) { 687 *is_sink = true; 688 sub = pa_xstrdup(mod_name + 4); 689 } else if (pa_startswith(mod_name, "Capture")) 690 sub = pa_xstrdup(mod_name + 7); 691 692 if (!sub || !*sub) { 693 pa_xfree(sub); 694 pa_log_warn("Can't match media roles for modifier %s", mod_name); 695 return NULL; 696 } 697 698 tmp = sub; 699 700 do { 701 *tmp = tolower(*tmp); 702 } while (*(++tmp)); 703 704 return sub; 705} 706 707static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) { 708 int i; 709 bool is_sink = false; 710 char *sub = NULL; 711 const char *role_name; 712 713 sub = modifier_name_to_role(mod_name, &is_sink); 714 if (!sub) 715 return; 716 717 modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; 718 modifier->media_role = sub; 719 720 role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES; 721 for (i = 0; i < modifier->n_suppdev; i++) { 722 /* if modifier has no specific pcm, we add role intent to its supported devices */ 723 if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) && 724 !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE)) 725 add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink); 726 } 727} 728 729static void append_lost_relationship(pa_alsa_ucm_device *dev) { 730 uint32_t idx; 731 pa_alsa_ucm_device *d; 732 733 if (dev->conflicting_devices) { 734 PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) { 735 if (!d->conflicting_devices) 736 d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 737 738 if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0) 739 pa_log_warn("Add lost conflicting device %s to %s", 740 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), 741 pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); 742 } 743 } 744 745 if (dev->supported_devices) { 746 PA_IDXSET_FOREACH(d, dev->supported_devices, idx) { 747 if (!d->supported_devices) 748 d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 749 750 if (pa_idxset_put(d->supported_devices, dev, NULL) == 0) 751 pa_log_warn("Add lost supported device %s to %s", 752 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), 753 pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME)); 754 } 755 } 756} 757 758int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) { 759 char *card_name; 760 const char **verb_list, *value; 761 int num_verbs, i, err = 0; 762 763 /* support multiple card instances, address card directly by index */ 764 card_name = pa_sprintf_malloc("hw:%i", card_index); 765 err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name); 766 if (err < 0) { 767 /* fallback longname: is UCM available for this card ? */ 768 pa_xfree(card_name); 769 err = snd_card_get_name(card_index, &card_name); 770 if (err < 0) { 771 pa_log("Card can't get card_name from card_index %d", card_index); 772 err = -PA_ALSA_ERR_UNSPECIFIED; 773 goto name_fail; 774 } 775 776 err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name); 777 if (err < 0) { 778 pa_log_info("UCM not available for card %s", card_name); 779 err = -PA_ALSA_ERR_UCM_OPEN; 780 goto ucm_mgr_fail; 781 } 782 } 783 784 err = snd_use_case_get(ucm->ucm_mgr, "=Linked", &value); 785 if (err >= 0) { 786 if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) { 787 free((void *)value); 788 pa_log_info("Empty (linked) UCM for card %s", card_name); 789 err = -PA_ALSA_ERR_UCM_LINKED; 790 goto ucm_verb_fail; 791 } 792 free((void *)value); 793 } 794 795 pa_log_info("UCM available for card %s", card_name); 796 797 if (snd_use_case_get(ucm->ucm_mgr, "_alibpref", &value) == 0) { 798 if (value[0]) { 799 ucm->alib_prefix = pa_xstrdup(value); 800 pa_log_debug("UCM _alibpref=%s", ucm->alib_prefix); 801 } 802 free((void *)value); 803 } 804 805 /* get a list of all UCM verbs (profiles) for this card */ 806 num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list); 807 if (num_verbs < 0) { 808 pa_log("UCM verb list not found for %s", card_name); 809 err = -PA_ALSA_ERR_UNSPECIFIED; 810 goto ucm_verb_fail; 811 } 812 813 /* get the properties of each UCM verb */ 814 for (i = 0; i < num_verbs; i += 2) { 815 pa_alsa_ucm_verb *verb; 816 817 /* Get devices and modifiers for each verb */ 818 err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb); 819 if (err < 0) { 820 pa_log("Failed to get the verb %s", verb_list[i]); 821 continue; 822 } 823 824 PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb); 825 } 826 827 if (!ucm->verbs) { 828 pa_log("No UCM verb is valid for %s", card_name); 829 err = -PA_ALSA_ERR_UCM_NO_VERB; 830 } 831 832 snd_use_case_free_list(verb_list, num_verbs); 833 834ucm_verb_fail: 835 if (err < 0) { 836 snd_use_case_mgr_close(ucm->ucm_mgr); 837 ucm->ucm_mgr = NULL; 838 } 839 840ucm_mgr_fail: 841 pa_xfree(card_name); 842 843name_fail: 844 return err; 845} 846 847int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) { 848 pa_alsa_ucm_device *d; 849 pa_alsa_ucm_modifier *mod; 850 pa_alsa_ucm_verb *verb; 851 char *value; 852 unsigned ui; 853 int err = 0; 854 855 *p_verb = NULL; 856 pa_log_info("Set UCM verb to %s", verb_name); 857 err = snd_use_case_set(uc_mgr, "_verb", verb_name); 858 if (err < 0) 859 return err; 860 861 verb = pa_xnew0(pa_alsa_ucm_verb, 1); 862 verb->proplist = pa_proplist_new(); 863 864 pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name)); 865 pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc)); 866 867 value = ucm_verb_value(uc_mgr, verb_name, "Priority"); 868 if (value && !pa_atou(value, &ui)) 869 verb->priority = ui > 10000 ? 10000 : ui; 870 free(value); 871 872 err = ucm_get_devices(verb, uc_mgr); 873 if (err < 0) 874 pa_log("No UCM devices for verb %s", verb_name); 875 876 err = ucm_get_modifiers(verb, uc_mgr); 877 if (err < 0) 878 pa_log("No UCM modifiers for verb %s", verb_name); 879 880 PA_LLIST_FOREACH(d, verb->devices) { 881 const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME); 882 883 /* Devices properties */ 884 ucm_get_device_property(d, uc_mgr, verb, dev_name); 885 } 886 /* make conflicting or supported device mutual */ 887 PA_LLIST_FOREACH(d, verb->devices) 888 append_lost_relationship(d); 889 890 PA_LLIST_FOREACH(mod, verb->modifiers) { 891 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); 892 893 /* Modifier properties */ 894 ucm_get_modifier_property(mod, uc_mgr, mod_name); 895 896 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */ 897 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name); 898 ucm_set_media_roles(mod, verb->devices, mod_name); 899 } 900 901 *p_verb = verb; 902 return 0; 903} 904 905static int pa_alsa_ucm_device_cmp(const void *a, const void *b) { 906 const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a; 907 const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b; 908 909 return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME)); 910} 911 912static void set_eld_devices(pa_hashmap *hash) 913{ 914 pa_device_port *port; 915 pa_alsa_ucm_port_data *data; 916 pa_alsa_ucm_device *dev; 917 const char *eld_mixer_device_name; 918 void *state; 919 int idx, eld_device; 920 921 PA_HASHMAP_FOREACH(port, hash, state) { 922 data = PA_DEVICE_PORT_DATA(port); 923 eld_mixer_device_name = NULL; 924 eld_device = -1; 925 PA_DYNARRAY_FOREACH(dev, data->devices, idx) { 926 if (dev->eld_device >= 0 && dev->eld_mixer_device_name) { 927 if (eld_device >= 0 && eld_device != dev->eld_device) { 928 pa_log_error("The ELD device is already set!"); 929 } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) { 930 pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name); 931 } else { 932 eld_mixer_device_name = dev->eld_mixer_device_name; 933 eld_device = dev->eld_device; 934 } 935 } 936 } 937 data->eld_device = eld_device; 938 data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name); 939 } 940} 941 942static void update_mixer_paths(pa_hashmap *ports, const char *profile) { 943 pa_device_port *port; 944 pa_alsa_ucm_port_data *data; 945 void *state; 946 947 /* select volume controls on ports */ 948 PA_HASHMAP_FOREACH(port, ports, state) { 949 pa_log_info("Updating mixer path for %s: %s", profile, port->name); 950 data = PA_DEVICE_PORT_DATA(port); 951 data->path = pa_hashmap_get(data->paths, profile); 952 } 953} 954 955static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) { 956 pa_device_port *port; 957 pa_alsa_path *path; 958 pa_alsa_ucm_port_data *data; 959 pa_alsa_ucm_device *dev; 960 snd_mixer_t *mixer_handle; 961 const char *profile, *mdev, *mdev2; 962 void *state, *state2; 963 int idx; 964 965 PA_HASHMAP_FOREACH(port, hash, state) { 966 data = PA_DEVICE_PORT_DATA(port); 967 968 mdev = NULL; 969 PA_DYNARRAY_FOREACH(dev, data->devices, idx) { 970 mdev2 = get_mixer_device(dev, is_sink); 971 if (mdev && mdev2 && !pa_streq(mdev, mdev2)) { 972 pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2); 973 goto fail; 974 } 975 if (mdev2) 976 mdev = mdev2; 977 } 978 979 if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) { 980 pa_log_error("Failed to find a working mixer device (%s).", mdev); 981 goto fail; 982 } 983 984 PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) { 985 if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) { 986 pa_log_warn("Could not probe path: %s, using s/w volume", path->name); 987 pa_hashmap_remove(data->paths, profile); 988 } else if (!path->has_volume && !path->has_mute) { 989 pa_log_warn("Path %s is not a volume or mute control", path->name); 990 pa_hashmap_remove(data->paths, profile); 991 } else 992 pa_log_debug("Set up h/w %s using '%s' for %s:%s", path->has_volume ? "volume" : "mute", 993 path->name, profile, port->name); 994 } 995 } 996 997 return; 998 999fail: 1000 /* We could not probe the paths we created. Free them and revert to software volumes. */ 1001 PA_HASHMAP_FOREACH(port, hash, state) { 1002 data = PA_DEVICE_PORT_DATA(port); 1003 pa_hashmap_remove_all(data->paths); 1004 } 1005} 1006 1007static void ucm_add_port_combination( 1008 pa_hashmap *hash, 1009 pa_alsa_ucm_mapping_context *context, 1010 bool is_sink, 1011 pa_alsa_ucm_device **pdevices, 1012 int num, 1013 pa_hashmap *ports, 1014 pa_card_profile *cp, 1015 pa_core *core) { 1016 1017 pa_device_port *port; 1018 int i; 1019 unsigned priority; 1020 double prio2; 1021 char *name, *desc; 1022 const char *dev_name; 1023 const char *direction; 1024 const char *profile; 1025 pa_alsa_ucm_device *sorted[num], *dev; 1026 pa_alsa_ucm_port_data *data; 1027 pa_alsa_ucm_volume *vol; 1028 pa_alsa_jack *jack, *jack2; 1029 pa_device_port_type_t type, type2; 1030 void *state; 1031 1032 for (i = 0; i < num; i++) 1033 sorted[i] = pdevices[i]; 1034 1035 /* Sort by alphabetical order so as to have a deterministic naming scheme 1036 * for combination ports */ 1037 qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp); 1038 1039 dev = sorted[0]; 1040 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); 1041 1042 name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name); 1043 desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION)) 1044 : pa_sprintf_malloc("Combination port for %s", dev_name); 1045 1046 priority = is_sink ? dev->playback_priority : dev->capture_priority; 1047 prio2 = (priority == 0 ? 0 : 1.0/priority); 1048 jack = ucm_get_jack(context->ucm, dev); 1049 type = dev->type; 1050 1051 for (i = 1; i < num; i++) { 1052 char *tmp; 1053 1054 dev = sorted[i]; 1055 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); 1056 1057 tmp = pa_sprintf_malloc("%s+%s", name, dev_name); 1058 pa_xfree(name); 1059 name = tmp; 1060 1061 tmp = pa_sprintf_malloc("%s,%s", desc, dev_name); 1062 pa_xfree(desc); 1063 desc = tmp; 1064 1065 priority = is_sink ? dev->playback_priority : dev->capture_priority; 1066 if (priority != 0 && prio2 > 0) 1067 prio2 += 1.0/priority; 1068 1069 jack2 = ucm_get_jack(context->ucm, dev); 1070 if (jack2) { 1071 if (jack && jack != jack2) 1072 pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name); 1073 jack = jack2; 1074 } 1075 1076 type2 = dev->type; 1077 if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) { 1078 if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2) 1079 pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2); 1080 type = type2; 1081 } 1082 } 1083 1084 /* Make combination ports always have lower priority, and use the formula 1085 1/p = 1/p1 + 1/p2 + ... 1/pn. 1086 This way, the result will always be less than the individual components, 1087 yet higher components will lead to higher result. */ 1088 1089 if (num > 1) 1090 priority = prio2 > 0 ? 1.0/prio2 : 0; 1091 1092 port = pa_hashmap_get(ports, name); 1093 if (!port) { 1094 pa_device_port_new_data port_data; 1095 1096 pa_device_port_new_data_init(&port_data); 1097 pa_device_port_new_data_set_name(&port_data, name); 1098 pa_device_port_new_data_set_description(&port_data, desc); 1099 pa_device_port_new_data_set_type(&port_data, type); 1100 pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT); 1101 if (jack) 1102 pa_device_port_new_data_set_availability_group(&port_data, jack->name); 1103 1104 port = pa_device_port_new(core, &port_data, sizeof(pa_alsa_ucm_port_data)); 1105 pa_device_port_new_data_done(&port_data); 1106 1107 data = PA_DEVICE_PORT_DATA(port); 1108 ucm_port_data_init(data, context->ucm, port, pdevices, num); 1109 port->impl_free = ucm_port_data_free; 1110 1111 pa_hashmap_put(ports, port->name, port); 1112 pa_log_debug("Add port %s: %s", port->name, port->description); 1113 1114 if (num == 1) { 1115 /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination 1116 * ports. */ 1117 PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { 1118 pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, 1119 is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); 1120 1121 if (!path) 1122 pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); 1123 else { 1124 if (vol->master_elem) { 1125 pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); 1126 e->switch_use = PA_ALSA_SWITCH_MUTE; 1127 e->volume_use = PA_ALSA_VOLUME_MERGE; 1128 } 1129 1130 pa_hashmap_put(data->paths, pa_xstrdup(profile), path); 1131 1132 /* Add path also to already created empty path set */ 1133 dev = sorted[0]; 1134 if (is_sink) 1135 pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); 1136 else 1137 pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); 1138 } 1139 } 1140 } 1141 } 1142 1143 port->priority = priority; 1144 1145 pa_xfree(name); 1146 pa_xfree(desc); 1147 1148 direction = is_sink ? "output" : "input"; 1149 pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority); 1150 1151 if (cp) { 1152 pa_log_debug("Adding profile %s to port %s.", cp->name, port->name); 1153 pa_hashmap_put(port->profiles, cp->name, cp); 1154 } 1155 1156 if (hash) { 1157 pa_hashmap_put(hash, port->name, port); 1158 pa_device_port_ref(port); 1159 } 1160} 1161 1162static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) { 1163 int ret = 0; 1164 const char *r; 1165 const char *state = NULL; 1166 size_t len; 1167 1168 if (!port_name || !dev_name) 1169 return false; 1170 1171 port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT); 1172 1173 while ((r = pa_split_in_place(port_name, "+", &len, &state))) { 1174 if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) { 1175 ret = 1; 1176 break; 1177 } 1178 } 1179 1180 return ret; 1181} 1182 1183static int ucm_check_conformance( 1184 pa_alsa_ucm_mapping_context *context, 1185 pa_alsa_ucm_device **pdevices, 1186 int dev_num, 1187 pa_alsa_ucm_device *dev) { 1188 1189 uint32_t idx; 1190 pa_alsa_ucm_device *d; 1191 int i; 1192 1193 pa_assert(dev); 1194 1195 pa_log_debug("Check device %s conformance with %d other devices", 1196 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num); 1197 if (dev_num == 0) { 1198 pa_log_debug("First device in combination, number 1"); 1199 return 1; 1200 } 1201 1202 if (dev->conflicting_devices) { /* the device defines conflicting devices */ 1203 PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) { 1204 for (i = 0; i < dev_num; i++) { 1205 if (pdevices[i] == d) { 1206 pa_log_debug("Conflicting device found"); 1207 return 0; 1208 } 1209 } 1210 } 1211 } else if (dev->supported_devices) { /* the device defines supported devices */ 1212 for (i = 0; i < dev_num; i++) { 1213 if (!ucm_device_exists(dev->supported_devices, pdevices[i])) { 1214 pa_log_debug("Supported device not found"); 1215 return 0; 1216 } 1217 } 1218 } else { /* not support any other devices */ 1219 pa_log_debug("Not support any other devices"); 1220 return 0; 1221 } 1222 1223 pa_log_debug("Device added to combination, number %d", dev_num + 1); 1224 return 1; 1225} 1226 1227static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) { 1228 pa_alsa_ucm_device *dev; 1229 1230 if (*idx == PA_IDXSET_INVALID) 1231 dev = pa_idxset_first(idxset, idx); 1232 else 1233 dev = pa_idxset_next(idxset, idx); 1234 1235 return dev; 1236} 1237 1238static void ucm_add_ports_combination( 1239 pa_hashmap *hash, 1240 pa_alsa_ucm_mapping_context *context, 1241 bool is_sink, 1242 pa_alsa_ucm_device **pdevices, 1243 int dev_num, 1244 uint32_t map_index, 1245 pa_hashmap *ports, 1246 pa_card_profile *cp, 1247 pa_core *core) { 1248 1249 pa_alsa_ucm_device *dev; 1250 uint32_t idx = map_index; 1251 1252 if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL) 1253 return; 1254 1255 /* check if device at map_index can combine with existing devices combination */ 1256 if (ucm_check_conformance(context, pdevices, dev_num, dev)) { 1257 /* add device at map_index to devices combination */ 1258 pdevices[dev_num] = dev; 1259 /* add current devices combination as a new port */ 1260 ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core); 1261 /* try more elements combination */ 1262 ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core); 1263 } 1264 1265 /* try other device with current elements number */ 1266 ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core); 1267} 1268 1269static char* merge_roles(const char *cur, const char *add) { 1270 char *r, *ret; 1271 const char *state = NULL; 1272 1273 if (add == NULL) 1274 return pa_xstrdup(cur); 1275 else if (cur == NULL) 1276 return pa_xstrdup(add); 1277 1278 ret = pa_xstrdup(cur); 1279 1280 while ((r = pa_split_spaces(add, &state))) { 1281 char *value; 1282 1283 if (!pa_str_in_list_spaces(ret, r)) 1284 value = pa_sprintf_malloc("%s %s", ret, r); 1285 else { 1286 pa_xfree(r); 1287 continue; 1288 } 1289 1290 pa_xfree(ret); 1291 ret = value; 1292 pa_xfree(r); 1293 } 1294 1295 return ret; 1296} 1297 1298void pa_alsa_ucm_add_ports_combination( 1299 pa_hashmap *p, 1300 pa_alsa_ucm_mapping_context *context, 1301 bool is_sink, 1302 pa_hashmap *ports, 1303 pa_card_profile *cp, 1304 pa_core *core) { 1305 1306 pa_alsa_ucm_device **pdevices; 1307 1308 pa_assert(context->ucm_devices); 1309 1310 if (pa_idxset_size(context->ucm_devices) > 0) { 1311 pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices)); 1312 ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core); 1313 pa_xfree(pdevices); 1314 } 1315 1316 /* ELD devices */ 1317 set_eld_devices(ports); 1318} 1319 1320void pa_alsa_ucm_add_ports( 1321 pa_hashmap **p, 1322 pa_proplist *proplist, 1323 pa_alsa_ucm_mapping_context *context, 1324 bool is_sink, 1325 pa_card *card, 1326 snd_pcm_t *pcm_handle, 1327 bool ignore_dB) { 1328 1329 uint32_t idx; 1330 char *merged_roles; 1331 const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES; 1332 pa_alsa_ucm_device *dev; 1333 pa_alsa_ucm_modifier *mod; 1334 char *tmp; 1335 1336 pa_assert(p); 1337 pa_assert(*p); 1338 1339 /* add ports first */ 1340 pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core); 1341 1342 /* now set up volume paths if any */ 1343 probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB); 1344 1345 /* probe_volumes() removes per-profile paths from ports if probing them 1346 * fails. The path for the current profile is cached in 1347 * pa_alsa_ucm_port_data.path, which is not cleared by probe_volumes() if 1348 * the path gets removed, so we have to call update_mixer_paths() here to 1349 * unset the cached path if needed. */ 1350 if (card->active_profile) 1351 update_mixer_paths(*p, card->active_profile->name); 1352 1353 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */ 1354 merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES)); 1355 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { 1356 const char *roles = pa_proplist_gets(dev->proplist, role_name); 1357 tmp = merge_roles(merged_roles, roles); 1358 pa_xfree(merged_roles); 1359 merged_roles = tmp; 1360 } 1361 1362 if (context->ucm_modifiers) 1363 PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) { 1364 tmp = merge_roles(merged_roles, mod->media_role); 1365 pa_xfree(merged_roles); 1366 merged_roles = tmp; 1367 } 1368 1369 if (merged_roles) 1370 pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles); 1371 1372 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles)); 1373 pa_xfree(merged_roles); 1374} 1375 1376/* Change UCM verb and device to match selected card profile */ 1377int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) { 1378 int ret = 0; 1379 const char *profile; 1380 pa_alsa_ucm_verb *verb; 1381 1382 if (new_profile == old_profile) 1383 return ret; 1384 else if (new_profile == NULL || old_profile == NULL) 1385 profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE; 1386 else if (!pa_streq(new_profile, old_profile)) 1387 profile = new_profile; 1388 else 1389 return ret; 1390 1391 /* change verb */ 1392 pa_log_info("Set UCM verb to %s", profile); 1393 if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) { 1394 pa_log("Failed to set verb %s", profile); 1395 ret = -1; 1396 } 1397 1398 /* find active verb */ 1399 ucm->active_verb = NULL; 1400 PA_LLIST_FOREACH(verb, ucm->verbs) { 1401 const char *verb_name; 1402 verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME); 1403 if (pa_streq(verb_name, profile)) { 1404 ucm->active_verb = verb; 1405 break; 1406 } 1407 } 1408 1409 update_mixer_paths(card->ports, profile); 1410 return ret; 1411} 1412 1413int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) { 1414 int i; 1415 int ret = 0; 1416 pa_alsa_ucm_config *ucm; 1417 const char **enable_devs; 1418 int enable_num = 0; 1419 uint32_t idx; 1420 pa_alsa_ucm_device *dev; 1421 1422 pa_assert(context && context->ucm); 1423 1424 ucm = context->ucm; 1425 pa_assert(ucm->ucm_mgr); 1426 1427 enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices)); 1428 1429 /* first disable then enable */ 1430 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { 1431 const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); 1432 1433 if (ucm_port_contains(port->name, dev_name, is_sink)) 1434 enable_devs[enable_num++] = dev_name; 1435 else { 1436 pa_log_debug("Disable ucm device %s", dev_name); 1437 if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) { 1438 pa_log("Failed to disable ucm device %s", dev_name); 1439 ret = -1; 1440 break; 1441 } 1442 } 1443 } 1444 1445 for (i = 0; i < enable_num; i++) { 1446 pa_log_debug("Enable ucm device %s", enable_devs[i]); 1447 if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) { 1448 pa_log("Failed to enable ucm device %s", enable_devs[i]); 1449 ret = -1; 1450 break; 1451 } 1452 } 1453 1454 pa_xfree(enable_devs); 1455 1456 return ret; 1457} 1458 1459static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) { 1460 1461 pa_alsa_path_set *ps; 1462 1463 /* create empty path set for the future path additions */ 1464 ps = pa_xnew0(pa_alsa_path_set, 1); 1465 ps->direction = m->direction; 1466 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1467 1468 switch (m->direction) { 1469 case PA_ALSA_DIRECTION_ANY: 1470 pa_idxset_put(p->output_mappings, m, NULL); 1471 pa_idxset_put(p->input_mappings, m, NULL); 1472 m->output_path_set = ps; 1473 m->input_path_set = ps; 1474 break; 1475 case PA_ALSA_DIRECTION_OUTPUT: 1476 pa_idxset_put(p->output_mappings, m, NULL); 1477 m->output_path_set = ps; 1478 break; 1479 case PA_ALSA_DIRECTION_INPUT: 1480 pa_idxset_put(p->input_mappings, m, NULL); 1481 m->input_path_set = ps; 1482 break; 1483 } 1484} 1485 1486static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) { 1487 char *cur_desc; 1488 const char *new_desc, *mdev; 1489 bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT; 1490 1491 pa_idxset_put(m->ucm_context.ucm_devices, device, NULL); 1492 1493 new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); 1494 cur_desc = m->description; 1495 if (cur_desc) 1496 m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc); 1497 else 1498 m->description = pa_xstrdup(new_desc); 1499 pa_xfree(cur_desc); 1500 1501 /* walk around null case */ 1502 m->description = m->description ? m->description : pa_xstrdup(""); 1503 1504 /* save mapping to ucm device */ 1505 if (is_sink) 1506 device->playback_mapping = m; 1507 else 1508 device->capture_mapping = m; 1509 1510 mdev = get_mixer_device(device, is_sink); 1511 if (mdev) 1512 pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev); 1513} 1514 1515static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) { 1516 char *cur_desc; 1517 const char *new_desc, *mod_name, *channel_str; 1518 uint32_t channels = 0; 1519 1520 pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL); 1521 1522 new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); 1523 cur_desc = m->description; 1524 if (cur_desc) 1525 m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc); 1526 else 1527 m->description = pa_xstrdup(new_desc); 1528 pa_xfree(cur_desc); 1529 1530 m->description = m->description ? m->description : pa_xstrdup(""); 1531 1532 /* Modifier sinks should not be routed to by default */ 1533 m->priority = 0; 1534 1535 mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME); 1536 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name); 1537 1538 /* save mapping to ucm modifier */ 1539 if (m->direction == PA_ALSA_DIRECTION_OUTPUT) { 1540 modifier->playback_mapping = m; 1541 channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS); 1542 } else { 1543 modifier->capture_mapping = m; 1544 channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS); 1545 } 1546 1547 if (channel_str) { 1548 /* FIXME: channel_str is unsanitized input from the UCM configuration, 1549 * we should do proper error handling instead of asserting. 1550 * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */ 1551 pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels)); 1552 pa_log_debug("Got channel count %" PRIu32 " for modifier", channels); 1553 } 1554 1555 if (channels) 1556 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA); 1557 else 1558 pa_channel_map_init(&m->channel_map); 1559} 1560 1561static pa_alsa_mapping* ucm_alsa_mapping_get(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps, const char *verb_name, const char *device_str, bool is_sink) { 1562 pa_alsa_mapping *m; 1563 char *mapping_name; 1564 size_t ucm_alibpref_len = 0; 1565 1566 /* find private alsa-lib's configuration device prefix */ 1567 1568 if (ucm->alib_prefix && pa_startswith(device_str, ucm->alib_prefix)) 1569 ucm_alibpref_len = strlen(ucm->alib_prefix); 1570 1571 mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str + ucm_alibpref_len, is_sink ? "sink" : "source"); 1572 1573 m = pa_alsa_mapping_get(ps, mapping_name); 1574 1575 if (!m) 1576 pa_log("No mapping for %s", mapping_name); 1577 1578 pa_xfree(mapping_name); 1579 1580 return m; 1581} 1582 1583static int ucm_create_mapping_direction( 1584 pa_alsa_ucm_config *ucm, 1585 pa_alsa_profile_set *ps, 1586 pa_alsa_profile *p, 1587 pa_alsa_ucm_device *device, 1588 const char *verb_name, 1589 const char *device_name, 1590 const char *device_str, 1591 bool is_sink) { 1592 1593 pa_alsa_mapping *m; 1594 unsigned priority, rate, channels; 1595 1596 m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink); 1597 1598 if (!m) 1599 return -1; 1600 1601 pa_log_debug("UCM mapping: %s dev %s", m->name, device_name); 1602 1603 priority = is_sink ? device->playback_priority : device->capture_priority; 1604 rate = is_sink ? device->playback_rate : device->capture_rate; 1605 channels = is_sink ? device->playback_channels : device->capture_channels; 1606 1607 if (!m->ucm_context.ucm_devices) { /* new mapping */ 1608 m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1609 m->ucm_context.ucm = ucm; 1610 m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; 1611 1612 m->device_strings = pa_xnew0(char*, 2); 1613 m->device_strings[0] = pa_xstrdup(device_str); 1614 m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT; 1615 1616 ucm_add_mapping(p, m); 1617 if (rate) 1618 m->sample_spec.rate = rate; 1619 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA); 1620 } 1621 1622 /* mapping priority is the highest one of ucm devices */ 1623 if (priority > m->priority) 1624 m->priority = priority; 1625 1626 /* mapping channels is the lowest one of ucm devices */ 1627 if (channels < m->channel_map.channels) 1628 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA); 1629 1630 alsa_mapping_add_ucm_device(m, device); 1631 1632 return 0; 1633} 1634 1635static int ucm_create_mapping_for_modifier( 1636 pa_alsa_ucm_config *ucm, 1637 pa_alsa_profile_set *ps, 1638 pa_alsa_profile *p, 1639 pa_alsa_ucm_modifier *modifier, 1640 const char *verb_name, 1641 const char *mod_name, 1642 const char *device_str, 1643 bool is_sink) { 1644 1645 pa_alsa_mapping *m; 1646 1647 m = ucm_alsa_mapping_get(ucm, ps, verb_name, device_str, is_sink); 1648 1649 if (!m) 1650 return -1; 1651 1652 pa_log_info("UCM mapping: %s modifier %s", m->name, mod_name); 1653 1654 if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */ 1655 m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1656 m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1657 m->ucm_context.ucm = ucm; 1658 m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT; 1659 1660 m->device_strings = pa_xnew0(char*, 2); 1661 m->device_strings[0] = pa_xstrdup(device_str); 1662 m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT; 1663 /* Modifier sinks should not be routed to by default */ 1664 m->priority = 0; 1665 1666 ucm_add_mapping(p, m); 1667 } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */ 1668 m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1669 1670 alsa_mapping_add_ucm_modifier(m, modifier); 1671 1672 return 0; 1673} 1674 1675static int ucm_create_mapping( 1676 pa_alsa_ucm_config *ucm, 1677 pa_alsa_profile_set *ps, 1678 pa_alsa_profile *p, 1679 pa_alsa_ucm_device *device, 1680 const char *verb_name, 1681 const char *device_name, 1682 const char *sink, 1683 const char *source) { 1684 1685 int ret = 0; 1686 1687 if (!sink && !source) { 1688 pa_log("No sink and source at %s: %s", verb_name, device_name); 1689 return -1; 1690 } 1691 1692 if (sink) 1693 ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true); 1694 if (ret == 0 && source) 1695 ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false); 1696 1697 return ret; 1698} 1699 1700static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device) { 1701 pa_alsa_jack *j; 1702 const char *device_name; 1703 const char *jack_control; 1704 const char *mixer_device_name; 1705 char *name; 1706 1707 pa_assert(ucm); 1708 pa_assert(device); 1709 1710 device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME); 1711 1712 jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL); 1713 if (jack_control) { 1714#if SND_LIB_VERSION >= 0x10201 1715 snd_ctl_elem_id_t *ctl; 1716 int err, index; 1717 snd_ctl_elem_id_alloca(&ctl); 1718 err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control); 1719 if (err < 0) 1720 return NULL; 1721 jack_control = snd_ctl_elem_id_get_name(ctl); 1722 index = snd_ctl_elem_id_get_index(ctl); 1723 if (index > 0) { 1724 pa_log("[%s] Invalid JackControl index value: \"%s\",%d", device_name, jack_control, index); 1725 return NULL; 1726 } 1727#else 1728#warning "Upgrade to alsa-lib 1.2.1!" 1729#endif 1730 if (!pa_endswith(jack_control, " Jack")) { 1731 pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control); 1732 return NULL; 1733 } 1734 1735 /* pa_alsa_jack_new() expects a jack name without " Jack" at the 1736 * end, so drop the trailing " Jack". */ 1737 name = pa_xstrndup(jack_control, strlen(jack_control) - 5); 1738 } else { 1739 /* The jack control hasn't been explicitly configured, fail. */ 1740 return NULL; 1741 } 1742 1743 PA_LLIST_FOREACH(j, ucm->jacks) 1744 if (pa_streq(j->name, name)) 1745 goto finish; 1746 1747 mixer_device_name = get_jack_mixer_device(device, true); 1748 if (!mixer_device_name) 1749 mixer_device_name = get_jack_mixer_device(device, false); 1750 if (!mixer_device_name) { 1751 pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control); 1752 return NULL; 1753 } 1754 j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0); 1755 PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j); 1756 1757finish: 1758 pa_xfree(name); 1759 1760 return j; 1761} 1762 1763static int ucm_create_profile( 1764 pa_alsa_ucm_config *ucm, 1765 pa_alsa_profile_set *ps, 1766 pa_alsa_ucm_verb *verb, 1767 const char *verb_name, 1768 const char *verb_desc) { 1769 1770 pa_alsa_profile *p; 1771 pa_alsa_ucm_device *dev; 1772 pa_alsa_ucm_modifier *mod; 1773 int i = 0; 1774 const char *name, *sink, *source; 1775 unsigned int priority; 1776 1777 pa_assert(ps); 1778 1779 if (pa_hashmap_get(ps->profiles, verb_name)) { 1780 pa_log("Verb %s already exists", verb_name); 1781 return -1; 1782 } 1783 1784 p = pa_xnew0(pa_alsa_profile, 1); 1785 p->profile_set = ps; 1786 p->name = pa_xstrdup(verb_name); 1787 p->description = pa_xstrdup(verb_desc); 1788 1789 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1790 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 1791 1792 p->supported = true; 1793 pa_hashmap_put(ps->profiles, p->name, p); 1794 1795 /* TODO: get profile priority from policy management */ 1796 priority = verb->priority; 1797 1798 if (priority == 0) { 1799 char *verb_cmp, *c; 1800 c = verb_cmp = pa_xstrdup(verb_name); 1801 while (*c) { 1802 if (*c == '_') *c = ' '; 1803 c++; 1804 } 1805 for (i = 0; verb_info[i].id; i++) { 1806 if (strcasecmp(verb_info[i].id, verb_cmp) == 0) { 1807 priority = verb_info[i].priority; 1808 break; 1809 } 1810 } 1811 pa_xfree(verb_cmp); 1812 } 1813 1814 p->priority = priority; 1815 1816 PA_LLIST_FOREACH(dev, verb->devices) { 1817 pa_alsa_jack *jack; 1818 const char *jack_hw_mute; 1819 1820 name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME); 1821 1822 sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK); 1823 source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE); 1824 1825 ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source); 1826 1827 jack = ucm_get_jack(ucm, dev); 1828 if (jack) 1829 device_set_jack(dev, jack); 1830 1831 /* JackHWMute contains a list of device names. Each listed device must 1832 * be associated with the jack object that we just created. */ 1833 jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE); 1834 if (jack_hw_mute && !jack) { 1835 pa_log("[%s] JackHWMute set, but JackControl is missing", name); 1836 jack_hw_mute = NULL; 1837 } 1838 if (jack_hw_mute) { 1839 char *hw_mute_device_name; 1840 const char *state = NULL; 1841 1842 while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, &state))) { 1843 pa_alsa_ucm_verb *verb2; 1844 bool device_found = false; 1845 1846 /* Search the referenced device from all verbs. If there are 1847 * multiple verbs that have a device with this name, we add the 1848 * hw mute association to each of those devices. */ 1849 PA_LLIST_FOREACH(verb2, ucm->verbs) { 1850 pa_alsa_ucm_device *hw_mute_device; 1851 1852 hw_mute_device = verb_find_device(verb2, hw_mute_device_name); 1853 if (hw_mute_device) { 1854 device_found = true; 1855 device_add_hw_mute_jack(hw_mute_device, jack); 1856 } 1857 } 1858 1859 if (!device_found) 1860 pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name); 1861 1862 pa_xfree(hw_mute_device_name); 1863 } 1864 } 1865 } 1866 1867 /* Now find modifiers that have their own PlaybackPCM and create 1868 * separate sinks for them. */ 1869 PA_LLIST_FOREACH(mod, verb->modifiers) { 1870 name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); 1871 1872 sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK); 1873 source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE); 1874 1875 if (sink) 1876 ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true); 1877 else if (source) 1878 ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false); 1879 } 1880 1881 pa_alsa_profile_dump(p); 1882 1883 return 0; 1884} 1885 1886static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm) 1887{ 1888 pa_alsa_ucm_mapping_context *context = &m->ucm_context; 1889 pa_alsa_ucm_device *dev; 1890 uint32_t idx; 1891 char *mdev, *alib_prefix; 1892 snd_pcm_info_t *info; 1893 int pcm_card, pcm_device; 1894 1895 snd_pcm_info_alloca(&info); 1896 if (snd_pcm_info(pcm, info) < 0) 1897 return; 1898 1899 if ((pcm_card = snd_pcm_info_get_card(info)) < 0) 1900 return; 1901 if ((pcm_device = snd_pcm_info_get_device(info)) < 0) 1902 return; 1903 1904 alib_prefix = context->ucm->alib_prefix; 1905 1906 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { 1907 mdev = pa_sprintf_malloc("%shw:%i", alib_prefix ? alib_prefix : "", pcm_card); 1908 if (mdev == NULL) 1909 continue; 1910 dev->eld_mixer_device_name = mdev; 1911 dev->eld_device = pcm_device; 1912 } 1913} 1914 1915static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) { 1916 snd_pcm_t* pcm; 1917 pa_sample_spec try_ss = ucm->core->default_sample_spec; 1918 pa_channel_map try_map; 1919 snd_pcm_uframes_t try_period_size, try_buffer_size; 1920 bool exact_channels = m->channel_map.channels > 0; 1921 1922 if (exact_channels) { 1923 try_map = m->channel_map; 1924 try_ss.channels = try_map.channels; 1925 } else 1926 pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA); 1927 1928 try_period_size = 1929 pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) / 1930 pa_frame_size(&try_ss); 1931 try_buffer_size = ucm->core->default_n_fragments * try_period_size; 1932 1933 pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss, 1934 &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels); 1935 1936 if (pcm) { 1937 if (!exact_channels) 1938 m->channel_map = try_map; 1939 mapping_init_eld(m, pcm); 1940 } 1941 1942 return pcm; 1943} 1944 1945static void profile_finalize_probing(pa_alsa_profile *p) { 1946 pa_alsa_mapping *m; 1947 uint32_t idx; 1948 1949 PA_IDXSET_FOREACH(m, p->output_mappings, idx) { 1950 if (p->supported) 1951 m->supported++; 1952 1953 if (!m->output_pcm) 1954 continue; 1955 1956 snd_pcm_close(m->output_pcm); 1957 m->output_pcm = NULL; 1958 } 1959 1960 PA_IDXSET_FOREACH(m, p->input_mappings, idx) { 1961 if (p->supported) 1962 m->supported++; 1963 1964 if (!m->input_pcm) 1965 continue; 1966 1967 snd_pcm_close(m->input_pcm); 1968 m->input_pcm = NULL; 1969 } 1970} 1971 1972static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) { 1973 snd_mixer_t *mixer_handle; 1974 pa_alsa_ucm_mapping_context *context = &m->ucm_context; 1975 pa_alsa_ucm_device *dev; 1976 uint32_t idx; 1977 1978 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { 1979 bool has_control; 1980 1981 if (!dev->jack || !dev->jack->mixer_device_name) 1982 continue; 1983 1984 mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true); 1985 if (!mixer_handle) { 1986 pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name); 1987 continue; 1988 } 1989 1990 has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL; 1991 pa_alsa_jack_set_has_control(dev->jack, has_control); 1992 pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control); 1993 } 1994} 1995 1996static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) { 1997 void *state; 1998 pa_alsa_profile *p; 1999 pa_alsa_mapping *m; 2000 uint32_t idx; 2001 2002 PA_HASHMAP_FOREACH(p, ps->profiles, state) { 2003 /* change verb */ 2004 pa_log_info("Set ucm verb to %s", p->name); 2005 2006 if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) { 2007 pa_log("Failed to set verb %s", p->name); 2008 p->supported = false; 2009 continue; 2010 } 2011 2012 PA_IDXSET_FOREACH(m, p->output_mappings, idx) { 2013 if (PA_UCM_IS_MODIFIER_MAPPING(m)) { 2014 /* Skip jack probing on modifier PCMs since we expect this to 2015 * only be controlled on the main device/verb PCM. */ 2016 continue; 2017 } 2018 2019 m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK); 2020 if (!m->output_pcm) { 2021 p->supported = false; 2022 break; 2023 } 2024 } 2025 2026 if (p->supported) { 2027 PA_IDXSET_FOREACH(m, p->input_mappings, idx) { 2028 if (PA_UCM_IS_MODIFIER_MAPPING(m)) { 2029 /* Skip jack probing on modifier PCMs since we expect this to 2030 * only be controlled on the main device/verb PCM. */ 2031 continue; 2032 } 2033 2034 m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE); 2035 if (!m->input_pcm) { 2036 p->supported = false; 2037 break; 2038 } 2039 } 2040 } 2041 2042 if (!p->supported) { 2043 profile_finalize_probing(p); 2044 continue; 2045 } 2046 2047 pa_log_debug("Profile %s supported.", p->name); 2048 2049 PA_IDXSET_FOREACH(m, p->output_mappings, idx) 2050 if (!PA_UCM_IS_MODIFIER_MAPPING(m)) 2051 ucm_mapping_jack_probe(m, ucm->mixers); 2052 2053 PA_IDXSET_FOREACH(m, p->input_mappings, idx) 2054 if (!PA_UCM_IS_MODIFIER_MAPPING(m)) 2055 ucm_mapping_jack_probe(m, ucm->mixers); 2056 2057 profile_finalize_probing(p); 2058 } 2059 2060 /* restore ucm state */ 2061 snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE); 2062 2063 pa_alsa_profile_set_drop_unsupported(ps); 2064} 2065 2066pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) { 2067 pa_alsa_ucm_verb *verb; 2068 pa_alsa_profile_set *ps; 2069 2070 ps = pa_xnew0(pa_alsa_profile_set, 1); 2071 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 2072 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 2073 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 2074 2075 /* create a profile for each verb */ 2076 PA_LLIST_FOREACH(verb, ucm->verbs) { 2077 const char *verb_name; 2078 const char *verb_desc; 2079 2080 verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME); 2081 verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION); 2082 if (verb_name == NULL) { 2083 pa_log("Verb with no name"); 2084 continue; 2085 } 2086 2087 ucm_create_profile(ucm, ps, verb, verb_name, verb_desc); 2088 } 2089 2090 ucm_probe_profile_set(ucm, ps); 2091 ps->probed = true; 2092 2093 return ps; 2094} 2095 2096static void free_verb(pa_alsa_ucm_verb *verb) { 2097 pa_alsa_ucm_device *di, *dn; 2098 pa_alsa_ucm_modifier *mi, *mn; 2099 2100 PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) { 2101 PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di); 2102 2103 if (di->hw_mute_jacks) 2104 pa_dynarray_free(di->hw_mute_jacks); 2105 2106 if (di->ucm_ports) 2107 pa_dynarray_free(di->ucm_ports); 2108 2109 if (di->playback_volumes) 2110 pa_hashmap_free(di->playback_volumes); 2111 if (di->capture_volumes) 2112 pa_hashmap_free(di->capture_volumes); 2113 2114 pa_proplist_free(di->proplist); 2115 2116 if (di->conflicting_devices) 2117 pa_idxset_free(di->conflicting_devices, NULL); 2118 if (di->supported_devices) 2119 pa_idxset_free(di->supported_devices, NULL); 2120 2121 pa_xfree(di->eld_mixer_device_name); 2122 2123 pa_xfree(di); 2124 } 2125 2126 PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) { 2127 PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi); 2128 pa_proplist_free(mi->proplist); 2129 if (mi->n_suppdev > 0) 2130 snd_use_case_free_list(mi->supported_devices, mi->n_suppdev); 2131 if (mi->n_confdev > 0) 2132 snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev); 2133 pa_xfree(mi->media_role); 2134 pa_xfree(mi); 2135 } 2136 pa_proplist_free(verb->proplist); 2137 pa_xfree(verb); 2138} 2139 2140static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) { 2141 pa_alsa_ucm_device *device; 2142 2143 pa_assert(verb); 2144 pa_assert(device_name); 2145 2146 PA_LLIST_FOREACH(device, verb->devices) { 2147 const char *name; 2148 2149 name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME); 2150 if (pa_streq(name, device_name)) 2151 return device; 2152 } 2153 2154 return NULL; 2155} 2156 2157void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) { 2158 pa_alsa_ucm_verb *vi, *vn; 2159 pa_alsa_jack *ji, *jn; 2160 2161 PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) { 2162 PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi); 2163 free_verb(vi); 2164 } 2165 PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) { 2166 PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji); 2167 pa_alsa_jack_free(ji); 2168 } 2169 if (ucm->ucm_mgr) { 2170 snd_use_case_mgr_close(ucm->ucm_mgr); 2171 ucm->ucm_mgr = NULL; 2172 } 2173 pa_xfree(ucm->alib_prefix); 2174 ucm->alib_prefix = NULL; 2175} 2176 2177void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) { 2178 pa_alsa_ucm_device *dev; 2179 pa_alsa_ucm_modifier *mod; 2180 uint32_t idx; 2181 2182 if (context->ucm_devices) { 2183 /* clear ucm device pointer to mapping */ 2184 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { 2185 if (context->direction == PA_DIRECTION_OUTPUT) 2186 dev->playback_mapping = NULL; 2187 else 2188 dev->capture_mapping = NULL; 2189 } 2190 2191 pa_idxset_free(context->ucm_devices, NULL); 2192 } 2193 2194 if (context->ucm_modifiers) { 2195 PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) { 2196 if (context->direction == PA_DIRECTION_OUTPUT) 2197 mod->playback_mapping = NULL; 2198 else 2199 mod->capture_mapping = NULL; 2200 } 2201 2202 pa_idxset_free(context->ucm_modifiers, NULL); 2203 } 2204} 2205 2206/* Enable the modifier when the first stream with matched role starts */ 2207void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { 2208 pa_alsa_ucm_modifier *mod; 2209 2210 if (!ucm->active_verb) 2211 return; 2212 2213 PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) { 2214 if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { 2215 if (mod->enabled_counter == 0) { 2216 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); 2217 2218 pa_log_info("Enable ucm modifier %s", mod_name); 2219 if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) { 2220 pa_log("Failed to enable ucm modifier %s", mod_name); 2221 } 2222 } 2223 2224 mod->enabled_counter++; 2225 break; 2226 } 2227 } 2228} 2229 2230/* Disable the modifier when the last stream with matched role ends */ 2231void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { 2232 pa_alsa_ucm_modifier *mod; 2233 2234 if (!ucm->active_verb) 2235 return; 2236 2237 PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) { 2238 if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { 2239 2240 mod->enabled_counter--; 2241 if (mod->enabled_counter == 0) { 2242 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); 2243 2244 pa_log_info("Disable ucm modifier %s", mod_name); 2245 if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) { 2246 pa_log("Failed to disable ucm modifier %s", mod_name); 2247 } 2248 } 2249 2250 break; 2251 } 2252 } 2253} 2254 2255static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) { 2256 pa_assert(device); 2257 pa_assert(port); 2258 2259 pa_dynarray_append(device->ucm_ports, port); 2260} 2261 2262static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { 2263 pa_assert(device); 2264 pa_assert(jack); 2265 2266 device->jack = jack; 2267 pa_alsa_jack_add_ucm_device(jack, device); 2268 2269 pa_alsa_ucm_device_update_available(device); 2270} 2271 2272static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { 2273 pa_assert(device); 2274 pa_assert(jack); 2275 2276 pa_dynarray_append(device->hw_mute_jacks, jack); 2277 pa_alsa_jack_add_ucm_hw_mute_device(jack, device); 2278 2279 pa_alsa_ucm_device_update_available(device); 2280} 2281 2282static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) { 2283 pa_alsa_ucm_port_data *port; 2284 unsigned idx; 2285 2286 pa_assert(device); 2287 2288 if (available == device->available) 2289 return; 2290 2291 device->available = available; 2292 2293 PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx) 2294 ucm_port_update_available(port); 2295} 2296 2297void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { 2298 pa_available_t available = PA_AVAILABLE_UNKNOWN; 2299 pa_alsa_jack *jack; 2300 unsigned idx; 2301 2302 pa_assert(device); 2303 2304 if (device->jack && device->jack->has_control) 2305 available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO; 2306 2307 PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) { 2308 if (jack->plugged_in) { 2309 available = PA_AVAILABLE_NO; 2310 break; 2311 } 2312 } 2313 2314 device_set_available(device, available); 2315} 2316 2317static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, 2318 pa_alsa_ucm_device **devices, unsigned n_devices) { 2319 unsigned i; 2320 2321 pa_assert(ucm); 2322 pa_assert(core_port); 2323 pa_assert(devices); 2324 2325 port->ucm = ucm; 2326 port->core_port = core_port; 2327 port->devices = pa_dynarray_new(NULL); 2328 port->eld_device = -1; 2329 2330 for (i = 0; i < n_devices; i++) { 2331 pa_dynarray_append(port->devices, devices[i]); 2332 device_add_ucm_port(devices[i], port); 2333 } 2334 2335 port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, 2336 (pa_free_cb_t) pa_alsa_path_free); 2337 2338 ucm_port_update_available(port); 2339} 2340 2341static void ucm_port_data_free(pa_device_port *port) { 2342 pa_alsa_ucm_port_data *ucm_port; 2343 2344 pa_assert(port); 2345 2346 ucm_port = PA_DEVICE_PORT_DATA(port); 2347 2348 if (ucm_port->devices) 2349 pa_dynarray_free(ucm_port->devices); 2350 2351 if (ucm_port->paths) 2352 pa_hashmap_free(ucm_port->paths); 2353 2354 pa_xfree(ucm_port->eld_mixer_device_name); 2355} 2356 2357static void ucm_port_update_available(pa_alsa_ucm_port_data *port) { 2358 pa_alsa_ucm_device *device; 2359 unsigned idx; 2360 pa_available_t available = PA_AVAILABLE_YES; 2361 2362 pa_assert(port); 2363 2364 PA_DYNARRAY_FOREACH(device, port->devices, idx) { 2365 if (device->available == PA_AVAILABLE_UNKNOWN) 2366 available = PA_AVAILABLE_UNKNOWN; 2367 else if (device->available == PA_AVAILABLE_NO) { 2368 available = PA_AVAILABLE_NO; 2369 break; 2370 } 2371 } 2372 2373 pa_device_port_set_available(port->core_port, available); 2374} 2375 2376#else /* HAVE_ALSA_UCM */ 2377 2378/* Dummy functions for systems without UCM support */ 2379 2380int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) { 2381 pa_log_info("UCM not available."); 2382 return -1; 2383} 2384 2385pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) { 2386 return NULL; 2387} 2388 2389int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) { 2390 return -1; 2391} 2392 2393int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb) { 2394 return -1; 2395} 2396 2397void pa_alsa_ucm_add_ports( 2398 pa_hashmap **hash, 2399 pa_proplist *proplist, 2400 pa_alsa_ucm_mapping_context *context, 2401 bool is_sink, 2402 pa_card *card, 2403 snd_pcm_t *pcm_handle, 2404 bool ignore_dB) { 2405} 2406 2407void pa_alsa_ucm_add_ports_combination( 2408 pa_hashmap *hash, 2409 pa_alsa_ucm_mapping_context *context, 2410 bool is_sink, 2411 pa_hashmap *ports, 2412 pa_card_profile *cp, 2413 pa_core *core) { 2414} 2415 2416int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) { 2417 return -1; 2418} 2419 2420void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) { 2421} 2422 2423void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) { 2424} 2425 2426void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { 2427} 2428 2429void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { 2430} 2431 2432#endif 2433