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 && ui
< PA_CHANNELS_MAX
)
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 && ui
< PA_CHANNELS_MAX
)
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 && ui
< PA_RATE_MAX
) {
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 && ui
< PA_RATE_MAX
) {
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 if (!m
->description
)
1112 /* Modifier sinks should not be routed to by default */
1115 mod_name
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1116 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_MODIFIER
, mod_name
);
1118 /* save mapping to ucm modifier */
1119 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1120 modifier
->playback_mapping
= m
;
1121 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
1123 modifier
->capture_mapping
= m
;
1124 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
1128 pa_assert_se(pa_atou(channel_str
, &channels
) == 0 && channels
< PA_CHANNELS_MAX
);
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 snd_hctl_t
*hctl_handle
;
1428 pa_alsa_ucm_mapping_context
*context
= &m
->ucm_context
;
1429 pa_alsa_ucm_device
*dev
;
1432 pcm_handle
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? m
->output_pcm
: m
->input_pcm
;
1433 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
1434 if (!mixer_handle
|| !hctl_handle
)
1437 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1439 jack
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? dev
->output_jack
: dev
->input_jack
;
1441 jack
->has_control
= pa_alsa_find_jack(hctl_handle
, jack
->alsa_name
) != NULL
;
1442 pa_log_info("UCM jack %s has_control=%d", jack
->name
, jack
->has_control
);
1445 snd_mixer_close(mixer_handle
);
1448 static void ucm_probe_profile_set(pa_alsa_ucm_config
*ucm
, pa_alsa_profile_set
*ps
) {
1454 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
1456 pa_log_info("Set ucm verb to %s", p
->name
);
1458 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", p
->name
)) < 0) {
1459 pa_log("Failed to set verb %s", p
->name
);
1460 p
->supported
= false;
1464 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1465 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1466 /* Skip jack probing on modifier PCMs since we expect this to
1467 * only be controlled on the main device/verb PCM. */
1471 m
->output_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_PLAYBACK
);
1472 if (!m
->output_pcm
) {
1473 p
->supported
= false;
1479 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1480 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1481 /* Skip jack probing on modifier PCMs since we expect this to
1482 * only be controlled on the main device/verb PCM. */
1486 m
->input_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_CAPTURE
);
1487 if (!m
->input_pcm
) {
1488 p
->supported
= false;
1494 if (!p
->supported
) {
1495 profile_finalize_probing(p
);
1499 pa_log_debug("Profile %s supported.", p
->name
);
1501 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
1502 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1503 ucm_mapping_jack_probe(m
);
1505 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
1506 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1507 ucm_mapping_jack_probe(m
);
1509 profile_finalize_probing(p
);
1512 /* restore ucm state */
1513 snd_use_case_set(ucm
->ucm_mgr
, "_verb", SND_USE_CASE_VERB_INACTIVE
);
1515 pa_alsa_profile_set_drop_unsupported(ps
);
1518 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1519 pa_alsa_ucm_verb
*verb
;
1520 pa_alsa_profile_set
*ps
;
1522 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
1523 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1524 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1525 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1527 /* create a profile for each verb */
1528 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
1529 const char *verb_name
;
1530 const char *verb_desc
;
1532 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1533 verb_desc
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1534 if (verb_name
== NULL
) {
1535 pa_log("Verb with no name");
1539 ucm_create_profile(ucm
, ps
, verb
, verb_name
, verb_desc
);
1542 ucm_probe_profile_set(ucm
, ps
);
1548 static void free_verb(pa_alsa_ucm_verb
*verb
) {
1549 pa_alsa_ucm_device
*di
, *dn
;
1550 pa_alsa_ucm_modifier
*mi
, *mn
;
1552 PA_LLIST_FOREACH_SAFE(di
, dn
, verb
->devices
) {
1553 PA_LLIST_REMOVE(pa_alsa_ucm_device
, verb
->devices
, di
);
1554 pa_proplist_free(di
->proplist
);
1555 if (di
->conflicting_devices
)
1556 pa_idxset_free(di
->conflicting_devices
, NULL
);
1557 if (di
->supported_devices
)
1558 pa_idxset_free(di
->supported_devices
, NULL
);
1562 PA_LLIST_FOREACH_SAFE(mi
, mn
, verb
->modifiers
) {
1563 PA_LLIST_REMOVE(pa_alsa_ucm_modifier
, verb
->modifiers
, mi
);
1564 pa_proplist_free(mi
->proplist
);
1565 if (mi
->n_suppdev
> 0)
1566 snd_use_case_free_list(mi
->supported_devices
, mi
->n_suppdev
);
1567 if (mi
->n_confdev
> 0)
1568 snd_use_case_free_list(mi
->conflicting_devices
, mi
->n_confdev
);
1569 pa_xfree(mi
->media_role
);
1572 pa_proplist_free(verb
->proplist
);
1576 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1577 pa_alsa_ucm_verb
*vi
, *vn
;
1578 pa_alsa_jack
*ji
, *jn
;
1580 PA_LLIST_FOREACH_SAFE(vi
, vn
, ucm
->verbs
) {
1581 PA_LLIST_REMOVE(pa_alsa_ucm_verb
, ucm
->verbs
, vi
);
1584 PA_LLIST_FOREACH_SAFE(ji
, jn
, ucm
->jacks
) {
1585 PA_LLIST_REMOVE(pa_alsa_jack
, ucm
->jacks
, ji
);
1586 pa_xfree(ji
->alsa_name
);
1591 snd_use_case_mgr_close(ucm
->ucm_mgr
);
1592 ucm
->ucm_mgr
= NULL
;
1596 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1597 pa_alsa_ucm_device
*dev
;
1598 pa_alsa_ucm_modifier
*mod
;
1601 if (context
->ucm_devices
) {
1602 /* clear ucm device pointer to mapping */
1603 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1604 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1605 dev
->playback_mapping
= NULL
;
1607 dev
->capture_mapping
= NULL
;
1610 pa_idxset_free(context
->ucm_devices
, NULL
);
1613 if (context
->ucm_modifiers
) {
1614 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
1615 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1616 mod
->playback_mapping
= NULL
;
1618 mod
->capture_mapping
= NULL
;
1621 pa_idxset_free(context
->ucm_modifiers
, NULL
);
1625 /* Enable the modifier when the first stream with matched role starts */
1626 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1627 pa_alsa_ucm_modifier
*mod
;
1629 if (!ucm
->active_verb
)
1632 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1633 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1634 if (mod
->enabled_counter
== 0) {
1635 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1637 pa_log_info("Enable ucm modifier %s", mod_name
);
1638 if (snd_use_case_set(ucm
->ucm_mgr
, "_enamod", mod_name
) < 0) {
1639 pa_log("Failed to enable ucm modifier %s", mod_name
);
1643 mod
->enabled_counter
++;
1649 /* Disable the modifier when the last stream with matched role ends */
1650 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1651 pa_alsa_ucm_modifier
*mod
;
1653 if (!ucm
->active_verb
)
1656 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1657 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1659 mod
->enabled_counter
--;
1660 if (mod
->enabled_counter
== 0) {
1661 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1663 pa_log_info("Disable ucm modifier %s", mod_name
);
1664 if (snd_use_case_set(ucm
->ucm_mgr
, "_dismod", mod_name
) < 0) {
1665 pa_log("Failed to disable ucm modifier %s", mod_name
);
1674 #else /* HAVE_ALSA_UCM */
1676 /* Dummy functions for systems without UCM support */
1678 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
) {
1679 pa_log_info("UCM not available.");
1683 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1687 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
1691 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
) {
1695 void pa_alsa_ucm_add_ports(
1697 pa_proplist
*proplist
,
1698 pa_alsa_ucm_mapping_context
*context
,
1703 void pa_alsa_ucm_add_ports_combination(
1705 pa_alsa_ucm_mapping_context
*context
,
1708 pa_card_profile
*cp
,
1712 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
1716 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1719 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1722 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1725 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {