2
This file is part of PulseAudio.
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.
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.
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.
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/>.
28
#include <sys/types.h>
30
#include <asoundlib.h>
32
#ifdef HAVE_VALGRIND_MEMCHECK_H
33
#include <valgrind/memcheck.h>
36
#include <pulse/sample.h>
37
#include <pulse/xmalloc.h>
38
#include <pulse/timeval.h>
39
#include <pulse/util.h>
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>
51
#include "alsa-mixer.h"
52
#include "alsa-util.h"
55
#define PA_UCM_PRE_TAG_OUTPUT "[Out] "
56
#define PA_UCM_PRE_TAG_INPUT "[In] "
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) \
62
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \
63
if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \
65
#define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
79
static struct ucm_items item[] = {
80
{"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
81
{"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
82
{"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
83
{"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
84
{"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
85
{"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
86
{"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
87
{"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
88
{"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
89
{"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
90
{"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
91
{"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
92
{"TQ", PA_ALSA_PROP_UCM_QOS},
96
/* UCM verb info - this should eventually be part of policy manangement */
97
static struct ucm_info verb_info[] = {
98
{SND_USE_CASE_VERB_INACTIVE, 0},
99
{SND_USE_CASE_VERB_HIFI, 8000},
100
{SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
101
{SND_USE_CASE_VERB_VOICE, 6000},
102
{SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
103
{SND_USE_CASE_VERB_VOICECALL, 4000},
104
{SND_USE_CASE_VERB_IP_VOICECALL, 4000},
105
{SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
106
{SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
110
/* UCM device info - should be overwritten by ucm property */
111
static struct ucm_info dev_info[] = {
112
{SND_USE_CASE_DEV_SPEAKER, 100},
113
{SND_USE_CASE_DEV_LINE, 100},
114
{SND_USE_CASE_DEV_HEADPHONES, 100},
115
{SND_USE_CASE_DEV_HEADSET, 300},
116
{SND_USE_CASE_DEV_HANDSET, 200},
117
{SND_USE_CASE_DEV_BLUETOOTH, 400},
118
{SND_USE_CASE_DEV_EARPIECE, 100},
119
{SND_USE_CASE_DEV_SPDIF, 100},
120
{SND_USE_CASE_DEV_HDMI, 100},
121
{SND_USE_CASE_DEV_NONE, 100},
125
/* UCM profile properties - The verb data is store so it can be used to fill
126
* the new profiles properties */
127
static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
132
for (i = 0; item[i].id; i++) {
135
id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
136
err = snd_use_case_get(uc_mgr, id, &value);
141
pa_log_debug("Got %s for verb %s: %s", item[i].id, verb_name, value);
142
pa_proplist_sets(verb->proplist, item[i].property, value);
149
static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
150
pa_alsa_ucm_device *d;
153
PA_IDXSET_FOREACH(d, idxset, idx)
160
static void ucm_add_devices_to_idxset(
162
pa_alsa_ucm_device *me,
163
pa_alsa_ucm_device *devices,
164
const char **dev_names,
167
pa_alsa_ucm_device *d;
169
PA_LLIST_FOREACH(d, devices) {
176
name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
178
for (i = 0; i < n; i++)
179
if (pa_streq(dev_names[i], name))
180
pa_idxset_put(idxset, d, NULL);
184
/* Create a property list for this ucm device */
185
static int ucm_get_device_property(
186
pa_alsa_ucm_device *device,
187
snd_use_case_mgr_t *uc_mgr,
188
pa_alsa_ucm_verb *verb,
189
const char *device_name) {
192
const char **devices;
197
int n_confdev, n_suppdev;
199
for (i = 0; item[i].id; i++) {
200
id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
201
err = snd_use_case_get(uc_mgr, id, &value);
206
pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
207
pa_proplist_sets(device->proplist, item[i].property, value);
211
/* get direction and channels */
212
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
213
if (value) { /* output */
215
if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
216
device->playback_channels = ui;
218
pa_log("UCM playback channels %s for device %s out of range", value, device_name);
221
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
222
if (!value) { /* take pcm from verb playback default */
223
value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SINK);
225
pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name, value);
226
pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SINK, value);
228
pa_log("UCM playback device %s fetch pcm failed", device_name);
232
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
233
if (value) { /* input */
235
if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
236
device->capture_channels = ui;
238
pa_log("UCM capture channels %s for device %s out of range", value, device_name);
241
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
242
if (!value) { /* take pcm from verb capture default */
243
value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SOURCE);
245
pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name, value);
246
pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SOURCE, value);
248
pa_log("UCM capture device %s fetch pcm failed", device_name);
252
if (device->playback_channels == 0 && device->capture_channels == 0) {
253
pa_log_warn("UCM file does not specify 'PlaybackChannels' or 'CaptureChannels'"
254
"for device %s, assuming stereo duplex.", device_name);
255
device->playback_channels = 2;
256
device->capture_channels = 2;
259
/* get rate and priority of device */
260
if (device->playback_channels) { /* sink device */
262
if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE)) ||
263
(value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
264
if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
265
pa_log_debug("UCM playback device %s rate %d", device_name, ui);
266
device->playback_rate = ui;
268
pa_log_debug("UCM playback device %s has bad rate %s", device_name, value);
271
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
273
/* get priority from ucm config */
274
if (pa_atou(value, &ui) == 0)
275
device->playback_priority = ui;
277
pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
281
if (device->capture_channels) { /* source device */
283
if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE)) ||
284
(value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
285
if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
286
pa_log_debug("UCM capture device %s rate %d", device_name, ui);
287
device->capture_rate = ui;
289
pa_log_debug("UCM capture device %s has bad rate %s", device_name, value);
292
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
294
/* get priority from ucm config */
295
if (pa_atou(value, &ui) == 0)
296
device->capture_priority = ui;
298
pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
302
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
303
/* get priority from static table */
304
for (i = 0; dev_info[i].id; i++) {
305
if (strcasecmp(dev_info[i].id, device_name) == 0) {
306
PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
312
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
313
/* fall through to default priority */
314
device->playback_priority = 100;
317
if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
318
/* fall through to default priority */
319
device->capture_priority = 100;
322
id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
323
n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
327
pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
329
device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
330
ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
331
snd_use_case_free_list(devices, n_confdev);
334
id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
335
n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
339
pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
341
device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
342
ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
343
snd_use_case_free_list(devices, n_suppdev);
349
/* Create a property list for this ucm modifier */
350
static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
355
for (i = 0; item[i].id; i++) {
358
id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
359
err = snd_use_case_get(uc_mgr, id, &value);
364
pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
365
pa_proplist_sets(modifier->proplist, item[i].property, value);
369
id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
370
modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
372
if (modifier->n_confdev < 0)
373
pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
375
id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
376
modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
378
if (modifier->n_suppdev < 0)
379
pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
384
/* Create a list of devices for this verb */
385
static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
386
const char **dev_list;
389
num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
393
for (i = 0; i < num_dev; i += 2) {
394
pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
396
d->proplist = pa_proplist_new();
397
pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
398
pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
400
PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
403
snd_use_case_free_list(dev_list, num_dev);
408
static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
409
const char **mod_list;
412
num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
416
for (i = 0; i < num_mod; i += 2) {
417
pa_alsa_ucm_modifier *m;
420
pa_log_warn("Got a modifier with a null name. Skipping.");
424
m = pa_xnew0(pa_alsa_ucm_modifier, 1);
425
m->proplist = pa_proplist_new();
427
pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
428
pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
430
PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
433
snd_use_case_free_list(mod_list, num_mod);
438
static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
439
const char *cur = pa_proplist_gets(dev->proplist, role_name);
442
pa_proplist_sets(dev->proplist, role_name, role);
443
else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
444
char *value = pa_sprintf_malloc("%s %s", cur, role);
446
pa_proplist_sets(dev->proplist, role_name, value);
450
pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
454
static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
455
pa_alsa_ucm_device *d;
457
PA_LLIST_FOREACH(d, list) {
458
const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
460
if (pa_streq(dev_name, name)) {
461
const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
462
const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
465
add_role_to_device(d, dev_name, role_name, role);
466
else if (!is_sink && source)
467
add_role_to_device(d, dev_name, role_name, role);
473
static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
474
char *sub = NULL, *tmp;
478
if (pa_startswith(mod_name, "Play")) {
480
sub = pa_xstrdup(mod_name + 4);
481
} else if (pa_startswith(mod_name, "Capture"))
482
sub = pa_xstrdup(mod_name + 7);
486
pa_log_warn("Can't match media roles for modifer %s", mod_name);
493
*tmp = tolower(*tmp);
499
static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
501
bool is_sink = false;
503
const char *role_name;
505
sub = modifier_name_to_role(mod_name, &is_sink);
509
modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
510
modifier->media_role = sub;
512
role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
513
for (i = 0; i < modifier->n_suppdev; i++) {
514
/* if modifier has no specific pcm, we add role intent to its supported devices */
515
if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
516
!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
517
add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
521
static void append_lost_relationship(pa_alsa_ucm_device *dev) {
523
pa_alsa_ucm_device *d;
525
if (dev->conflicting_devices) {
526
PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
527
if (!d->conflicting_devices)
528
d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
530
if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
531
pa_log_warn("Add lost conflicting device %s to %s",
532
pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
533
pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
537
if (dev->supported_devices) {
538
PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
539
if (!d->supported_devices)
540
d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
542
if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
543
pa_log_warn("Add lost supported device %s to %s",
544
pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
545
pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
550
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
552
const char **verb_list;
553
int num_verbs, i, err = 0;
555
/* is UCM available for this card ? */
556
err = snd_card_get_name(card_index, &card_name);
558
pa_log("Card can't get card_name from card_index %d", card_index);
562
err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
564
pa_log_info("UCM not available for card %s", card_name);
568
pa_log_info("UCM available for card %s", card_name);
570
/* get a list of all UCM verbs (profiles) for this card */
571
num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
573
pa_log("UCM verb list not found for %s", card_name);
577
/* get the properties of each UCM verb */
578
for (i = 0; i < num_verbs; i += 2) {
579
pa_alsa_ucm_verb *verb;
581
/* Get devices and modifiers for each verb */
582
err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
584
pa_log("Failed to get the verb %s", verb_list[i]);
588
PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
592
pa_log("No UCM verb is valid for %s", card_name);
596
snd_use_case_free_list(verb_list, num_verbs);
600
snd_use_case_mgr_close(ucm->ucm_mgr);
611
int 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) {
612
pa_alsa_ucm_device *d;
613
pa_alsa_ucm_modifier *mod;
614
pa_alsa_ucm_verb *verb;
618
pa_log_info("Set UCM verb to %s", verb_name);
619
err = snd_use_case_set(uc_mgr, "_verb", verb_name);
623
verb = pa_xnew0(pa_alsa_ucm_verb, 1);
624
verb->proplist = pa_proplist_new();
626
pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
627
pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
629
err = ucm_get_devices(verb, uc_mgr);
631
pa_log("No UCM devices for verb %s", verb_name);
633
err = ucm_get_modifiers(verb, uc_mgr);
635
pa_log("No UCM modifiers for verb %s", verb_name);
637
/* Verb properties */
638
ucm_get_property(verb, uc_mgr, verb_name);
640
PA_LLIST_FOREACH(d, verb->devices) {
641
const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
643
/* Devices properties */
644
ucm_get_device_property(d, uc_mgr, verb, dev_name);
646
/* make conflicting or supported device mutual */
647
PA_LLIST_FOREACH(d, verb->devices)
648
append_lost_relationship(d);
650
PA_LLIST_FOREACH(mod, verb->modifiers) {
651
const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
653
/* Modifier properties */
654
ucm_get_modifier_property(mod, uc_mgr, mod_name);
656
/* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
657
pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
658
ucm_set_media_roles(mod, verb->devices, mod_name);
665
static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
666
const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
667
const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
669
return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
672
static void ucm_add_port_combination(
674
pa_alsa_ucm_mapping_context *context,
676
pa_alsa_ucm_device **pdevices,
682
pa_device_port *port;
687
const char *dev_name;
688
const char *direction;
689
pa_alsa_ucm_device *sorted[num], *dev;
691
for (i = 0; i < num; i++)
692
sorted[i] = pdevices[i];
694
/* Sort by alphabetical order so as to have a deterministic naming scheme
695
* for combination ports */
696
qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
699
dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
701
name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
702
desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
703
: pa_sprintf_malloc("Combination port for %s", dev_name);
705
priority = is_sink ? dev->playback_priority : dev->capture_priority;
706
prio2 = (priority == 0 ? 0 : 1.0/priority);
708
for (i = 1; i < num; i++) {
712
dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
714
tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
718
tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
722
priority = is_sink ? dev->playback_priority : dev->capture_priority;
723
if (priority != 0 && prio2 > 0)
724
prio2 += 1.0/priority;
727
/* Make combination ports always have lower priority, and use the formula
728
1/p = 1/p1 + 1/p2 + ... 1/pn.
729
This way, the result will always be less than the individual components,
730
yet higher components will lead to higher result. */
733
priority = prio2 > 0 ? 1.0/prio2 : 0;
735
port = pa_hashmap_get(ports, name);
737
pa_device_port_new_data port_data;
739
pa_device_port_new_data_init(&port_data);
740
pa_device_port_new_data_set_name(&port_data, name);
741
pa_device_port_new_data_set_description(&port_data, desc);
742
pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
744
port = pa_device_port_new(core, &port_data, 0);
745
pa_device_port_new_data_done(&port_data);
748
pa_hashmap_put(ports, port->name, port);
749
pa_log_debug("Add port %s: %s", port->name, port->description);
750
port->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
753
port->priority = priority;
758
direction = is_sink ? "output" : "input";
759
pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
762
pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
763
pa_hashmap_put(port->profiles, cp->name, cp);
767
pa_hashmap_put(hash, port->name, port);
768
pa_device_port_ref(port);
772
static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
775
const char *state = NULL;
778
if (!port_name || !dev_name)
781
port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
783
while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
784
if (!strncmp(r, dev_name, len)) {
793
static int ucm_check_conformance(
794
pa_alsa_ucm_mapping_context *context,
795
pa_alsa_ucm_device **pdevices,
797
pa_alsa_ucm_device *dev) {
800
pa_alsa_ucm_device *d;
805
pa_log_debug("Check device %s conformance with %d other devices",
806
pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
808
pa_log_debug("First device in combination, number 1");
812
if (dev->conflicting_devices) { /* the device defines conflicting devices */
813
PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
814
for (i = 0; i < dev_num; i++) {
815
if (pdevices[i] == d) {
816
pa_log_debug("Conflicting device found");
821
} else if (dev->supported_devices) { /* the device defines supported devices */
822
for (i = 0; i < dev_num; i++) {
823
if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
824
pa_log_debug("Supported device not found");
828
} else { /* not support any other devices */
829
pa_log_debug("Not support any other devices");
833
pa_log_debug("Device added to combination, number %d", dev_num + 1);
837
static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
838
pa_alsa_ucm_device *dev;
840
if (*idx == PA_IDXSET_INVALID)
841
dev = pa_idxset_first(idxset, idx);
843
dev = pa_idxset_next(idxset, idx);
848
static void ucm_add_ports_combination(
850
pa_alsa_ucm_mapping_context *context,
852
pa_alsa_ucm_device **pdevices,
859
pa_alsa_ucm_device *dev;
860
uint32_t idx = map_index;
862
if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
865
/* check if device at map_index can combine with existing devices combination */
866
if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
867
/* add device at map_index to devices combination */
868
pdevices[dev_num] = dev;
869
/* add current devices combination as a new port */
870
ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
871
/* try more elements combination */
872
ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
875
/* try other device with current elements number */
876
ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
879
static char* merge_roles(const char *cur, const char *add) {
881
const char *state = NULL;
884
return pa_xstrdup(cur);
885
else if (cur == NULL)
886
return pa_xstrdup(add);
888
ret = pa_xstrdup(cur);
890
while ((r = pa_split_spaces(add, &state))) {
893
if (!pa_str_in_list_spaces(ret, r))
894
value = pa_sprintf_malloc("%s %s", ret, r);
908
void pa_alsa_ucm_add_ports_combination(
910
pa_alsa_ucm_mapping_context *context,
916
pa_alsa_ucm_device **pdevices;
918
pa_assert(context->ucm_devices);
920
if (pa_idxset_size(context->ucm_devices) > 0) {
921
pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
922
ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
927
void pa_alsa_ucm_add_ports(
929
pa_proplist *proplist,
930
pa_alsa_ucm_mapping_context *context,
936
const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
937
pa_alsa_ucm_device *dev;
938
pa_alsa_ucm_modifier *mod;
944
/* add ports first */
945
pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
947
/* then set property PA_PROP_DEVICE_INTENDED_ROLES */
948
merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
949
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
950
const char *roles = pa_proplist_gets(dev->proplist, role_name);
951
tmp = merge_roles(merged_roles, roles);
952
pa_xfree(merged_roles);
956
if (context->ucm_modifiers)
957
PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
958
tmp = merge_roles(merged_roles, mod->media_role);
959
pa_xfree(merged_roles);
964
pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
966
pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
967
pa_xfree(merged_roles);
970
/* Change UCM verb and device to match selected card profile */
971
int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
974
pa_alsa_ucm_verb *verb;
976
if (new_profile == old_profile)
978
else if (new_profile == NULL || old_profile == NULL)
979
profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
980
else if (!pa_streq(new_profile, old_profile))
981
profile = new_profile;
986
pa_log_info("Set UCM verb to %s", profile);
987
if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
988
pa_log("Failed to set verb %s", profile);
992
/* find active verb */
993
ucm->active_verb = NULL;
994
PA_LLIST_FOREACH(verb, ucm->verbs) {
995
const char *verb_name;
996
verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
997
if (pa_streq(verb_name, profile)) {
998
ucm->active_verb = verb;
1006
int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1009
pa_alsa_ucm_config *ucm;
1010
const char **enable_devs;
1013
pa_alsa_ucm_device *dev;
1015
pa_assert(context && context->ucm);
1018
pa_assert(ucm->ucm_mgr);
1020
enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
1022
/* first disable then enable */
1023
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1024
const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1026
if (ucm_port_contains(port->name, dev_name, is_sink))
1027
enable_devs[enable_num++] = dev_name;
1029
pa_log_debug("Disable ucm device %s", dev_name);
1030
if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
1031
pa_log("Failed to disable ucm device %s", dev_name);
1038
for (i = 0; i < enable_num; i++) {
1039
pa_log_debug("Enable ucm device %s", enable_devs[i]);
1040
if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
1041
pa_log("Failed to enable ucm device %s", enable_devs[i]);
1047
pa_xfree(enable_devs);
1052
static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1054
switch (m->direction) {
1055
case PA_ALSA_DIRECTION_ANY:
1056
pa_idxset_put(p->output_mappings, m, NULL);
1057
pa_idxset_put(p->input_mappings, m, NULL);
1059
case PA_ALSA_DIRECTION_OUTPUT:
1060
pa_idxset_put(p->output_mappings, m, NULL);
1062
case PA_ALSA_DIRECTION_INPUT:
1063
pa_idxset_put(p->input_mappings, m, NULL);
1068
static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
1070
const char *new_desc;
1072
pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
1074
new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1075
cur_desc = m->description;
1077
m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1079
m->description = pa_xstrdup(new_desc);
1082
/* walk around null case */
1083
m->description = m->description ? m->description : pa_xstrdup("");
1085
/* save mapping to ucm device */
1086
if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
1087
device->playback_mapping = m;
1089
device->capture_mapping = m;
1092
static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
1094
const char *new_desc, *mod_name, *channel_str;
1095
uint32_t channels = 0;
1097
pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
1099
new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1100
cur_desc = m->description;
1102
m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1104
m->description = pa_xstrdup(new_desc);
1107
m->description = m->description ? m->description : pa_xstrdup("");
1109
/* Modifier sinks should not be routed to by default */
1112
mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
1113
pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
1115
/* save mapping to ucm modifier */
1116
if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
1117
modifier->playback_mapping = m;
1118
channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
1120
modifier->capture_mapping = m;
1121
channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
1125
/* FIXME: channel_str is unsanitized input from the UCM configuration,
1126
* we should do proper error handling instead of asserting.
1127
* https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
1128
pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
1129
pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
1133
pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1135
pa_channel_map_init(&m->channel_map);
1138
static int ucm_create_mapping_direction(
1139
pa_alsa_ucm_config *ucm,
1140
pa_alsa_profile_set *ps,
1142
pa_alsa_ucm_device *device,
1143
const char *verb_name,
1144
const char *device_name,
1145
const char *device_str,
1150
unsigned priority, rate, channels;
1152
mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
1154
m = pa_alsa_mapping_get(ps, mapping_name);
1156
pa_log("No mapping for %s", mapping_name);
1157
pa_xfree(mapping_name);
1160
pa_log_debug("UCM mapping: %s dev %s", mapping_name, device_name);
1161
pa_xfree(mapping_name);
1163
priority = is_sink ? device->playback_priority : device->capture_priority;
1164
rate = is_sink ? device->playback_rate : device->capture_rate;
1165
channels = is_sink ? device->playback_channels : device->capture_channels;
1167
if (!m->ucm_context.ucm_devices) { /* new mapping */
1168
m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1169
m->ucm_context.ucm = ucm;
1170
m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1172
m->device_strings = pa_xnew0(char*, 2);
1173
m->device_strings[0] = pa_xstrdup(device_str);
1174
m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1176
ucm_add_mapping(p, m);
1178
m->sample_spec.rate = rate;
1179
pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1182
/* mapping priority is the highest one of ucm devices */
1183
if (priority > m->priority)
1184
m->priority = priority;
1186
/* mapping channels is the lowest one of ucm devices */
1187
if (channels < m->channel_map.channels)
1188
pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1190
alsa_mapping_add_ucm_device(m, device);
1195
static int ucm_create_mapping_for_modifier(
1196
pa_alsa_ucm_config *ucm,
1197
pa_alsa_profile_set *ps,
1199
pa_alsa_ucm_modifier *modifier,
1200
const char *verb_name,
1201
const char *mod_name,
1202
const char *device_str,
1208
mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
1210
m = pa_alsa_mapping_get(ps, mapping_name);
1212
pa_log("no mapping for %s", mapping_name);
1213
pa_xfree(mapping_name);
1216
pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
1217
pa_xfree(mapping_name);
1219
if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */
1220
m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1221
m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1222
m->ucm_context.ucm = ucm;
1223
m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1225
m->device_strings = pa_xnew0(char*, 2);
1226
m->device_strings[0] = pa_xstrdup(device_str);
1227
m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1228
/* Modifier sinks should not be routed to by default */
1231
ucm_add_mapping(p, m);
1232
} else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
1233
m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1235
alsa_mapping_add_ucm_modifier(m, modifier);
1240
static int ucm_create_mapping(
1241
pa_alsa_ucm_config *ucm,
1242
pa_alsa_profile_set *ps,
1244
pa_alsa_ucm_device *device,
1245
const char *verb_name,
1246
const char *device_name,
1248
const char *source) {
1252
if (!sink && !source) {
1253
pa_log("No sink and source at %s: %s", verb_name, device_name);
1258
ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
1259
if (ret == 0 && source)
1260
ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
1265
static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, const char *dev_name, const char *pre_tag) {
1267
char *name = pa_sprintf_malloc("%s%s", pre_tag, dev_name);
1269
PA_LLIST_FOREACH(j, ucm->jacks)
1270
if (pa_streq(j->name, name))
1273
j = pa_xnew0(pa_alsa_jack, 1);
1274
j->state_unplugged = PA_AVAILABLE_NO;
1275
j->state_plugged = PA_AVAILABLE_YES;
1276
j->name = pa_xstrdup(name);
1277
j->alsa_name = pa_sprintf_malloc("%s Jack", dev_name);
1279
PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
1286
static int ucm_create_profile(
1287
pa_alsa_ucm_config *ucm,
1288
pa_alsa_profile_set *ps,
1289
pa_alsa_ucm_verb *verb,
1290
const char *verb_name,
1291
const char *verb_desc) {
1294
pa_alsa_ucm_device *dev;
1295
pa_alsa_ucm_modifier *mod;
1297
const char *name, *sink, *source;
1302
if (pa_hashmap_get(ps->profiles, verb_name)) {
1303
pa_log("Verb %s already exists", verb_name);
1307
p = pa_xnew0(pa_alsa_profile, 1);
1308
p->profile_set = ps;
1309
p->name = pa_xstrdup(verb_name);
1310
p->description = pa_xstrdup(verb_desc);
1312
p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1313
p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1315
p->supported = true;
1316
pa_hashmap_put(ps->profiles, p->name, p);
1318
/* TODO: get profile priority from ucm info or policy management */
1319
c = verb_cmp = pa_xstrdup(verb_name);
1321
if (*c == '_') *c = ' ';
1325
for (i = 0; verb_info[i].id; i++) {
1326
if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
1327
p->priority = verb_info[i].priority;
1334
if (verb_info[i].id == NULL)
1337
PA_LLIST_FOREACH(dev, verb->devices) {
1338
name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1340
sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1341
source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1343
ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
1346
dev->output_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_OUTPUT);
1348
dev->input_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_INPUT);
1351
/* Now find modifiers that have their own PlaybackPCM and create
1352
* separate sinks for them. */
1353
PA_LLIST_FOREACH(mod, verb->modifiers) {
1354
name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1356
sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
1357
source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1360
ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
1362
ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
1365
pa_alsa_profile_dump(p);
1370
static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
1372
pa_sample_spec try_ss = ucm->core->default_sample_spec;
1373
pa_channel_map try_map;
1374
snd_pcm_uframes_t try_period_size, try_buffer_size;
1375
bool exact_channels = m->channel_map.channels > 0;
1377
if (exact_channels) {
1378
try_map = m->channel_map;
1379
try_ss.channels = try_map.channels;
1381
pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
1384
pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
1385
pa_frame_size(&try_ss);
1386
try_buffer_size = ucm->core->default_n_fragments * try_period_size;
1388
pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
1389
&try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
1391
if (pcm && !exact_channels)
1392
m->channel_map = try_map;
1397
static void profile_finalize_probing(pa_alsa_profile *p) {
1401
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
1408
snd_pcm_close(m->output_pcm);
1409
m->output_pcm = NULL;
1412
PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
1419
snd_pcm_close(m->input_pcm);
1420
m->input_pcm = NULL;
1424
static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
1425
snd_pcm_t *pcm_handle;
1426
snd_mixer_t *mixer_handle;
1427
pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1428
pa_alsa_ucm_device *dev;
1431
pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : m->input_pcm;
1432
mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
1436
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1438
jack = m->direction == PA_ALSA_DIRECTION_OUTPUT ? dev->output_jack : dev->input_jack;
1440
jack->has_control = pa_alsa_mixer_find(mixer_handle, jack->alsa_name, 0) != NULL;
1441
pa_log_info("UCM jack %s has_control=%d", jack->name, jack->has_control);
1444
snd_mixer_close(mixer_handle);
1447
static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
1453
PA_HASHMAP_FOREACH(p, ps->profiles, state) {
1455
pa_log_info("Set ucm verb to %s", p->name);
1457
if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
1458
pa_log("Failed to set verb %s", p->name);
1459
p->supported = false;
1463
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
1464
if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
1465
/* Skip jack probing on modifier PCMs since we expect this to
1466
* only be controlled on the main device/verb PCM. */
1470
m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
1471
if (!m->output_pcm) {
1472
p->supported = false;
1478
PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
1479
if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
1480
/* Skip jack probing on modifier PCMs since we expect this to
1481
* only be controlled on the main device/verb PCM. */
1485
m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
1486
if (!m->input_pcm) {
1487
p->supported = false;
1493
if (!p->supported) {
1494
profile_finalize_probing(p);
1498
pa_log_debug("Profile %s supported.", p->name);
1500
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
1501
if (!PA_UCM_IS_MODIFIER_MAPPING(m))
1502
ucm_mapping_jack_probe(m);
1504
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
1505
if (!PA_UCM_IS_MODIFIER_MAPPING(m))
1506
ucm_mapping_jack_probe(m);
1508
profile_finalize_probing(p);
1511
/* restore ucm state */
1512
snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
1514
pa_alsa_profile_set_drop_unsupported(ps);
1517
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
1518
pa_alsa_ucm_verb *verb;
1519
pa_alsa_profile_set *ps;
1521
ps = pa_xnew0(pa_alsa_profile_set, 1);
1522
ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1523
ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1524
ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1526
/* create a profile for each verb */
1527
PA_LLIST_FOREACH(verb, ucm->verbs) {
1528
const char *verb_name;
1529
const char *verb_desc;
1531
verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1532
verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1533
if (verb_name == NULL) {
1534
pa_log("Verb with no name");
1538
ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
1541
ucm_probe_profile_set(ucm, ps);
1547
static void free_verb(pa_alsa_ucm_verb *verb) {
1548
pa_alsa_ucm_device *di, *dn;
1549
pa_alsa_ucm_modifier *mi, *mn;
1551
PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
1552
PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
1553
pa_proplist_free(di->proplist);
1554
if (di->conflicting_devices)
1555
pa_idxset_free(di->conflicting_devices, NULL);
1556
if (di->supported_devices)
1557
pa_idxset_free(di->supported_devices, NULL);
1561
PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
1562
PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
1563
pa_proplist_free(mi->proplist);
1564
if (mi->n_suppdev > 0)
1565
snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
1566
if (mi->n_confdev > 0)
1567
snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
1568
pa_xfree(mi->media_role);
1571
pa_proplist_free(verb->proplist);
1575
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
1576
pa_alsa_ucm_verb *vi, *vn;
1577
pa_alsa_jack *ji, *jn;
1579
PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
1580
PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
1583
PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
1584
PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
1585
pa_xfree(ji->alsa_name);
1590
snd_use_case_mgr_close(ucm->ucm_mgr);
1591
ucm->ucm_mgr = NULL;
1595
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
1596
pa_alsa_ucm_device *dev;
1597
pa_alsa_ucm_modifier *mod;
1600
if (context->ucm_devices) {
1601
/* clear ucm device pointer to mapping */
1602
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1603
if (context->direction == PA_DIRECTION_OUTPUT)
1604
dev->playback_mapping = NULL;
1606
dev->capture_mapping = NULL;
1609
pa_idxset_free(context->ucm_devices, NULL);
1612
if (context->ucm_modifiers) {
1613
PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1614
if (context->direction == PA_DIRECTION_OUTPUT)
1615
mod->playback_mapping = NULL;
1617
mod->capture_mapping = NULL;
1620
pa_idxset_free(context->ucm_modifiers, NULL);
1624
/* Enable the modifier when the first stream with matched role starts */
1625
void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
1626
pa_alsa_ucm_modifier *mod;
1628
if (!ucm->active_verb)
1631
PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
1632
if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
1633
if (mod->enabled_counter == 0) {
1634
const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1636
pa_log_info("Enable ucm modifier %s", mod_name);
1637
if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
1638
pa_log("Failed to enable ucm modifier %s", mod_name);
1642
mod->enabled_counter++;
1648
/* Disable the modifier when the last stream with matched role ends */
1649
void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
1650
pa_alsa_ucm_modifier *mod;
1652
if (!ucm->active_verb)
1655
PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
1656
if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
1658
mod->enabled_counter--;
1659
if (mod->enabled_counter == 0) {
1660
const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1662
pa_log_info("Disable ucm modifier %s", mod_name);
1663
if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
1664
pa_log("Failed to disable ucm modifier %s", mod_name);
1673
#else /* HAVE_ALSA_UCM */
1675
/* Dummy functions for systems without UCM support */
1677
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
1678
pa_log_info("UCM not available.");
1682
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
1686
int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
1690
int 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) {
1694
void pa_alsa_ucm_add_ports(
1696
pa_proplist *proplist,
1697
pa_alsa_ucm_mapping_context *context,
1702
void pa_alsa_ucm_add_ports_combination(
1704
pa_alsa_ucm_mapping_context *context,
1707
pa_card_profile *cp,
1711
int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1715
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
1718
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
1721
void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
1724
void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {