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, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
32 #include <asoundlib.h>
34 #ifdef HAVE_VALGRIND_MEMCHECK_H
35 #include <valgrind/memcheck.h>
38 #include <pulse/sample.h>
39 #include <pulse/xmalloc.h>
40 #include <pulse/timeval.h>
41 #include <pulse/util.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/core-util.h>
46 #include <pulsecore/atomic.h>
47 #include <pulsecore/core-error.h>
48 #include <pulsecore/once.h>
49 #include <pulsecore/thread.h>
50 #include <pulsecore/conf-parser.h>
51 #include <pulsecore/strbuf.h>
53 #include "alsa-mixer.h"
54 #include "alsa-util.h"
57 #define PA_UCM_PRE_TAG_OUTPUT "[Out] "
58 #define PA_UCM_PRE_TAG_INPUT "[In] "
60 #define PA_UCM_PLAYBACK_PRIORITY_UNSET(device) ((device)->playback_channels && !(device)->playback_priority)
61 #define PA_UCM_CAPTURE_PRIORITY_UNSET(device) ((device)->capture_channels && !(device)->capture_priority)
62 #define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
64 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \
65 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \
67 #define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
81 static struct ucm_items item
[] = {
82 {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK
},
83 {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE
},
84 {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME
},
85 {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH
},
86 {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY
},
87 {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE
},
88 {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
},
89 {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME
},
90 {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH
},
91 {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
},
92 {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE
},
93 {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
},
94 {"TQ", PA_ALSA_PROP_UCM_QOS
},
98 /* UCM verb info - this should eventually be part of policy manangement */
99 static struct ucm_info verb_info
[] = {
100 {SND_USE_CASE_VERB_INACTIVE
, 0},
101 {SND_USE_CASE_VERB_HIFI
, 8000},
102 {SND_USE_CASE_VERB_HIFI_LOW_POWER
, 7000},
103 {SND_USE_CASE_VERB_VOICE
, 6000},
104 {SND_USE_CASE_VERB_VOICE_LOW_POWER
, 5000},
105 {SND_USE_CASE_VERB_VOICECALL
, 4000},
106 {SND_USE_CASE_VERB_IP_VOICECALL
, 4000},
107 {SND_USE_CASE_VERB_ANALOG_RADIO
, 3000},
108 {SND_USE_CASE_VERB_DIGITAL_RADIO
, 3000},
112 /* UCM device info - should be overwritten by ucm property */
113 static struct ucm_info dev_info
[] = {
114 {SND_USE_CASE_DEV_SPEAKER
, 100},
115 {SND_USE_CASE_DEV_LINE
, 100},
116 {SND_USE_CASE_DEV_HEADPHONES
, 100},
117 {SND_USE_CASE_DEV_HEADSET
, 300},
118 {SND_USE_CASE_DEV_HANDSET
, 200},
119 {SND_USE_CASE_DEV_BLUETOOTH
, 400},
120 {SND_USE_CASE_DEV_EARPIECE
, 100},
121 {SND_USE_CASE_DEV_SPDIF
, 100},
122 {SND_USE_CASE_DEV_HDMI
, 100},
123 {SND_USE_CASE_DEV_NONE
, 100},
127 /* UCM profile properties - The verb data is store so it can be used to fill
128 * the new profiles properties */
129 static int ucm_get_property(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
, const char *verb_name
) {
134 for (i
= 0; item
[i
].id
; i
++) {
137 id
= pa_sprintf_malloc("=%s//%s", item
[i
].id
, verb_name
);
138 err
= snd_use_case_get(uc_mgr
, id
, &value
);
143 pa_log_debug("Got %s for verb %s: %s", item
[i
].id
, verb_name
, value
);
144 pa_proplist_sets(verb
->proplist
, item
[i
].property
, value
);
151 static int ucm_device_exists(pa_idxset
*idxset
, pa_alsa_ucm_device
*dev
) {
152 pa_alsa_ucm_device
*d
;
155 PA_IDXSET_FOREACH(d
, idxset
, idx
)
162 static void ucm_add_devices_to_idxset(
164 pa_alsa_ucm_device
*me
,
165 pa_alsa_ucm_device
*devices
,
166 const char **dev_names
,
169 pa_alsa_ucm_device
*d
;
171 PA_LLIST_FOREACH(d
, devices
) {
178 name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
180 for (i
= 0; i
< n
; i
++)
181 if (pa_streq(dev_names
[i
], name
))
182 pa_idxset_put(idxset
, d
, NULL
);
186 /* Create a property list for this ucm device */
187 static int ucm_get_device_property(
188 pa_alsa_ucm_device
*device
,
189 snd_use_case_mgr_t
*uc_mgr
,
190 pa_alsa_ucm_verb
*verb
,
191 const char *device_name
) {
194 const char **devices
;
199 int n_confdev
, n_suppdev
;
201 for (i
= 0; item
[i
].id
; i
++) {
202 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, device_name
);
203 err
= snd_use_case_get(uc_mgr
, id
, &value
);
208 pa_log_debug("Got %s for device %s: %s", item
[i
].id
, device_name
, value
);
209 pa_proplist_sets(device
->proplist
, item
[i
].property
, value
);
213 /* get direction and channels */
214 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
215 if (value
) { /* output */
217 if (pa_atou(value
, &ui
) == 0 && pa_channels_valid(ui
))
218 device
->playback_channels
= ui
;
220 pa_log("UCM playback channels %s for device %s out of range", value
, device_name
);
223 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_SINK
);
224 if (!value
) { /* take pcm from verb playback default */
225 value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_SINK
);
227 pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name
, value
);
228 pa_proplist_sets(device
->proplist
, PA_ALSA_PROP_UCM_SINK
, value
);
230 pa_log("UCM playback device %s fetch pcm failed", device_name
);
234 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
235 if (value
) { /* input */
237 if (pa_atou(value
, &ui
) == 0 && pa_channels_valid(ui
))
238 device
->capture_channels
= ui
;
240 pa_log("UCM capture channels %s for device %s out of range", value
, device_name
);
243 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
244 if (!value
) { /* take pcm from verb capture default */
245 value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
247 pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name
, value
);
248 pa_proplist_sets(device
->proplist
, PA_ALSA_PROP_UCM_SOURCE
, value
);
250 pa_log("UCM capture device %s fetch pcm failed", device_name
);
254 if (device
->playback_channels
== 0 && device
->capture_channels
== 0) {
255 pa_log_warn("UCM file does not specify 'PlaybackChannels' or 'CaptureChannels'"
256 "for device %s, assuming stereo duplex.", device_name
);
257 device
->playback_channels
= 2;
258 device
->capture_channels
= 2;
261 /* get rate and priority of device */
262 if (device
->playback_channels
) { /* sink device */
264 if ((value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_RATE
)) ||
265 (value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_RATE
))) {
266 if (pa_atou(value
, &ui
) == 0 && pa_sample_rate_valid(ui
)) {
267 pa_log_debug("UCM playback device %s rate %d", device_name
, ui
);
268 device
->playback_rate
= ui
;
270 pa_log_debug("UCM playback device %s has bad rate %s", device_name
, value
);
273 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY
);
275 /* get priority from ucm config */
276 if (pa_atou(value
, &ui
) == 0)
277 device
->playback_priority
= ui
;
279 pa_log_debug("UCM playback priority %s for device %s error", value
, device_name
);
283 if (device
->capture_channels
) { /* source device */
285 if ((value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_RATE
)) ||
286 (value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_RATE
))) {
287 if (pa_atou(value
, &ui
) == 0 && pa_sample_rate_valid(ui
)) {
288 pa_log_debug("UCM capture device %s rate %d", device_name
, ui
);
289 device
->capture_rate
= ui
;
291 pa_log_debug("UCM capture device %s has bad rate %s", device_name
, value
);
294 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
);
296 /* get priority from ucm config */
297 if (pa_atou(value
, &ui
) == 0)
298 device
->capture_priority
= ui
;
300 pa_log_debug("UCM capture priority %s for device %s error", value
, device_name
);
304 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
) || PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
305 /* get priority from static table */
306 for (i
= 0; dev_info
[i
].id
; i
++) {
307 if (strcasecmp(dev_info
[i
].id
, device_name
) == 0) {
308 PA_UCM_DEVICE_PRIORITY_SET(device
, dev_info
[i
].priority
);
314 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
)) {
315 /* fall through to default priority */
316 device
->playback_priority
= 100;
319 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
320 /* fall through to default priority */
321 device
->capture_priority
= 100;
324 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name
);
325 n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
329 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name
);
331 device
->conflicting_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
332 ucm_add_devices_to_idxset(device
->conflicting_devices
, device
, verb
->devices
, devices
, n_confdev
);
333 snd_use_case_free_list(devices
, n_confdev
);
336 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name
);
337 n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
341 pa_log_debug("No %s for device %s", "_supporteddevs", device_name
);
343 device
->supported_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
344 ucm_add_devices_to_idxset(device
->supported_devices
, device
, verb
->devices
, devices
, n_suppdev
);
345 snd_use_case_free_list(devices
, n_suppdev
);
351 /* Create a property list for this ucm modifier */
352 static int ucm_get_modifier_property(pa_alsa_ucm_modifier
*modifier
, snd_use_case_mgr_t
*uc_mgr
, const char *modifier_name
) {
357 for (i
= 0; item
[i
].id
; i
++) {
360 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, modifier_name
);
361 err
= snd_use_case_get(uc_mgr
, id
, &value
);
366 pa_log_debug("Got %s for modifier %s: %s", item
[i
].id
, modifier_name
, value
);
367 pa_proplist_sets(modifier
->proplist
, item
[i
].property
, value
);
371 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name
);
372 modifier
->n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &modifier
->conflicting_devices
);
374 if (modifier
->n_confdev
< 0)
375 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name
);
377 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name
);
378 modifier
->n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &modifier
->supported_devices
);
380 if (modifier
->n_suppdev
< 0)
381 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name
);
386 /* Create a list of devices for this verb */
387 static int ucm_get_devices(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
388 const char **dev_list
;
391 num_dev
= snd_use_case_get_list(uc_mgr
, "_devices", &dev_list
);
395 for (i
= 0; i
< num_dev
; i
+= 2) {
396 pa_alsa_ucm_device
*d
= pa_xnew0(pa_alsa_ucm_device
, 1);
398 d
->proplist
= pa_proplist_new();
399 pa_proplist_sets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(dev_list
[i
]));
400 pa_proplist_sets(d
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(dev_list
[i
+ 1]));
402 PA_LLIST_PREPEND(pa_alsa_ucm_device
, verb
->devices
, d
);
405 snd_use_case_free_list(dev_list
, num_dev
);
410 static int ucm_get_modifiers(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
411 const char **mod_list
;
414 num_mod
= snd_use_case_get_list(uc_mgr
, "_modifiers", &mod_list
);
418 for (i
= 0; i
< num_mod
; i
+= 2) {
419 pa_alsa_ucm_modifier
*m
;
422 pa_log_warn("Got a modifier with a null name. Skipping.");
426 m
= pa_xnew0(pa_alsa_ucm_modifier
, 1);
427 m
->proplist
= pa_proplist_new();
429 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_NAME
, mod_list
[i
]);
430 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(mod_list
[i
+ 1]));
432 PA_LLIST_PREPEND(pa_alsa_ucm_modifier
, verb
->modifiers
, m
);
435 snd_use_case_free_list(mod_list
, num_mod
);
440 static void add_role_to_device(pa_alsa_ucm_device
*dev
, const char *dev_name
, const char *role_name
, const char *role
) {
441 const char *cur
= pa_proplist_gets(dev
->proplist
, role_name
);
444 pa_proplist_sets(dev
->proplist
, role_name
, role
);
445 else if (!pa_str_in_list_spaces(cur
, role
)) { /* does not exist */
446 char *value
= pa_sprintf_malloc("%s %s", cur
, role
);
448 pa_proplist_sets(dev
->proplist
, role_name
, value
);
452 pa_log_info("Add role %s to device %s(%s), result %s", role
, dev_name
, role_name
, pa_proplist_gets(dev
->proplist
,
456 static void add_media_role(const char *name
, pa_alsa_ucm_device
*list
, const char *role_name
, const char *role
, bool is_sink
) {
457 pa_alsa_ucm_device
*d
;
459 PA_LLIST_FOREACH(d
, list
) {
460 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
462 if (pa_streq(dev_name
, name
)) {
463 const char *sink
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_SINK
);
464 const char *source
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
467 add_role_to_device(d
, dev_name
, role_name
, role
);
468 else if (!is_sink
&& source
)
469 add_role_to_device(d
, dev_name
, role_name
, role
);
475 static char *modifier_name_to_role(const char *mod_name
, bool *is_sink
) {
476 char *sub
= NULL
, *tmp
;
480 if (pa_startswith(mod_name
, "Play")) {
482 sub
= pa_xstrdup(mod_name
+ 4);
483 } else if (pa_startswith(mod_name
, "Capture"))
484 sub
= pa_xstrdup(mod_name
+ 7);
488 pa_log_warn("Can't match media roles for modifer %s", mod_name
);
495 *tmp
= tolower(*tmp
);
501 static void ucm_set_media_roles(pa_alsa_ucm_modifier
*modifier
, pa_alsa_ucm_device
*list
, const char *mod_name
) {
503 bool is_sink
= false;
505 const char *role_name
;
507 sub
= modifier_name_to_role(mod_name
, &is_sink
);
511 modifier
->action_direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
512 modifier
->media_role
= sub
;
514 role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
515 for (i
= 0; i
< modifier
->n_suppdev
; i
++) {
516 /* if modifier has no specific pcm, we add role intent to its supported devices */
517 if (!pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_SINK
) &&
518 !pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_SOURCE
))
519 add_media_role(modifier
->supported_devices
[i
], list
, role_name
, sub
, is_sink
);
523 static void append_lost_relationship(pa_alsa_ucm_device
*dev
) {
525 pa_alsa_ucm_device
*d
;
527 if (dev
->conflicting_devices
) {
528 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
529 if (!d
->conflicting_devices
)
530 d
->conflicting_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
532 if (pa_idxset_put(d
->conflicting_devices
, dev
, NULL
) == 0)
533 pa_log_warn("Add lost conflicting device %s to %s",
534 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
),
535 pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
));
539 if (dev
->supported_devices
) {
540 PA_IDXSET_FOREACH(d
, dev
->supported_devices
, idx
) {
541 if (!d
->supported_devices
)
542 d
->supported_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
544 if (pa_idxset_put(d
->supported_devices
, dev
, NULL
) == 0)
545 pa_log_warn("Add lost supported device %s to %s",
546 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
),
547 pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
));
552 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
) {
554 const char **verb_list
;
555 int num_verbs
, i
, err
= 0;
557 /* is UCM available for this card ? */
558 err
= snd_card_get_name(card_index
, &card_name
);
560 pa_log("Card can't get card_name from card_index %d", card_index
);
564 err
= snd_use_case_mgr_open(&ucm
->ucm_mgr
, card_name
);
566 pa_log_info("UCM not available for card %s", card_name
);
570 pa_log_info("UCM available for card %s", card_name
);
572 /* get a list of all UCM verbs (profiles) for this card */
573 num_verbs
= snd_use_case_verb_list(ucm
->ucm_mgr
, &verb_list
);
575 pa_log("UCM verb list not found for %s", card_name
);
579 /* get the properties of each UCM verb */
580 for (i
= 0; i
< num_verbs
; i
+= 2) {
581 pa_alsa_ucm_verb
*verb
;
583 /* Get devices and modifiers for each verb */
584 err
= pa_alsa_ucm_get_verb(ucm
->ucm_mgr
, verb_list
[i
], verb_list
[i
+1], &verb
);
586 pa_log("Failed to get the verb %s", verb_list
[i
]);
590 PA_LLIST_PREPEND(pa_alsa_ucm_verb
, ucm
->verbs
, verb
);
594 pa_log("No UCM verb is valid for %s", card_name
);
598 snd_use_case_free_list(verb_list
, num_verbs
);
602 snd_use_case_mgr_close(ucm
->ucm_mgr
);
613 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
) {
614 pa_alsa_ucm_device
*d
;
615 pa_alsa_ucm_modifier
*mod
;
616 pa_alsa_ucm_verb
*verb
;
620 pa_log_info("Set UCM verb to %s", verb_name
);
621 err
= snd_use_case_set(uc_mgr
, "_verb", verb_name
);
625 verb
= pa_xnew0(pa_alsa_ucm_verb
, 1);
626 verb
->proplist
= pa_proplist_new();
628 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(verb_name
));
629 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(verb_desc
));
631 err
= ucm_get_devices(verb
, uc_mgr
);
633 pa_log("No UCM devices for verb %s", verb_name
);
635 err
= ucm_get_modifiers(verb
, uc_mgr
);
637 pa_log("No UCM modifiers for verb %s", verb_name
);
639 /* Verb properties */
640 ucm_get_property(verb
, uc_mgr
, verb_name
);
642 PA_LLIST_FOREACH(d
, verb
->devices
) {
643 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
645 /* Devices properties */
646 ucm_get_device_property(d
, uc_mgr
, verb
, dev_name
);
648 /* make conflicting or supported device mutual */
649 PA_LLIST_FOREACH(d
, verb
->devices
)
650 append_lost_relationship(d
);
652 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
653 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
655 /* Modifier properties */
656 ucm_get_modifier_property(mod
, uc_mgr
, mod_name
);
658 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
659 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name
, mod_name
);
660 ucm_set_media_roles(mod
, verb
->devices
, mod_name
);
667 static int pa_alsa_ucm_device_cmp(const void *a
, const void *b
) {
668 const pa_alsa_ucm_device
*d1
= *(pa_alsa_ucm_device
**)a
;
669 const pa_alsa_ucm_device
*d2
= *(pa_alsa_ucm_device
**)b
;
671 return strcmp(pa_proplist_gets(d1
->proplist
, PA_ALSA_PROP_UCM_NAME
), pa_proplist_gets(d2
->proplist
, PA_ALSA_PROP_UCM_NAME
));
674 static void ucm_add_port_combination(
676 pa_alsa_ucm_mapping_context
*context
,
678 pa_alsa_ucm_device
**pdevices
,
684 pa_device_port
*port
;
689 const char *dev_name
;
690 const char *direction
;
691 pa_alsa_ucm_device
*sorted
[num
], *dev
;
693 for (i
= 0; i
< num
; i
++)
694 sorted
[i
] = pdevices
[i
];
696 /* Sort by alphabetical order so as to have a deterministic naming scheme
697 * for combination ports */
698 qsort(&sorted
[0], num
, sizeof(pa_alsa_ucm_device
*), pa_alsa_ucm_device_cmp
);
701 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
703 name
= pa_sprintf_malloc("%s%s", is_sink
? PA_UCM_PRE_TAG_OUTPUT
: PA_UCM_PRE_TAG_INPUT
, dev_name
);
704 desc
= num
== 1 ? pa_xstrdup(pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
))
705 : pa_sprintf_malloc("Combination port for %s", dev_name
);
707 priority
= is_sink
? dev
->playback_priority
: dev
->capture_priority
;
708 prio2
= (priority
== 0 ? 0 : 1.0/priority
);
710 for (i
= 1; i
< num
; i
++) {
714 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
716 tmp
= pa_sprintf_malloc("%s+%s", name
, dev_name
);
720 tmp
= pa_sprintf_malloc("%s,%s", desc
, dev_name
);
724 priority
= is_sink
? dev
->playback_priority
: dev
->capture_priority
;
725 if (priority
!= 0 && prio2
> 0)
726 prio2
+= 1.0/priority
;
729 /* Make combination ports always have lower priority, and use the formula
730 1/p = 1/p1 + 1/p2 + ... 1/pn.
731 This way, the result will always be less than the individual components,
732 yet higher components will lead to higher result. */
735 priority
= prio2
> 0 ? 1.0/prio2
: 0;
737 port
= pa_hashmap_get(ports
, name
);
739 pa_device_port_new_data port_data
;
741 pa_device_port_new_data_init(&port_data
);
742 pa_device_port_new_data_set_name(&port_data
, name
);
743 pa_device_port_new_data_set_description(&port_data
, desc
);
744 pa_device_port_new_data_set_direction(&port_data
, is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
);
746 port
= pa_device_port_new(core
, &port_data
, 0);
747 pa_device_port_new_data_done(&port_data
);
750 pa_hashmap_put(ports
, port
->name
, port
);
751 pa_log_debug("Add port %s: %s", port
->name
, port
->description
);
752 port
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
755 port
->priority
= priority
;
760 direction
= is_sink
? "output" : "input";
761 pa_log_debug("Port %s direction %s, priority %d", port
->name
, direction
, priority
);
764 pa_log_debug("Adding profile %s to port %s.", cp
->name
, port
->name
);
765 pa_hashmap_put(port
->profiles
, cp
->name
, cp
);
769 pa_hashmap_put(hash
, port
->name
, port
);
770 pa_device_port_ref(port
);
774 static int ucm_port_contains(const char *port_name
, const char *dev_name
, bool is_sink
) {
777 const char *state
= NULL
;
780 if (!port_name
|| !dev_name
)
783 port_name
+= is_sink
? strlen(PA_UCM_PRE_TAG_OUTPUT
) : strlen(PA_UCM_PRE_TAG_INPUT
);
785 while ((r
= pa_split_in_place(port_name
, "+", &len
, &state
))) {
786 if (!strncmp(r
, dev_name
, len
)) {
795 static int ucm_check_conformance(
796 pa_alsa_ucm_mapping_context
*context
,
797 pa_alsa_ucm_device
**pdevices
,
799 pa_alsa_ucm_device
*dev
) {
802 pa_alsa_ucm_device
*d
;
807 pa_log_debug("Check device %s conformance with %d other devices",
808 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
), dev_num
);
810 pa_log_debug("First device in combination, number 1");
814 if (dev
->conflicting_devices
) { /* the device defines conflicting devices */
815 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
816 for (i
= 0; i
< dev_num
; i
++) {
817 if (pdevices
[i
] == d
) {
818 pa_log_debug("Conflicting device found");
823 } else if (dev
->supported_devices
) { /* the device defines supported devices */
824 for (i
= 0; i
< dev_num
; i
++) {
825 if (!ucm_device_exists(dev
->supported_devices
, pdevices
[i
])) {
826 pa_log_debug("Supported device not found");
830 } else { /* not support any other devices */
831 pa_log_debug("Not support any other devices");
835 pa_log_debug("Device added to combination, number %d", dev_num
+ 1);
839 static inline pa_alsa_ucm_device
*get_next_device(pa_idxset
*idxset
, uint32_t *idx
) {
840 pa_alsa_ucm_device
*dev
;
842 if (*idx
== PA_IDXSET_INVALID
)
843 dev
= pa_idxset_first(idxset
, idx
);
845 dev
= pa_idxset_next(idxset
, idx
);
850 static void ucm_add_ports_combination(
852 pa_alsa_ucm_mapping_context
*context
,
854 pa_alsa_ucm_device
**pdevices
,
861 pa_alsa_ucm_device
*dev
;
862 uint32_t idx
= map_index
;
864 if ((dev
= get_next_device(context
->ucm_devices
, &idx
)) == NULL
)
867 /* check if device at map_index can combine with existing devices combination */
868 if (ucm_check_conformance(context
, pdevices
, dev_num
, dev
)) {
869 /* add device at map_index to devices combination */
870 pdevices
[dev_num
] = dev
;
871 /* add current devices combination as a new port */
872 ucm_add_port_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, ports
, cp
, core
);
873 /* try more elements combination */
874 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, idx
, ports
, cp
, core
);
877 /* try other device with current elements number */
878 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
, idx
, ports
, cp
, core
);
881 static char* merge_roles(const char *cur
, const char *add
) {
883 const char *state
= NULL
;
886 return pa_xstrdup(cur
);
887 else if (cur
== NULL
)
888 return pa_xstrdup(add
);
890 ret
= pa_xstrdup(cur
);
892 while ((r
= pa_split_spaces(add
, &state
))) {
895 if (!pa_str_in_list_spaces(ret
, r
))
896 value
= pa_sprintf_malloc("%s %s", ret
, r
);
910 void pa_alsa_ucm_add_ports_combination(
912 pa_alsa_ucm_mapping_context
*context
,
918 pa_alsa_ucm_device
**pdevices
;
920 pa_assert(context
->ucm_devices
);
922 if (pa_idxset_size(context
->ucm_devices
) > 0) {
923 pdevices
= pa_xnew(pa_alsa_ucm_device
*, pa_idxset_size(context
->ucm_devices
));
924 ucm_add_ports_combination(p
, context
, is_sink
, pdevices
, 0, PA_IDXSET_INVALID
, ports
, cp
, core
);
929 void pa_alsa_ucm_add_ports(
931 pa_proplist
*proplist
,
932 pa_alsa_ucm_mapping_context
*context
,
938 const char *role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
939 pa_alsa_ucm_device
*dev
;
940 pa_alsa_ucm_modifier
*mod
;
946 /* add ports first */
947 pa_alsa_ucm_add_ports_combination(*p
, context
, is_sink
, card
->ports
, NULL
, card
->core
);
949 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
950 merged_roles
= pa_xstrdup(pa_proplist_gets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
));
951 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
952 const char *roles
= pa_proplist_gets(dev
->proplist
, role_name
);
953 tmp
= merge_roles(merged_roles
, roles
);
954 pa_xfree(merged_roles
);
958 if (context
->ucm_modifiers
)
959 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
960 tmp
= merge_roles(merged_roles
, mod
->media_role
);
961 pa_xfree(merged_roles
);
966 pa_proplist_sets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
, merged_roles
);
968 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist
, PA_PROP_DEVICE_STRING
), pa_strnull(merged_roles
));
969 pa_xfree(merged_roles
);
972 /* Change UCM verb and device to match selected card profile */
973 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
976 pa_alsa_ucm_verb
*verb
;
978 if (new_profile
== old_profile
)
980 else if (new_profile
== NULL
|| old_profile
== NULL
)
981 profile
= new_profile
? new_profile
: SND_USE_CASE_VERB_INACTIVE
;
982 else if (!pa_streq(new_profile
, old_profile
))
983 profile
= new_profile
;
988 pa_log_info("Set UCM verb to %s", profile
);
989 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", profile
)) < 0) {
990 pa_log("Failed to set verb %s", profile
);
994 /* find active verb */
995 ucm
->active_verb
= NULL
;
996 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
997 const char *verb_name
;
998 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
999 if (pa_streq(verb_name
, profile
)) {
1000 ucm
->active_verb
= verb
;
1008 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
1011 pa_alsa_ucm_config
*ucm
;
1012 const char **enable_devs
;
1015 pa_alsa_ucm_device
*dev
;
1017 pa_assert(context
&& context
->ucm
);
1020 pa_assert(ucm
->ucm_mgr
);
1022 enable_devs
= pa_xnew(const char *, pa_idxset_size(context
->ucm_devices
));
1024 /* first disable then enable */
1025 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1026 const char *dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1028 if (ucm_port_contains(port
->name
, dev_name
, is_sink
))
1029 enable_devs
[enable_num
++] = dev_name
;
1031 pa_log_debug("Disable ucm device %s", dev_name
);
1032 if (snd_use_case_set(ucm
->ucm_mgr
, "_disdev", dev_name
) > 0) {
1033 pa_log("Failed to disable ucm device %s", dev_name
);
1040 for (i
= 0; i
< enable_num
; i
++) {
1041 pa_log_debug("Enable ucm device %s", enable_devs
[i
]);
1042 if (snd_use_case_set(ucm
->ucm_mgr
, "_enadev", enable_devs
[i
]) < 0) {
1043 pa_log("Failed to enable ucm device %s", enable_devs
[i
]);
1049 pa_xfree(enable_devs
);
1054 static void ucm_add_mapping(pa_alsa_profile
*p
, pa_alsa_mapping
*m
) {
1056 switch (m
->direction
) {
1057 case PA_ALSA_DIRECTION_ANY
:
1058 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1059 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1061 case PA_ALSA_DIRECTION_OUTPUT
:
1062 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1064 case PA_ALSA_DIRECTION_INPUT
:
1065 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1070 static void alsa_mapping_add_ucm_device(pa_alsa_mapping
*m
, pa_alsa_ucm_device
*device
) {
1072 const char *new_desc
;
1074 pa_idxset_put(m
->ucm_context
.ucm_devices
, device
, NULL
);
1076 new_desc
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1077 cur_desc
= m
->description
;
1079 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1081 m
->description
= pa_xstrdup(new_desc
);
1084 /* walk around null case */
1085 m
->description
= m
->description
? m
->description
: pa_xstrdup("");
1087 /* save mapping to ucm device */
1088 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1089 device
->playback_mapping
= m
;
1091 device
->capture_mapping
= m
;
1094 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping
*m
, pa_alsa_ucm_modifier
*modifier
) {
1096 const char *new_desc
, *mod_name
, *channel_str
;
1097 uint32_t channels
= 0;
1099 pa_idxset_put(m
->ucm_context
.ucm_modifiers
, modifier
, NULL
);
1101 new_desc
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1102 cur_desc
= m
->description
;
1104 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1106 m
->description
= pa_xstrdup(new_desc
);
1109 m
->description
= m
->description
? m
->description
: pa_xstrdup("");
1111 /* Modifier sinks should not be routed to by default */
1114 mod_name
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1115 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_MODIFIER
, mod_name
);
1117 /* save mapping to ucm modifier */
1118 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1119 modifier
->playback_mapping
= m
;
1120 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
1122 modifier
->capture_mapping
= m
;
1123 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
1127 /* FIXME: channel_str is unsanitized input from the UCM configuration,
1128 * we should do proper error handling instead of asserting.
1129 * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
1130 pa_assert_se(pa_atou(channel_str
, &channels
) == 0 && pa_channels_valid(channels
));
1131 pa_log_debug("Got channel count %" PRIu32
" for modifier", channels
);
1135 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1137 pa_channel_map_init(&m
->channel_map
);
1140 static int ucm_create_mapping_direction(
1141 pa_alsa_ucm_config
*ucm
,
1142 pa_alsa_profile_set
*ps
,
1144 pa_alsa_ucm_device
*device
,
1145 const char *verb_name
,
1146 const char *device_name
,
1147 const char *device_str
,
1152 unsigned priority
, rate
, channels
;
1154 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1156 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1158 pa_log("No mapping for %s", mapping_name
);
1159 pa_xfree(mapping_name
);
1162 pa_log_debug("UCM mapping: %s dev %s", mapping_name
, device_name
);
1163 pa_xfree(mapping_name
);
1165 priority
= is_sink
? device
->playback_priority
: device
->capture_priority
;
1166 rate
= is_sink
? device
->playback_rate
: device
->capture_rate
;
1167 channels
= is_sink
? device
->playback_channels
: device
->capture_channels
;
1169 if (!m
->ucm_context
.ucm_devices
) { /* new mapping */
1170 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1171 m
->ucm_context
.ucm
= ucm
;
1172 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1174 m
->device_strings
= pa_xnew0(char*, 2);
1175 m
->device_strings
[0] = pa_xstrdup(device_str
);
1176 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1178 ucm_add_mapping(p
, m
);
1180 m
->sample_spec
.rate
= rate
;
1181 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1184 /* mapping priority is the highest one of ucm devices */
1185 if (priority
> m
->priority
)
1186 m
->priority
= priority
;
1188 /* mapping channels is the lowest one of ucm devices */
1189 if (channels
< m
->channel_map
.channels
)
1190 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1192 alsa_mapping_add_ucm_device(m
, device
);
1197 static int ucm_create_mapping_for_modifier(
1198 pa_alsa_ucm_config
*ucm
,
1199 pa_alsa_profile_set
*ps
,
1201 pa_alsa_ucm_modifier
*modifier
,
1202 const char *verb_name
,
1203 const char *mod_name
,
1204 const char *device_str
,
1210 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1212 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1214 pa_log("no mapping for %s", mapping_name
);
1215 pa_xfree(mapping_name
);
1218 pa_log_info("ucm mapping: %s modifier %s", mapping_name
, mod_name
);
1219 pa_xfree(mapping_name
);
1221 if (!m
->ucm_context
.ucm_devices
&& !m
->ucm_context
.ucm_modifiers
) { /* new mapping */
1222 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1223 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1224 m
->ucm_context
.ucm
= ucm
;
1225 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1227 m
->device_strings
= pa_xnew0(char*, 2);
1228 m
->device_strings
[0] = pa_xstrdup(device_str
);
1229 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1230 /* Modifier sinks should not be routed to by default */
1233 ucm_add_mapping(p
, m
);
1234 } else if (!m
->ucm_context
.ucm_modifiers
) /* share pcm with device */
1235 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1237 alsa_mapping_add_ucm_modifier(m
, modifier
);
1242 static int ucm_create_mapping(
1243 pa_alsa_ucm_config
*ucm
,
1244 pa_alsa_profile_set
*ps
,
1246 pa_alsa_ucm_device
*device
,
1247 const char *verb_name
,
1248 const char *device_name
,
1250 const char *source
) {
1254 if (!sink
&& !source
) {
1255 pa_log("No sink and source at %s: %s", verb_name
, device_name
);
1260 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, sink
, true);
1261 if (ret
== 0 && source
)
1262 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, source
, false);
1267 static pa_alsa_jack
* ucm_get_jack(pa_alsa_ucm_config
*ucm
, const char *dev_name
, const char *pre_tag
) {
1269 char *name
= pa_sprintf_malloc("%s%s", pre_tag
, dev_name
);
1271 PA_LLIST_FOREACH(j
, ucm
->jacks
)
1272 if (pa_streq(j
->name
, name
))
1275 j
= pa_xnew0(pa_alsa_jack
, 1);
1276 j
->state_unplugged
= PA_AVAILABLE_NO
;
1277 j
->state_plugged
= PA_AVAILABLE_YES
;
1278 j
->name
= pa_xstrdup(name
);
1279 j
->alsa_name
= pa_sprintf_malloc("%s Jack", dev_name
);
1281 PA_LLIST_PREPEND(pa_alsa_jack
, ucm
->jacks
, j
);
1288 static int ucm_create_profile(
1289 pa_alsa_ucm_config
*ucm
,
1290 pa_alsa_profile_set
*ps
,
1291 pa_alsa_ucm_verb
*verb
,
1292 const char *verb_name
,
1293 const char *verb_desc
) {
1296 pa_alsa_ucm_device
*dev
;
1297 pa_alsa_ucm_modifier
*mod
;
1299 const char *name
, *sink
, *source
;
1304 if (pa_hashmap_get(ps
->profiles
, verb_name
)) {
1305 pa_log("Verb %s already exists", verb_name
);
1309 p
= pa_xnew0(pa_alsa_profile
, 1);
1310 p
->profile_set
= ps
;
1311 p
->name
= pa_xstrdup(verb_name
);
1312 p
->description
= pa_xstrdup(verb_desc
);
1314 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1315 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1317 p
->supported
= true;
1318 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
1320 /* TODO: get profile priority from ucm info or policy management */
1321 c
= verb_cmp
= pa_xstrdup(verb_name
);
1323 if (*c
== '_') *c
= ' ';
1327 for (i
= 0; verb_info
[i
].id
; i
++) {
1328 if (strcasecmp(verb_info
[i
].id
, verb_cmp
) == 0) {
1329 p
->priority
= verb_info
[i
].priority
;
1336 if (verb_info
[i
].id
== NULL
)
1339 PA_LLIST_FOREACH(dev
, verb
->devices
) {
1340 name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1342 sink
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1343 source
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1345 ucm_create_mapping(ucm
, ps
, p
, dev
, verb_name
, name
, sink
, source
);
1348 dev
->output_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_OUTPUT
);
1350 dev
->input_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_INPUT
);
1353 /* Now find modifiers that have their own PlaybackPCM and create
1354 * separate sinks for them. */
1355 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
1356 name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1358 sink
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1359 source
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1362 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, sink
, true);
1364 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, source
, false);
1367 pa_alsa_profile_dump(p
);
1372 static snd_pcm_t
* mapping_open_pcm(pa_alsa_ucm_config
*ucm
, pa_alsa_mapping
*m
, int mode
) {
1374 pa_sample_spec try_ss
= ucm
->core
->default_sample_spec
;
1375 pa_channel_map try_map
;
1376 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
1377 bool exact_channels
= m
->channel_map
.channels
> 0;
1379 if (exact_channels
) {
1380 try_map
= m
->channel_map
;
1381 try_ss
.channels
= try_map
.channels
;
1383 pa_channel_map_init_extend(&try_map
, try_ss
.channels
, PA_CHANNEL_MAP_ALSA
);
1386 pa_usec_to_bytes(ucm
->core
->default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
1387 pa_frame_size(&try_ss
);
1388 try_buffer_size
= ucm
->core
->default_n_fragments
* try_period_size
;
1390 pcm
= pa_alsa_open_by_device_string(m
->device_strings
[0], NULL
, &try_ss
,
1391 &try_map
, mode
, &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
, exact_channels
);
1393 if (pcm
&& !exact_channels
)
1394 m
->channel_map
= try_map
;
1399 static void profile_finalize_probing(pa_alsa_profile
*p
) {
1403 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1410 snd_pcm_close(m
->output_pcm
);
1411 m
->output_pcm
= NULL
;
1414 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1421 snd_pcm_close(m
->input_pcm
);
1422 m
->input_pcm
= NULL
;
1426 static void ucm_mapping_jack_probe(pa_alsa_mapping
*m
) {
1427 snd_pcm_t
*pcm_handle
;
1428 snd_mixer_t
*mixer_handle
;
1429 snd_hctl_t
*hctl_handle
;
1430 pa_alsa_ucm_mapping_context
*context
= &m
->ucm_context
;
1431 pa_alsa_ucm_device
*dev
;
1434 pcm_handle
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? m
->output_pcm
: m
->input_pcm
;
1435 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
1436 if (!mixer_handle
|| !hctl_handle
)
1439 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1441 jack
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? dev
->output_jack
: dev
->input_jack
;
1443 jack
->has_control
= pa_alsa_find_jack(hctl_handle
, jack
->alsa_name
) != NULL
;
1444 pa_log_info("UCM jack %s has_control=%d", jack
->name
, jack
->has_control
);
1447 snd_mixer_close(mixer_handle
);
1450 static void ucm_probe_profile_set(pa_alsa_ucm_config
*ucm
, pa_alsa_profile_set
*ps
) {
1456 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
1458 pa_log_info("Set ucm verb to %s", p
->name
);
1460 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", p
->name
)) < 0) {
1461 pa_log("Failed to set verb %s", p
->name
);
1462 p
->supported
= false;
1466 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1467 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1468 /* Skip jack probing on modifier PCMs since we expect this to
1469 * only be controlled on the main device/verb PCM. */
1473 m
->output_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_PLAYBACK
);
1474 if (!m
->output_pcm
) {
1475 p
->supported
= false;
1481 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1482 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1483 /* Skip jack probing on modifier PCMs since we expect this to
1484 * only be controlled on the main device/verb PCM. */
1488 m
->input_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_CAPTURE
);
1489 if (!m
->input_pcm
) {
1490 p
->supported
= false;
1496 if (!p
->supported
) {
1497 profile_finalize_probing(p
);
1501 pa_log_debug("Profile %s supported.", p
->name
);
1503 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
1504 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1505 ucm_mapping_jack_probe(m
);
1507 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
1508 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1509 ucm_mapping_jack_probe(m
);
1511 profile_finalize_probing(p
);
1514 /* restore ucm state */
1515 snd_use_case_set(ucm
->ucm_mgr
, "_verb", SND_USE_CASE_VERB_INACTIVE
);
1517 pa_alsa_profile_set_drop_unsupported(ps
);
1520 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1521 pa_alsa_ucm_verb
*verb
;
1522 pa_alsa_profile_set
*ps
;
1524 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
1525 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1526 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1527 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1529 /* create a profile for each verb */
1530 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
1531 const char *verb_name
;
1532 const char *verb_desc
;
1534 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1535 verb_desc
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1536 if (verb_name
== NULL
) {
1537 pa_log("Verb with no name");
1541 ucm_create_profile(ucm
, ps
, verb
, verb_name
, verb_desc
);
1544 ucm_probe_profile_set(ucm
, ps
);
1550 static void free_verb(pa_alsa_ucm_verb
*verb
) {
1551 pa_alsa_ucm_device
*di
, *dn
;
1552 pa_alsa_ucm_modifier
*mi
, *mn
;
1554 PA_LLIST_FOREACH_SAFE(di
, dn
, verb
->devices
) {
1555 PA_LLIST_REMOVE(pa_alsa_ucm_device
, verb
->devices
, di
);
1556 pa_proplist_free(di
->proplist
);
1557 if (di
->conflicting_devices
)
1558 pa_idxset_free(di
->conflicting_devices
, NULL
);
1559 if (di
->supported_devices
)
1560 pa_idxset_free(di
->supported_devices
, NULL
);
1564 PA_LLIST_FOREACH_SAFE(mi
, mn
, verb
->modifiers
) {
1565 PA_LLIST_REMOVE(pa_alsa_ucm_modifier
, verb
->modifiers
, mi
);
1566 pa_proplist_free(mi
->proplist
);
1567 if (mi
->n_suppdev
> 0)
1568 snd_use_case_free_list(mi
->supported_devices
, mi
->n_suppdev
);
1569 if (mi
->n_confdev
> 0)
1570 snd_use_case_free_list(mi
->conflicting_devices
, mi
->n_confdev
);
1571 pa_xfree(mi
->media_role
);
1574 pa_proplist_free(verb
->proplist
);
1578 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1579 pa_alsa_ucm_verb
*vi
, *vn
;
1580 pa_alsa_jack
*ji
, *jn
;
1582 PA_LLIST_FOREACH_SAFE(vi
, vn
, ucm
->verbs
) {
1583 PA_LLIST_REMOVE(pa_alsa_ucm_verb
, ucm
->verbs
, vi
);
1586 PA_LLIST_FOREACH_SAFE(ji
, jn
, ucm
->jacks
) {
1587 PA_LLIST_REMOVE(pa_alsa_jack
, ucm
->jacks
, ji
);
1588 pa_xfree(ji
->alsa_name
);
1593 snd_use_case_mgr_close(ucm
->ucm_mgr
);
1594 ucm
->ucm_mgr
= NULL
;
1598 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1599 pa_alsa_ucm_device
*dev
;
1600 pa_alsa_ucm_modifier
*mod
;
1603 if (context
->ucm_devices
) {
1604 /* clear ucm device pointer to mapping */
1605 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1606 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1607 dev
->playback_mapping
= NULL
;
1609 dev
->capture_mapping
= NULL
;
1612 pa_idxset_free(context
->ucm_devices
, NULL
);
1615 if (context
->ucm_modifiers
) {
1616 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
1617 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1618 mod
->playback_mapping
= NULL
;
1620 mod
->capture_mapping
= NULL
;
1623 pa_idxset_free(context
->ucm_modifiers
, NULL
);
1627 /* Enable the modifier when the first stream with matched role starts */
1628 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1629 pa_alsa_ucm_modifier
*mod
;
1631 if (!ucm
->active_verb
)
1634 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1635 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1636 if (mod
->enabled_counter
== 0) {
1637 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1639 pa_log_info("Enable ucm modifier %s", mod_name
);
1640 if (snd_use_case_set(ucm
->ucm_mgr
, "_enamod", mod_name
) < 0) {
1641 pa_log("Failed to enable ucm modifier %s", mod_name
);
1645 mod
->enabled_counter
++;
1651 /* Disable the modifier when the last stream with matched role ends */
1652 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1653 pa_alsa_ucm_modifier
*mod
;
1655 if (!ucm
->active_verb
)
1658 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1659 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1661 mod
->enabled_counter
--;
1662 if (mod
->enabled_counter
== 0) {
1663 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1665 pa_log_info("Disable ucm modifier %s", mod_name
);
1666 if (snd_use_case_set(ucm
->ucm_mgr
, "_dismod", mod_name
) < 0) {
1667 pa_log("Failed to disable ucm modifier %s", mod_name
);
1676 #else /* HAVE_ALSA_UCM */
1678 /* Dummy functions for systems without UCM support */
1680 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
) {
1681 pa_log_info("UCM not available.");
1685 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1689 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
1693 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
) {
1697 void pa_alsa_ucm_add_ports(
1699 pa_proplist
*proplist
,
1700 pa_alsa_ucm_mapping_context
*context
,
1705 void pa_alsa_ucm_add_ports_combination(
1707 pa_alsa_ucm_mapping_context
*context
,
1710 pa_card_profile
*cp
,
1714 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
1718 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1721 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1724 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1727 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {