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 {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
},
88 {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME
},
89 {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH
},
90 {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
},
91 {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
},
92 {"TQ", PA_ALSA_PROP_UCM_QOS
},
96 /* UCM verb info - this should eventually be part of policy manangement */
97 static struct ucm_info verb_info
[] = {
98 {SND_USE_CASE_VERB_INACTIVE
, 0},
99 {SND_USE_CASE_VERB_HIFI
, 8000},
100 {SND_USE_CASE_VERB_HIFI_LOW_POWER
, 7000},
101 {SND_USE_CASE_VERB_VOICE
, 6000},
102 {SND_USE_CASE_VERB_VOICE_LOW_POWER
, 5000},
103 {SND_USE_CASE_VERB_VOICECALL
, 4000},
104 {SND_USE_CASE_VERB_IP_VOICECALL
, 4000},
105 {SND_USE_CASE_VERB_ANALOG_RADIO
, 3000},
106 {SND_USE_CASE_VERB_DIGITAL_RADIO
, 3000},
110 /* UCM device info - should be overwritten by ucm property */
111 static struct ucm_info dev_info
[] = {
112 {SND_USE_CASE_DEV_SPEAKER
, 100},
113 {SND_USE_CASE_DEV_LINE
, 100},
114 {SND_USE_CASE_DEV_HEADPHONES
, 100},
115 {SND_USE_CASE_DEV_HEADSET
, 300},
116 {SND_USE_CASE_DEV_HANDSET
, 200},
117 {SND_USE_CASE_DEV_BLUETOOTH
, 400},
118 {SND_USE_CASE_DEV_EARPIECE
, 100},
119 {SND_USE_CASE_DEV_SPDIF
, 100},
120 {SND_USE_CASE_DEV_HDMI
, 100},
121 {SND_USE_CASE_DEV_NONE
, 100},
125 /* UCM profile properties - The verb data is store so it can be used to fill
126 * the new profiles properties */
127 static int ucm_get_property(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
, const char *verb_name
) {
132 for (i
= 0; item
[i
].id
; i
++) {
135 id
= pa_sprintf_malloc("=%s//%s", item
[i
].id
, verb_name
);
136 err
= snd_use_case_get(uc_mgr
, id
, &value
);
141 pa_log_debug("Got %s for verb %s: %s", item
[i
].id
, verb_name
, value
);
142 pa_proplist_sets(verb
->proplist
, item
[i
].property
, value
);
149 static int ucm_device_exists(pa_idxset
*idxset
, pa_alsa_ucm_device
*dev
) {
150 pa_alsa_ucm_device
*d
;
153 PA_IDXSET_FOREACH(d
, idxset
, idx
)
160 static void ucm_add_devices_to_idxset(
162 pa_alsa_ucm_device
*me
,
163 pa_alsa_ucm_device
*devices
,
164 const char **dev_names
,
167 pa_alsa_ucm_device
*d
;
169 PA_LLIST_FOREACH(d
, devices
) {
176 name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
178 for (i
= 0; i
< n
; i
++)
179 if (pa_streq(dev_names
[i
], name
))
180 pa_idxset_put(idxset
, d
, NULL
);
184 /* Create a property list for this ucm device */
185 static int ucm_get_device_property(
186 pa_alsa_ucm_device
*device
,
187 snd_use_case_mgr_t
*uc_mgr
,
188 pa_alsa_ucm_verb
*verb
,
189 const char *device_name
) {
192 const char **devices
;
197 int n_confdev
, n_suppdev
;
199 for (i
= 0; item
[i
].id
; i
++) {
200 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, device_name
);
201 err
= snd_use_case_get(uc_mgr
, id
, &value
);
206 pa_log_debug("Got %s for device %s: %s", item
[i
].id
, device_name
, value
);
207 pa_proplist_sets(device
->proplist
, item
[i
].property
, value
);
211 /* get direction and channels */
212 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
213 if (value
) { /* output */
215 if (pa_atou(value
, &ui
) == 0 && ui
< PA_CHANNELS_MAX
)
216 device
->playback_channels
= ui
;
218 pa_log("UCM playback channels %s for device %s out of range", value
, device_name
);
221 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_SINK
);
222 if (!value
) { /* take pcm from verb playback default */
223 value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_SINK
);
225 pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name
, value
);
226 pa_proplist_sets(device
->proplist
, PA_ALSA_PROP_UCM_SINK
, value
);
228 pa_log("UCM playback device %s fetch pcm failed", device_name
);
232 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
233 if (value
) { /* input */
235 if (pa_atou(value
, &ui
) == 0 && ui
< PA_CHANNELS_MAX
)
236 device
->capture_channels
= ui
;
238 pa_log("UCM capture channels %s for device %s out of range", value
, device_name
);
241 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
242 if (!value
) { /* take pcm from verb capture default */
243 value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
245 pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name
, value
);
246 pa_proplist_sets(device
->proplist
, PA_ALSA_PROP_UCM_SOURCE
, value
);
248 pa_log("UCM capture device %s fetch pcm failed", device_name
);
252 if (device
->playback_channels
== 0 && device
->capture_channels
== 0) {
253 pa_log_warn("UCM file does not specify 'PlaybackChannels' or 'CaptureChannels'"
254 "for device %s, assuming stereo duplex.", device_name
);
255 device
->playback_channels
= 2;
256 device
->capture_channels
= 2;
259 /* get priority of device */
260 if (device
->playback_channels
) { /* sink device */
261 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY
);
263 /* get priority from ucm config */
264 if (pa_atou(value
, &ui
) == 0)
265 device
->playback_priority
= ui
;
267 pa_log_debug("UCM playback priority %s for device %s error", value
, device_name
);
271 if (device
->capture_channels
) { /* source device */
272 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
);
274 /* get priority from ucm config */
275 if (pa_atou(value
, &ui
) == 0)
276 device
->capture_priority
= ui
;
278 pa_log_debug("UCM capture priority %s for device %s error", value
, device_name
);
282 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
) || PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
283 /* get priority from static table */
284 for (i
= 0; dev_info
[i
].id
; i
++) {
285 if (strcasecmp(dev_info
[i
].id
, device_name
) == 0) {
286 PA_UCM_DEVICE_PRIORITY_SET(device
, dev_info
[i
].priority
);
292 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
)) {
293 /* fall through to default priority */
294 device
->playback_priority
= 100;
297 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
298 /* fall through to default priority */
299 device
->capture_priority
= 100;
302 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name
);
303 n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
307 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name
);
309 device
->conflicting_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
310 ucm_add_devices_to_idxset(device
->conflicting_devices
, device
, verb
->devices
, devices
, n_confdev
);
311 snd_use_case_free_list(devices
, n_confdev
);
314 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name
);
315 n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
319 pa_log_debug("No %s for device %s", "_supporteddevs", device_name
);
321 device
->supported_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
322 ucm_add_devices_to_idxset(device
->supported_devices
, device
, verb
->devices
, devices
, n_suppdev
);
323 snd_use_case_free_list(devices
, n_suppdev
);
329 /* Create a property list for this ucm modifier */
330 static int ucm_get_modifier_property(pa_alsa_ucm_modifier
*modifier
, snd_use_case_mgr_t
*uc_mgr
, const char *modifier_name
) {
335 for (i
= 0; item
[i
].id
; i
++) {
338 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, modifier_name
);
339 err
= snd_use_case_get(uc_mgr
, id
, &value
);
344 pa_log_debug("Got %s for modifier %s: %s", item
[i
].id
, modifier_name
, value
);
345 pa_proplist_sets(modifier
->proplist
, item
[i
].property
, value
);
349 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name
);
350 modifier
->n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &modifier
->conflicting_devices
);
352 if (modifier
->n_confdev
< 0)
353 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name
);
355 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name
);
356 modifier
->n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &modifier
->supported_devices
);
358 if (modifier
->n_suppdev
< 0)
359 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name
);
364 /* Create a list of devices for this verb */
365 static int ucm_get_devices(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
366 const char **dev_list
;
369 num_dev
= snd_use_case_get_list(uc_mgr
, "_devices", &dev_list
);
373 for (i
= 0; i
< num_dev
; i
+= 2) {
374 pa_alsa_ucm_device
*d
= pa_xnew0(pa_alsa_ucm_device
, 1);
376 d
->proplist
= pa_proplist_new();
377 pa_proplist_sets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(dev_list
[i
]));
378 pa_proplist_sets(d
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(dev_list
[i
+ 1]));
380 PA_LLIST_PREPEND(pa_alsa_ucm_device
, verb
->devices
, d
);
383 snd_use_case_free_list(dev_list
, num_dev
);
388 static int ucm_get_modifiers(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
389 const char **mod_list
;
392 num_mod
= snd_use_case_get_list(uc_mgr
, "_modifiers", &mod_list
);
396 for (i
= 0; i
< num_mod
; i
+= 2) {
397 pa_alsa_ucm_modifier
*m
;
400 pa_log_warn("Got a modifier with a null name. Skipping.");
404 m
= pa_xnew0(pa_alsa_ucm_modifier
, 1);
405 m
->proplist
= pa_proplist_new();
407 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_NAME
, mod_list
[i
]);
408 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(mod_list
[i
+ 1]));
410 PA_LLIST_PREPEND(pa_alsa_ucm_modifier
, verb
->modifiers
, m
);
413 snd_use_case_free_list(mod_list
, num_mod
);
418 static void add_role_to_device(pa_alsa_ucm_device
*dev
, const char *dev_name
, const char *role_name
, const char *role
) {
419 const char *cur
= pa_proplist_gets(dev
->proplist
, role_name
);
422 pa_proplist_sets(dev
->proplist
, role_name
, role
);
423 else if (!pa_str_in_list_spaces(cur
, role
)) { /* does not exist */
424 char *value
= pa_sprintf_malloc("%s %s", cur
, role
);
426 pa_proplist_sets(dev
->proplist
, role_name
, value
);
430 pa_log_info("Add role %s to device %s(%s), result %s", role
, dev_name
, role_name
, pa_proplist_gets(dev
->proplist
,
434 static void add_media_role(const char *name
, pa_alsa_ucm_device
*list
, const char *role_name
, const char *role
, bool is_sink
) {
435 pa_alsa_ucm_device
*d
;
437 PA_LLIST_FOREACH(d
, list
) {
438 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
440 if (pa_streq(dev_name
, name
)) {
441 const char *sink
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_SINK
);
442 const char *source
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
445 add_role_to_device(d
, dev_name
, role_name
, role
);
446 else if (!is_sink
&& source
)
447 add_role_to_device(d
, dev_name
, role_name
, role
);
453 static char *modifier_name_to_role(const char *mod_name
, bool *is_sink
) {
454 char *sub
= NULL
, *tmp
;
458 if (pa_startswith(mod_name
, "Play")) {
460 sub
= pa_xstrdup(mod_name
+ 4);
461 } else if (pa_startswith(mod_name
, "Capture"))
462 sub
= pa_xstrdup(mod_name
+ 7);
466 pa_log_warn("Can't match media roles for modifer %s", mod_name
);
473 *tmp
= tolower(*tmp
);
479 static void ucm_set_media_roles(pa_alsa_ucm_modifier
*modifier
, pa_alsa_ucm_device
*list
, const char *mod_name
) {
481 bool is_sink
= false;
483 const char *role_name
;
485 sub
= modifier_name_to_role(mod_name
, &is_sink
);
489 modifier
->action_direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
490 modifier
->media_role
= sub
;
492 role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
493 for (i
= 0; i
< modifier
->n_suppdev
; i
++) {
494 /* if modifier has no specific pcm, we add role intent to its supported devices */
495 if (!pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_SINK
) &&
496 !pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_SOURCE
))
497 add_media_role(modifier
->supported_devices
[i
], list
, role_name
, sub
, is_sink
);
501 static void append_lost_relationship(pa_alsa_ucm_device
*dev
) {
503 pa_alsa_ucm_device
*d
;
505 if (dev
->conflicting_devices
) {
506 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
507 if (!d
->conflicting_devices
)
508 d
->conflicting_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
510 if (pa_idxset_put(d
->conflicting_devices
, dev
, NULL
) == 0)
511 pa_log_warn("Add lost conflicting device %s to %s",
512 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
),
513 pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
));
517 if (dev
->supported_devices
) {
518 PA_IDXSET_FOREACH(d
, dev
->supported_devices
, idx
) {
519 if (!d
->supported_devices
)
520 d
->supported_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
522 if (pa_idxset_put(d
->supported_devices
, dev
, NULL
) == 0)
523 pa_log_warn("Add lost supported device %s to %s",
524 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
),
525 pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
));
530 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
) {
532 const char **verb_list
;
533 int num_verbs
, i
, err
= 0;
535 /* is UCM available for this card ? */
536 err
= snd_card_get_name(card_index
, &card_name
);
538 pa_log("Card can't get card_name from card_index %d", card_index
);
542 err
= snd_use_case_mgr_open(&ucm
->ucm_mgr
, card_name
);
544 pa_log_info("UCM not available for card %s", card_name
);
548 pa_log_info("UCM available for card %s", card_name
);
550 /* get a list of all UCM verbs (profiles) for this card */
551 num_verbs
= snd_use_case_verb_list(ucm
->ucm_mgr
, &verb_list
);
553 pa_log("UCM verb list not found for %s", card_name
);
557 /* get the properties of each UCM verb */
558 for (i
= 0; i
< num_verbs
; i
+= 2) {
559 pa_alsa_ucm_verb
*verb
;
561 /* Get devices and modifiers for each verb */
562 err
= pa_alsa_ucm_get_verb(ucm
->ucm_mgr
, verb_list
[i
], verb_list
[i
+1], &verb
);
564 pa_log("Failed to get the verb %s", verb_list
[i
]);
568 PA_LLIST_PREPEND(pa_alsa_ucm_verb
, ucm
->verbs
, verb
);
572 pa_log("No UCM verb is valid for %s", card_name
);
576 snd_use_case_free_list(verb_list
, num_verbs
);
580 snd_use_case_mgr_close(ucm
->ucm_mgr
);
591 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
) {
592 pa_alsa_ucm_device
*d
;
593 pa_alsa_ucm_modifier
*mod
;
594 pa_alsa_ucm_verb
*verb
;
598 pa_log_info("Set UCM verb to %s", verb_name
);
599 err
= snd_use_case_set(uc_mgr
, "_verb", verb_name
);
603 verb
= pa_xnew0(pa_alsa_ucm_verb
, 1);
604 verb
->proplist
= pa_proplist_new();
606 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(verb_name
));
607 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(verb_desc
));
609 err
= ucm_get_devices(verb
, uc_mgr
);
611 pa_log("No UCM devices for verb %s", verb_name
);
613 err
= ucm_get_modifiers(verb
, uc_mgr
);
615 pa_log("No UCM modifiers for verb %s", verb_name
);
617 /* Verb properties */
618 ucm_get_property(verb
, uc_mgr
, verb_name
);
620 PA_LLIST_FOREACH(d
, verb
->devices
) {
621 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
623 /* Devices properties */
624 ucm_get_device_property(d
, uc_mgr
, verb
, dev_name
);
626 /* make conflicting or supported device mutual */
627 PA_LLIST_FOREACH(d
, verb
->devices
)
628 append_lost_relationship(d
);
630 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
631 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
633 /* Modifier properties */
634 ucm_get_modifier_property(mod
, uc_mgr
, mod_name
);
636 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
637 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name
, mod_name
);
638 ucm_set_media_roles(mod
, verb
->devices
, mod_name
);
645 static void ucm_add_port_combination(
647 pa_alsa_ucm_mapping_context
*context
,
649 pa_alsa_ucm_device
**pdevices
,
655 pa_device_port
*port
;
659 const char *dev_name
;
660 const char *direction
;
661 pa_alsa_ucm_device
*dev
;
664 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
666 name
= pa_sprintf_malloc("%s%s", is_sink
? PA_UCM_PRE_TAG_OUTPUT
: PA_UCM_PRE_TAG_INPUT
, dev_name
);
667 desc
= num
== 1 ? pa_xstrdup(pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
))
668 : pa_sprintf_malloc("Combination port for %s", dev_name
);
670 priority
= is_sink
? dev
->playback_priority
: dev
->capture_priority
;
672 for (i
= 1; i
< num
; i
++) {
676 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
678 tmp
= pa_sprintf_malloc("%s+%s", name
, dev_name
);
682 tmp
= pa_sprintf_malloc("%s,%s", desc
, dev_name
);
686 /* FIXME: Is this true? */
687 priority
+= (is_sink
? dev
->playback_priority
: dev
->capture_priority
);
690 port
= pa_hashmap_get(ports
, name
);
692 pa_device_port_new_data port_data
;
694 pa_device_port_new_data_init(&port_data
);
695 pa_device_port_new_data_set_name(&port_data
, name
);
696 pa_device_port_new_data_set_description(&port_data
, desc
);
697 pa_device_port_new_data_set_direction(&port_data
, is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
);
699 port
= pa_device_port_new(core
, &port_data
, 0);
700 pa_device_port_new_data_done(&port_data
);
703 pa_hashmap_put(ports
, port
->name
, port
);
704 pa_log_debug("Add port %s: %s", port
->name
, port
->description
);
705 port
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
708 port
->priority
= priority
;
713 direction
= is_sink
? "output" : "input";
714 pa_log_debug("Port %s direction %s, priority %d", port
->name
, direction
, priority
);
717 pa_log_debug("Adding port %s to profile %s", port
->name
, cp
->name
);
718 pa_hashmap_put(port
->profiles
, cp
->name
, cp
);
722 pa_hashmap_put(hash
, port
->name
, port
);
723 pa_device_port_ref(port
);
727 static int ucm_port_contains(const char *port_name
, const char *dev_name
, bool is_sink
) {
730 const char *state
= NULL
;
733 if (!port_name
|| !dev_name
)
736 port_name
+= is_sink
? strlen(PA_UCM_PRE_TAG_OUTPUT
) : strlen(PA_UCM_PRE_TAG_INPUT
);
738 while ((r
= pa_split_in_place(port_name
, "+", &len
, &state
))) {
739 if (!strncmp(r
, dev_name
, len
)) {
748 static int ucm_check_conformance(
749 pa_alsa_ucm_mapping_context
*context
,
750 pa_alsa_ucm_device
**pdevices
,
752 pa_alsa_ucm_device
*dev
) {
755 pa_alsa_ucm_device
*d
;
760 pa_log_debug("Check device %s conformance with %d other devices",
761 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
), dev_num
);
763 pa_log_debug("First device in combination, number 1");
767 if (dev
->conflicting_devices
) { /* the device defines conflicting devices */
768 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
769 for (i
= 0; i
< dev_num
; i
++) {
770 if (pdevices
[i
] == d
) {
771 pa_log_debug("Conflicting device found");
776 } else if (dev
->supported_devices
) { /* the device defines supported devices */
777 for (i
= 0; i
< dev_num
; i
++) {
778 if (!ucm_device_exists(dev
->supported_devices
, pdevices
[i
])) {
779 pa_log_debug("Supported device not found");
783 } else { /* not support any other devices */
784 pa_log_debug("Not support any other devices");
788 pa_log_debug("Device added to combination, number %d", dev_num
+ 1);
792 static inline pa_alsa_ucm_device
*get_next_device(pa_idxset
*idxset
, uint32_t *idx
) {
793 pa_alsa_ucm_device
*dev
;
795 if (*idx
== PA_IDXSET_INVALID
)
796 dev
= pa_idxset_first(idxset
, idx
);
798 dev
= pa_idxset_next(idxset
, idx
);
803 static void ucm_add_ports_combination(
805 pa_alsa_ucm_mapping_context
*context
,
807 pa_alsa_ucm_device
**pdevices
,
814 pa_alsa_ucm_device
*dev
;
815 uint32_t idx
= map_index
;
817 if ((dev
= get_next_device(context
->ucm_devices
, &idx
)) == NULL
)
820 /* check if device at map_index can combine with existing devices combination */
821 if (ucm_check_conformance(context
, pdevices
, dev_num
, dev
)) {
822 /* add device at map_index to devices combination */
823 pdevices
[dev_num
] = dev
;
824 /* add current devices combination as a new port */
825 ucm_add_port_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, ports
, cp
, core
);
826 /* try more elements combination */
827 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, idx
, ports
, cp
, core
);
830 /* try other device with current elements number */
831 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
, idx
, ports
, cp
, core
);
834 static char* merge_roles(const char *cur
, const char *add
) {
836 const char *state
= NULL
;
839 return pa_xstrdup(cur
);
840 else if (cur
== NULL
)
841 return pa_xstrdup(add
);
843 ret
= pa_xstrdup(cur
);
845 while ((r
= pa_split_spaces(add
, &state
))) {
848 if (!pa_str_in_list_spaces(ret
, r
))
849 value
= pa_sprintf_malloc("%s %s", ret
, r
);
863 void pa_alsa_ucm_add_ports_combination(
865 pa_alsa_ucm_mapping_context
*context
,
871 pa_alsa_ucm_device
**pdevices
;
873 pa_assert(context
->ucm_devices
);
875 if (pa_idxset_size(context
->ucm_devices
) > 0) {
876 pdevices
= pa_xnew(pa_alsa_ucm_device
*, pa_idxset_size(context
->ucm_devices
));
877 ucm_add_ports_combination(p
, context
, is_sink
, pdevices
, 0, PA_IDXSET_INVALID
, ports
, cp
, core
);
882 void pa_alsa_ucm_add_ports(
884 pa_proplist
*proplist
,
885 pa_alsa_ucm_mapping_context
*context
,
891 const char *role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
892 pa_alsa_ucm_device
*dev
;
893 pa_alsa_ucm_modifier
*mod
;
899 /* add ports first */
900 pa_alsa_ucm_add_ports_combination(*p
, context
, is_sink
, card
->ports
, NULL
, card
->core
);
902 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
903 merged_roles
= pa_xstrdup(pa_proplist_gets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
));
904 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
905 const char *roles
= pa_proplist_gets(dev
->proplist
, role_name
);
906 tmp
= merge_roles(merged_roles
, roles
);
907 pa_xfree(merged_roles
);
911 if (context
->ucm_modifiers
)
912 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
913 tmp
= merge_roles(merged_roles
, mod
->media_role
);
914 pa_xfree(merged_roles
);
919 pa_proplist_sets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
, merged_roles
);
921 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist
, PA_PROP_DEVICE_STRING
), pa_strnull(merged_roles
));
922 pa_xfree(merged_roles
);
925 /* Change UCM verb and device to match selected card profile */
926 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
929 pa_alsa_ucm_verb
*verb
;
931 if (new_profile
== old_profile
)
933 else if (new_profile
== NULL
|| old_profile
== NULL
)
934 profile
= new_profile
? new_profile
: SND_USE_CASE_VERB_INACTIVE
;
935 else if (!pa_streq(new_profile
, old_profile
))
936 profile
= new_profile
;
941 pa_log_info("Set UCM verb to %s", profile
);
942 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", profile
)) < 0) {
943 pa_log("Failed to set verb %s", profile
);
947 /* find active verb */
948 ucm
->active_verb
= NULL
;
949 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
950 const char *verb_name
;
951 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
952 if (pa_streq(verb_name
, profile
)) {
953 ucm
->active_verb
= verb
;
961 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
964 pa_alsa_ucm_config
*ucm
;
965 const char **enable_devs
;
968 pa_alsa_ucm_device
*dev
;
970 pa_assert(context
&& context
->ucm
);
973 pa_assert(ucm
->ucm_mgr
);
975 enable_devs
= pa_xnew(const char *, pa_idxset_size(context
->ucm_devices
));
977 /* first disable then enable */
978 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
979 const char *dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
981 if (ucm_port_contains(port
->name
, dev_name
, is_sink
))
982 enable_devs
[enable_num
++] = dev_name
;
984 pa_log_debug("Disable ucm device %s", dev_name
);
985 if (snd_use_case_set(ucm
->ucm_mgr
, "_disdev", dev_name
) > 0) {
986 pa_log("Failed to disable ucm device %s", dev_name
);
993 for (i
= 0; i
< enable_num
; i
++) {
994 pa_log_debug("Enable ucm device %s", enable_devs
[i
]);
995 if (snd_use_case_set(ucm
->ucm_mgr
, "_enadev", enable_devs
[i
]) < 0) {
996 pa_log("Failed to enable ucm device %s", enable_devs
[i
]);
1002 pa_xfree(enable_devs
);
1007 static void ucm_add_mapping(pa_alsa_profile
*p
, pa_alsa_mapping
*m
) {
1009 switch (m
->direction
) {
1010 case PA_ALSA_DIRECTION_ANY
:
1011 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1012 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1014 case PA_ALSA_DIRECTION_OUTPUT
:
1015 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1017 case PA_ALSA_DIRECTION_INPUT
:
1018 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1023 static void alsa_mapping_add_ucm_device(pa_alsa_mapping
*m
, pa_alsa_ucm_device
*device
) {
1025 const char *new_desc
;
1027 pa_idxset_put(m
->ucm_context
.ucm_devices
, device
, NULL
);
1029 new_desc
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1030 cur_desc
= m
->description
;
1032 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1034 m
->description
= pa_xstrdup(new_desc
);
1037 /* walk around null case */
1038 m
->description
= m
->description
? m
->description
: pa_xstrdup("");
1040 /* save mapping to ucm device */
1041 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1042 device
->playback_mapping
= m
;
1044 device
->capture_mapping
= m
;
1047 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping
*m
, pa_alsa_ucm_modifier
*modifier
) {
1049 const char *new_desc
, *mod_name
, *channel_str
;
1050 uint32_t channels
= 0;
1052 pa_idxset_put(m
->ucm_context
.ucm_modifiers
, modifier
, NULL
);
1054 new_desc
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1055 cur_desc
= m
->description
;
1057 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1059 m
->description
= pa_xstrdup(new_desc
);
1062 if (!m
->description
)
1065 /* Modifier sinks should not be routed to by default */
1068 mod_name
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1069 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_MODIFIER
, mod_name
);
1071 /* save mapping to ucm modifier */
1072 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1073 modifier
->playback_mapping
= m
;
1074 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
1076 modifier
->capture_mapping
= m
;
1077 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
1081 pa_assert_se(pa_atou(channel_str
, &channels
) == 0 && channels
< PA_CHANNELS_MAX
);
1082 pa_log_debug("Got channel count %" PRIu32
" for modifier", channels
);
1086 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1088 pa_channel_map_init(&m
->channel_map
);
1091 static int ucm_create_mapping_direction(
1092 pa_alsa_ucm_config
*ucm
,
1093 pa_alsa_profile_set
*ps
,
1095 pa_alsa_ucm_device
*device
,
1096 const char *verb_name
,
1097 const char *device_name
,
1098 const char *device_str
,
1103 unsigned priority
, channels
;
1105 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1107 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1109 pa_log("No mapping for %s", mapping_name
);
1110 pa_xfree(mapping_name
);
1113 pa_log_debug("UCM mapping: %s dev %s", mapping_name
, device_name
);
1114 pa_xfree(mapping_name
);
1116 priority
= is_sink
? device
->playback_priority
: device
->capture_priority
;
1117 channels
= is_sink
? device
->playback_channels
: device
->capture_channels
;
1119 if (!m
->ucm_context
.ucm_devices
) { /* new mapping */
1120 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1121 m
->ucm_context
.ucm
= ucm
;
1122 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1124 m
->device_strings
= pa_xnew0(char*, 2);
1125 m
->device_strings
[0] = pa_xstrdup(device_str
);
1126 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1128 ucm_add_mapping(p
, m
);
1129 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1132 /* mapping priority is the highest one of ucm devices */
1133 if (priority
> m
->priority
)
1134 m
->priority
= priority
;
1136 /* mapping channels is the lowest one of ucm devices */
1137 if (channels
< m
->channel_map
.channels
)
1138 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1140 alsa_mapping_add_ucm_device(m
, device
);
1145 static int ucm_create_mapping_for_modifier(
1146 pa_alsa_ucm_config
*ucm
,
1147 pa_alsa_profile_set
*ps
,
1149 pa_alsa_ucm_modifier
*modifier
,
1150 const char *verb_name
,
1151 const char *mod_name
,
1152 const char *device_str
,
1158 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1160 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1162 pa_log("no mapping for %s", mapping_name
);
1163 pa_xfree(mapping_name
);
1166 pa_log_info("ucm mapping: %s modifier %s", mapping_name
, mod_name
);
1167 pa_xfree(mapping_name
);
1169 if (!m
->ucm_context
.ucm_devices
&& !m
->ucm_context
.ucm_modifiers
) { /* 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_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1172 m
->ucm_context
.ucm
= ucm
;
1173 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1175 m
->device_strings
= pa_xnew0(char*, 2);
1176 m
->device_strings
[0] = pa_xstrdup(device_str
);
1177 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1178 /* Modifier sinks should not be routed to by default */
1181 ucm_add_mapping(p
, m
);
1182 } else if (!m
->ucm_context
.ucm_modifiers
) /* share pcm with device */
1183 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1185 alsa_mapping_add_ucm_modifier(m
, modifier
);
1190 static int ucm_create_mapping(
1191 pa_alsa_ucm_config
*ucm
,
1192 pa_alsa_profile_set
*ps
,
1194 pa_alsa_ucm_device
*device
,
1195 const char *verb_name
,
1196 const char *device_name
,
1198 const char *source
) {
1202 if (!sink
&& !source
) {
1203 pa_log("No sink and source at %s: %s", verb_name
, device_name
);
1208 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, sink
, true);
1209 if (ret
== 0 && source
)
1210 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, source
, false);
1215 static pa_alsa_jack
* ucm_get_jack(pa_alsa_ucm_config
*ucm
, const char *dev_name
, const char *pre_tag
) {
1217 char *name
= pa_sprintf_malloc("%s%s", pre_tag
, dev_name
);
1219 PA_LLIST_FOREACH(j
, ucm
->jacks
)
1220 if (pa_streq(j
->name
, name
))
1223 j
= pa_xnew0(pa_alsa_jack
, 1);
1224 j
->state_unplugged
= PA_AVAILABLE_NO
;
1225 j
->state_plugged
= PA_AVAILABLE_YES
;
1226 j
->name
= pa_xstrdup(name
);
1227 j
->alsa_name
= pa_sprintf_malloc("%s Jack", dev_name
);
1229 PA_LLIST_PREPEND(pa_alsa_jack
, ucm
->jacks
, j
);
1236 static int ucm_create_profile(
1237 pa_alsa_ucm_config
*ucm
,
1238 pa_alsa_profile_set
*ps
,
1239 pa_alsa_ucm_verb
*verb
,
1240 const char *verb_name
,
1241 const char *verb_desc
) {
1244 pa_alsa_ucm_device
*dev
;
1245 pa_alsa_ucm_modifier
*mod
;
1247 const char *name
, *sink
, *source
;
1252 if (pa_hashmap_get(ps
->profiles
, verb_name
)) {
1253 pa_log("Verb %s already exists", verb_name
);
1257 p
= pa_xnew0(pa_alsa_profile
, 1);
1258 p
->profile_set
= ps
;
1259 p
->name
= pa_xstrdup(verb_name
);
1260 p
->description
= pa_xstrdup(verb_desc
);
1262 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1263 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1265 p
->supported
= true;
1266 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
1268 /* TODO: get profile priority from ucm info or policy management */
1269 c
= verb_cmp
= pa_xstrdup(verb_name
);
1271 if (*c
== '_') *c
= ' ';
1275 for (i
= 0; verb_info
[i
].id
; i
++) {
1276 if (strcasecmp(verb_info
[i
].id
, verb_cmp
) == 0) {
1277 p
->priority
= verb_info
[i
].priority
;
1284 if (verb_info
[i
].id
== NULL
)
1287 PA_LLIST_FOREACH(dev
, verb
->devices
) {
1288 name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1290 sink
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1291 source
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1293 ucm_create_mapping(ucm
, ps
, p
, dev
, verb_name
, name
, sink
, source
);
1296 dev
->output_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_OUTPUT
);
1298 dev
->input_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_INPUT
);
1301 /* Now find modifiers that have their own PlaybackPCM and create
1302 * separate sinks for them. */
1303 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
1304 name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1306 sink
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1307 source
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1310 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, sink
, true);
1312 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, source
, false);
1315 pa_alsa_profile_dump(p
);
1320 static snd_pcm_t
* mapping_open_pcm(pa_alsa_ucm_config
*ucm
, pa_alsa_mapping
*m
, int mode
) {
1322 pa_sample_spec try_ss
= ucm
->core
->default_sample_spec
;
1323 pa_channel_map try_map
;
1324 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
1325 bool exact_channels
= m
->channel_map
.channels
> 0;
1327 if (exact_channels
) {
1328 try_map
= m
->channel_map
;
1329 try_ss
.channels
= try_map
.channels
;
1331 pa_channel_map_init_extend(&try_map
, try_ss
.channels
, PA_CHANNEL_MAP_ALSA
);
1334 pa_usec_to_bytes(ucm
->core
->default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
1335 pa_frame_size(&try_ss
);
1336 try_buffer_size
= ucm
->core
->default_n_fragments
* try_period_size
;
1338 pcm
= pa_alsa_open_by_device_string(m
->device_strings
[0], NULL
, &try_ss
,
1339 &try_map
, mode
, &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
, exact_channels
);
1341 if (pcm
&& !exact_channels
)
1342 m
->channel_map
= try_map
;
1347 static void profile_finalize_probing(pa_alsa_profile
*p
) {
1351 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1358 snd_pcm_close(m
->output_pcm
);
1359 m
->output_pcm
= NULL
;
1362 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1369 snd_pcm_close(m
->input_pcm
);
1370 m
->input_pcm
= NULL
;
1374 static void ucm_mapping_jack_probe(pa_alsa_mapping
*m
) {
1375 snd_pcm_t
*pcm_handle
;
1376 snd_mixer_t
*mixer_handle
;
1377 snd_hctl_t
*hctl_handle
;
1378 pa_alsa_ucm_mapping_context
*context
= &m
->ucm_context
;
1379 pa_alsa_ucm_device
*dev
;
1382 pcm_handle
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? m
->output_pcm
: m
->input_pcm
;
1383 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
1384 if (!mixer_handle
|| !hctl_handle
)
1387 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1389 jack
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? dev
->output_jack
: dev
->input_jack
;
1391 jack
->has_control
= pa_alsa_find_jack(hctl_handle
, jack
->alsa_name
) != NULL
;
1392 pa_log_info("UCM jack %s has_control=%d", jack
->name
, jack
->has_control
);
1395 snd_mixer_close(mixer_handle
);
1398 static void ucm_probe_profile_set(pa_alsa_ucm_config
*ucm
, pa_alsa_profile_set
*ps
) {
1404 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
1406 pa_log_info("Set ucm verb to %s", p
->name
);
1408 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", p
->name
)) < 0) {
1409 pa_log("Failed to set verb %s", p
->name
);
1410 p
->supported
= false;
1414 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1415 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1416 /* Skip jack probing on modifier PCMs since we expect this to
1417 * only be controlled on the main device/verb PCM. */
1421 m
->output_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_PLAYBACK
);
1422 if (!m
->output_pcm
) {
1423 p
->supported
= false;
1429 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1430 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1431 /* Skip jack probing on modifier PCMs since we expect this to
1432 * only be controlled on the main device/verb PCM. */
1436 m
->input_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_CAPTURE
);
1437 if (!m
->input_pcm
) {
1438 p
->supported
= false;
1444 if (!p
->supported
) {
1445 profile_finalize_probing(p
);
1449 pa_log_debug("Profile %s supported.", p
->name
);
1451 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
1452 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1453 ucm_mapping_jack_probe(m
);
1455 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
1456 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1457 ucm_mapping_jack_probe(m
);
1459 profile_finalize_probing(p
);
1462 /* restore ucm state */
1463 snd_use_case_set(ucm
->ucm_mgr
, "_verb", SND_USE_CASE_VERB_INACTIVE
);
1465 pa_alsa_profile_set_drop_unsupported(ps
);
1468 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1469 pa_alsa_ucm_verb
*verb
;
1470 pa_alsa_profile_set
*ps
;
1472 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
1473 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1474 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1475 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1477 /* create a profile for each verb */
1478 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
1479 const char *verb_name
;
1480 const char *verb_desc
;
1482 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1483 verb_desc
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1484 if (verb_name
== NULL
) {
1485 pa_log("Verb with no name");
1489 ucm_create_profile(ucm
, ps
, verb
, verb_name
, verb_desc
);
1492 ucm_probe_profile_set(ucm
, ps
);
1498 static void free_verb(pa_alsa_ucm_verb
*verb
) {
1499 pa_alsa_ucm_device
*di
, *dn
;
1500 pa_alsa_ucm_modifier
*mi
, *mn
;
1502 PA_LLIST_FOREACH_SAFE(di
, dn
, verb
->devices
) {
1503 PA_LLIST_REMOVE(pa_alsa_ucm_device
, verb
->devices
, di
);
1504 pa_proplist_free(di
->proplist
);
1505 if (di
->conflicting_devices
)
1506 pa_idxset_free(di
->conflicting_devices
, NULL
);
1507 if (di
->supported_devices
)
1508 pa_idxset_free(di
->supported_devices
, NULL
);
1512 PA_LLIST_FOREACH_SAFE(mi
, mn
, verb
->modifiers
) {
1513 PA_LLIST_REMOVE(pa_alsa_ucm_modifier
, verb
->modifiers
, mi
);
1514 pa_proplist_free(mi
->proplist
);
1515 if (mi
->n_suppdev
> 0)
1516 snd_use_case_free_list(mi
->supported_devices
, mi
->n_suppdev
);
1517 if (mi
->n_confdev
> 0)
1518 snd_use_case_free_list(mi
->conflicting_devices
, mi
->n_confdev
);
1519 pa_xfree(mi
->media_role
);
1522 pa_proplist_free(verb
->proplist
);
1526 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1527 pa_alsa_ucm_verb
*vi
, *vn
;
1528 pa_alsa_jack
*ji
, *jn
;
1530 PA_LLIST_FOREACH_SAFE(vi
, vn
, ucm
->verbs
) {
1531 PA_LLIST_REMOVE(pa_alsa_ucm_verb
, ucm
->verbs
, vi
);
1534 PA_LLIST_FOREACH_SAFE(ji
, jn
, ucm
->jacks
) {
1535 PA_LLIST_REMOVE(pa_alsa_jack
, ucm
->jacks
, ji
);
1536 pa_xfree(ji
->alsa_name
);
1541 snd_use_case_mgr_close(ucm
->ucm_mgr
);
1542 ucm
->ucm_mgr
= NULL
;
1546 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1547 pa_alsa_ucm_device
*dev
;
1548 pa_alsa_ucm_modifier
*mod
;
1551 if (context
->ucm_devices
) {
1552 /* clear ucm device pointer to mapping */
1553 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1554 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1555 dev
->playback_mapping
= NULL
;
1557 dev
->capture_mapping
= NULL
;
1560 pa_idxset_free(context
->ucm_devices
, NULL
);
1563 if (context
->ucm_modifiers
) {
1564 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
1565 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1566 mod
->playback_mapping
= NULL
;
1568 mod
->capture_mapping
= NULL
;
1571 pa_idxset_free(context
->ucm_modifiers
, NULL
);
1575 /* Enable the modifier when the first stream with matched role starts */
1576 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1577 pa_alsa_ucm_modifier
*mod
;
1579 if (!ucm
->active_verb
)
1582 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1583 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1584 if (mod
->enabled_counter
== 0) {
1585 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1587 pa_log_info("Enable ucm modifier %s", mod_name
);
1588 if (snd_use_case_set(ucm
->ucm_mgr
, "_enamod", mod_name
) < 0) {
1589 pa_log("Failed to enable ucm modifier %s", mod_name
);
1593 mod
->enabled_counter
++;
1599 /* Disable the modifier when the last stream with matched role ends */
1600 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1601 pa_alsa_ucm_modifier
*mod
;
1603 if (!ucm
->active_verb
)
1606 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1607 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1609 mod
->enabled_counter
--;
1610 if (mod
->enabled_counter
== 0) {
1611 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1613 pa_log_info("Disable ucm modifier %s", mod_name
);
1614 if (snd_use_case_set(ucm
->ucm_mgr
, "_dismod", mod_name
) < 0) {
1615 pa_log("Failed to disable ucm modifier %s", mod_name
);
1624 #else /* HAVE_ALSA_UCM */
1626 /* Dummy functions for systems without UCM support */
1628 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
) {
1629 pa_log_info("UCM not available.");
1633 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1637 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
1641 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
) {
1645 void pa_alsa_ucm_add_ports(
1647 pa_proplist
*proplist
,
1648 pa_alsa_ucm_mapping_context
*context
,
1653 void pa_alsa_ucm_add_ports_combination(
1655 pa_alsa_ucm_mapping_context
*context
,
1658 pa_card_profile
*cp
,
1662 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
1666 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1669 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1672 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1675 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {