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)
79 static struct ucm_items item
[] = {
80 {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK
},
81 {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE
},
82 {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME
},
83 {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH
},
84 {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY
},
85 {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
},
86 {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME
},
87 {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH
},
88 {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
},
89 {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
},
90 {"TQ", PA_ALSA_PROP_UCM_QOS
},
94 /* UCM verb info - this should eventually be part of policy manangement */
95 static struct ucm_info verb_info
[] = {
96 {SND_USE_CASE_VERB_INACTIVE
, 0},
97 {SND_USE_CASE_VERB_HIFI
, 8000},
98 {SND_USE_CASE_VERB_HIFI_LOW_POWER
, 7000},
99 {SND_USE_CASE_VERB_VOICE
, 6000},
100 {SND_USE_CASE_VERB_VOICE_LOW_POWER
, 5000},
101 {SND_USE_CASE_VERB_VOICECALL
, 4000},
102 {SND_USE_CASE_VERB_IP_VOICECALL
, 4000},
103 {SND_USE_CASE_VERB_ANALOG_RADIO
, 3000},
104 {SND_USE_CASE_VERB_DIGITAL_RADIO
, 3000},
108 /* UCM device info - should be overwritten by ucm property */
109 static struct ucm_info dev_info
[] = {
110 {SND_USE_CASE_DEV_SPEAKER
, 100},
111 {SND_USE_CASE_DEV_LINE
, 100},
112 {SND_USE_CASE_DEV_HEADPHONES
, 100},
113 {SND_USE_CASE_DEV_HEADSET
, 300},
114 {SND_USE_CASE_DEV_HANDSET
, 200},
115 {SND_USE_CASE_DEV_BLUETOOTH
, 400},
116 {SND_USE_CASE_DEV_EARPIECE
, 100},
117 {SND_USE_CASE_DEV_SPDIF
, 100},
118 {SND_USE_CASE_DEV_HDMI
, 100},
119 {SND_USE_CASE_DEV_NONE
, 100},
123 /* UCM profile properties - The verb data is store so it can be used to fill
124 * the new profiles properties */
125 static int ucm_get_property(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
, const char *verb_name
) {
130 for (i
= 0; item
[i
].id
; i
++) {
133 id
= pa_sprintf_malloc("=%s//%s", item
[i
].id
, verb_name
);
134 err
= snd_use_case_get(uc_mgr
, id
, &value
);
139 pa_log_debug("Got %s for verb %s: %s", item
[i
].id
, verb_name
, value
);
140 pa_proplist_sets(verb
->proplist
, item
[i
].property
, value
);
147 static int ucm_device_exists(pa_idxset
*idxset
, pa_alsa_ucm_device
*dev
) {
148 pa_alsa_ucm_device
*d
;
151 PA_IDXSET_FOREACH(d
, idxset
, idx
)
158 static void ucm_add_devices_to_idxset(
160 pa_alsa_ucm_device
*me
,
161 pa_alsa_ucm_device
*devices
,
162 const char **dev_names
,
165 pa_alsa_ucm_device
*d
;
167 PA_LLIST_FOREACH(d
, devices
) {
174 name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
176 for (i
= 0; i
< n
; i
++)
177 if (pa_streq(dev_names
[i
], name
))
178 pa_idxset_put(idxset
, d
, NULL
);
182 /* Create a property list for this ucm device */
183 static int ucm_get_device_property(
184 pa_alsa_ucm_device
*device
,
185 snd_use_case_mgr_t
*uc_mgr
,
186 pa_alsa_ucm_verb
*verb
,
187 const char *device_name
) {
190 const char **devices
;
195 int n_confdev
, n_suppdev
;
197 for (i
= 0; item
[i
].id
; i
++) {
198 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, device_name
);
199 err
= snd_use_case_get(uc_mgr
, id
, &value
);
204 pa_log_debug("Got %s for device %s: %s", item
[i
].id
, device_name
, value
);
205 pa_proplist_sets(device
->proplist
, item
[i
].property
, value
);
209 /* get direction and channels */
210 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
211 if (value
) { /* output */
213 if (pa_atou(value
, &ui
) == 0 && ui
< PA_CHANNELS_MAX
)
214 device
->playback_channels
= ui
;
216 pa_log("UCM playback channels %s for device %s out of range", value
, device_name
);
219 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_SINK
);
220 if (!value
) { /* take pcm from verb playback default */
221 value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_SINK
);
223 pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name
, value
);
224 pa_proplist_sets(device
->proplist
, PA_ALSA_PROP_UCM_SINK
, value
);
226 pa_log("UCM playback device %s fetch pcm failed", device_name
);
230 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
231 if (value
) { /* input */
233 if (pa_atou(value
, &ui
) == 0 && ui
< PA_CHANNELS_MAX
)
234 device
->capture_channels
= ui
;
236 pa_log("UCM capture channels %s for device %s out of range", value
, device_name
);
239 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
240 if (!value
) { /* take pcm from verb capture default */
241 value
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
243 pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name
, value
);
244 pa_proplist_sets(device
->proplist
, PA_ALSA_PROP_UCM_SOURCE
, value
);
246 pa_log("UCM capture device %s fetch pcm failed", device_name
);
250 if (device
->playback_channels
== 0 && device
->capture_channels
== 0) {
251 pa_log_warn("UCM file does not specify 'PlaybackChannels' or 'CaptureChannels'"
252 "for device %s, assuming stereo duplex.", device_name
);
253 device
->playback_channels
= 2;
254 device
->capture_channels
= 2;
257 /* get priority of device */
258 if (device
->playback_channels
) { /* sink device */
259 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY
);
261 /* get priority from ucm config */
262 if (pa_atou(value
, &ui
) == 0)
263 device
->playback_priority
= ui
;
265 pa_log_debug("UCM playback priority %s for device %s error", value
, device_name
);
269 if (device
->capture_channels
) { /* source device */
270 value
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY
);
272 /* get priority from ucm config */
273 if (pa_atou(value
, &ui
) == 0)
274 device
->capture_priority
= ui
;
276 pa_log_debug("UCM capture priority %s for device %s error", value
, device_name
);
280 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
) || PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
281 /* get priority from static table */
282 for (i
= 0; dev_info
[i
].id
; i
++) {
283 if (strcasecmp(dev_info
[i
].id
, device_name
) == 0) {
284 PA_UCM_DEVICE_PRIORITY_SET(device
, dev_info
[i
].priority
);
290 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device
)) {
291 /* fall through to default priority */
292 device
->playback_priority
= 100;
295 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device
)) {
296 /* fall through to default priority */
297 device
->capture_priority
= 100;
300 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name
);
301 n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
305 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name
);
307 device
->conflicting_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
308 ucm_add_devices_to_idxset(device
->conflicting_devices
, device
, verb
->devices
, devices
, n_confdev
);
309 snd_use_case_free_list(devices
, n_confdev
);
312 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name
);
313 n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &devices
);
317 pa_log_debug("No %s for device %s", "_supporteddevs", device_name
);
319 device
->supported_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
320 ucm_add_devices_to_idxset(device
->supported_devices
, device
, verb
->devices
, devices
, n_suppdev
);
321 snd_use_case_free_list(devices
, n_suppdev
);
327 /* Create a property list for this ucm modifier */
328 static int ucm_get_modifier_property(pa_alsa_ucm_modifier
*modifier
, snd_use_case_mgr_t
*uc_mgr
, const char *modifier_name
) {
333 for (i
= 0; item
[i
].id
; i
++) {
336 id
= pa_sprintf_malloc("=%s/%s", item
[i
].id
, modifier_name
);
337 err
= snd_use_case_get(uc_mgr
, id
, &value
);
342 pa_log_debug("Got %s for modifier %s: %s", item
[i
].id
, modifier_name
, value
);
343 pa_proplist_sets(modifier
->proplist
, item
[i
].property
, value
);
347 id
= pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name
);
348 modifier
->n_confdev
= snd_use_case_get_list(uc_mgr
, id
, &modifier
->conflicting_devices
);
350 if (modifier
->n_confdev
< 0)
351 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name
);
353 id
= pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name
);
354 modifier
->n_suppdev
= snd_use_case_get_list(uc_mgr
, id
, &modifier
->supported_devices
);
356 if (modifier
->n_suppdev
< 0)
357 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name
);
362 /* Create a list of devices for this verb */
363 static int ucm_get_devices(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
364 const char **dev_list
;
367 num_dev
= snd_use_case_get_list(uc_mgr
, "_devices", &dev_list
);
371 for (i
= 0; i
< num_dev
; i
+= 2) {
372 pa_alsa_ucm_device
*d
= pa_xnew0(pa_alsa_ucm_device
, 1);
374 d
->proplist
= pa_proplist_new();
375 pa_proplist_sets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(dev_list
[i
]));
376 pa_proplist_sets(d
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(dev_list
[i
+ 1]));
378 PA_LLIST_PREPEND(pa_alsa_ucm_device
, verb
->devices
, d
);
381 snd_use_case_free_list(dev_list
, num_dev
);
386 static int ucm_get_modifiers(pa_alsa_ucm_verb
*verb
, snd_use_case_mgr_t
*uc_mgr
) {
387 const char **mod_list
;
390 num_mod
= snd_use_case_get_list(uc_mgr
, "_modifiers", &mod_list
);
394 for (i
= 0; i
< num_mod
; i
+= 2) {
395 pa_alsa_ucm_modifier
*m
;
398 pa_log_warn("Got a modifier with a null name. Skipping.");
402 m
= pa_xnew0(pa_alsa_ucm_modifier
, 1);
403 m
->proplist
= pa_proplist_new();
405 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_NAME
, mod_list
[i
]);
406 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(mod_list
[i
+ 1]));
408 PA_LLIST_PREPEND(pa_alsa_ucm_modifier
, verb
->modifiers
, m
);
411 snd_use_case_free_list(mod_list
, num_mod
);
416 static void add_role_to_device(pa_alsa_ucm_device
*dev
, const char *dev_name
, const char *role_name
, const char *role
) {
417 const char *cur
= pa_proplist_gets(dev
->proplist
, role_name
);
420 pa_proplist_sets(dev
->proplist
, role_name
, role
);
421 else if (!pa_str_in_list_spaces(cur
, role
)) { /* does not exist */
422 char *value
= pa_sprintf_malloc("%s %s", cur
, role
);
424 pa_proplist_sets(dev
->proplist
, role_name
, value
);
428 pa_log_info("Add role %s to device %s(%s), result %s", role
, dev_name
, role_name
, pa_proplist_gets(dev
->proplist
,
432 static void add_media_role(const char *name
, pa_alsa_ucm_device
*list
, const char *role_name
, const char *role
, bool is_sink
) {
433 pa_alsa_ucm_device
*d
;
435 PA_LLIST_FOREACH(d
, list
) {
436 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
438 if (pa_streq(dev_name
, name
)) {
439 const char *sink
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_SINK
);
440 const char *source
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
443 add_role_to_device(d
, dev_name
, role_name
, role
);
444 else if (!is_sink
&& source
)
445 add_role_to_device(d
, dev_name
, role_name
, role
);
451 static char *modifier_name_to_role(const char *mod_name
, bool *is_sink
) {
452 char *sub
= NULL
, *tmp
;
456 if (pa_startswith(mod_name
, "Play")) {
458 sub
= pa_xstrdup(mod_name
+ 4);
459 } else if (pa_startswith(mod_name
, "Capture"))
460 sub
= pa_xstrdup(mod_name
+ 7);
464 pa_log_warn("Can't match media roles for modifer %s", mod_name
);
471 *tmp
= tolower(*tmp
);
477 static void ucm_set_media_roles(pa_alsa_ucm_modifier
*modifier
, pa_alsa_ucm_device
*list
, const char *mod_name
) {
479 bool is_sink
= FALSE
;
481 const char *role_name
;
483 sub
= modifier_name_to_role(mod_name
, &is_sink
);
487 modifier
->action_direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
488 modifier
->media_role
= sub
;
490 role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
491 for (i
= 0; i
< modifier
->n_suppdev
; i
++) {
492 /* if modifier has no specific pcm, we add role intent to its supported devices */
493 if (!pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_SINK
) &&
494 !pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_SOURCE
))
495 add_media_role(modifier
->supported_devices
[i
], list
, role_name
, sub
, is_sink
);
499 static void append_lost_relationship(pa_alsa_ucm_device
*dev
) {
501 pa_alsa_ucm_device
*d
;
503 if (dev
->conflicting_devices
) {
504 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
505 if (!d
->conflicting_devices
)
506 d
->conflicting_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
508 if (pa_idxset_put(d
->conflicting_devices
, dev
, NULL
) == 0)
509 pa_log_warn("Add lost conflicting device %s to %s",
510 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
),
511 pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
));
515 if (dev
->supported_devices
) {
516 PA_IDXSET_FOREACH(d
, dev
->supported_devices
, idx
) {
517 if (!d
->supported_devices
)
518 d
->supported_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
520 if (pa_idxset_put(d
->supported_devices
, dev
, NULL
) == 0)
521 pa_log_warn("Add lost supported device %s to %s",
522 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
),
523 pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
));
528 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config
*ucm
, int card_index
)
531 const char **verb_list
;
532 int num_verbs
, i
, err
= 0;
534 /* is UCM available for this card ? */
535 err
= snd_card_get_name(card_index
, &card_name
);
537 pa_log("Card can't get card_name from card_index %d", card_index
);
541 err
= snd_use_case_mgr_open(&ucm
->ucm_mgr
, card_name
);
543 pa_log_info("UCM not available for card %s", card_name
);
547 pa_log_info("UCM available for card %s", card_name
);
549 /* get a list of all UCM verbs (profiles) for this card */
550 num_verbs
= snd_use_case_verb_list(ucm
->ucm_mgr
, &verb_list
);
552 pa_log("UCM verb list not found for %s", card_name
);
556 /* get the properties of each UCM verb */
557 for (i
= 0; i
< num_verbs
; i
+= 2) {
558 pa_alsa_ucm_verb
*verb
;
560 /* Get devices and modifiers for each verb */
561 err
= pa_alsa_ucm_get_verb(ucm
->ucm_mgr
, verb_list
[i
], verb_list
[i
+1], &verb
);
563 pa_log("Failed to get the verb %s", verb_list
[i
]);
567 PA_LLIST_PREPEND(pa_alsa_ucm_verb
, ucm
->verbs
, verb
);
571 pa_log("No UCM verb is valid for %s", card_name
);
575 snd_use_case_free_list(verb_list
, num_verbs
);
579 snd_use_case_mgr_close(ucm
->ucm_mgr
);
590 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
) {
591 pa_alsa_ucm_device
*d
;
592 pa_alsa_ucm_modifier
*mod
;
593 pa_alsa_ucm_verb
*verb
;
597 pa_log_info("Set UCM verb to %s", verb_name
);
598 err
= snd_use_case_set(uc_mgr
, "_verb", verb_name
);
602 verb
= pa_xnew0(pa_alsa_ucm_verb
, 1);
603 verb
->proplist
= pa_proplist_new();
605 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
, pa_strnull(verb_name
));
606 pa_proplist_sets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
, pa_strna(verb_desc
));
608 err
= ucm_get_devices(verb
, uc_mgr
);
610 pa_log("No UCM devices for verb %s", verb_name
);
612 err
= ucm_get_modifiers(verb
, uc_mgr
);
614 pa_log("No UCM modifiers for verb %s", verb_name
);
616 /* Verb properties */
617 ucm_get_property(verb
, uc_mgr
, verb_name
);
619 PA_LLIST_FOREACH(d
, verb
->devices
) {
620 const char *dev_name
= pa_proplist_gets(d
->proplist
, PA_ALSA_PROP_UCM_NAME
);
622 /* Devices properties */
623 ucm_get_device_property(d
, uc_mgr
, verb
, dev_name
);
625 /* make conflicting or supported device mutual */
626 PA_LLIST_FOREACH(d
, verb
->devices
)
627 append_lost_relationship(d
);
629 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
630 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
632 /* Modifier properties */
633 ucm_get_modifier_property(mod
, uc_mgr
, mod_name
);
635 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
636 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name
, mod_name
);
637 ucm_set_media_roles(mod
, verb
->devices
, mod_name
);
644 static void ucm_add_port_combination(
646 pa_alsa_ucm_mapping_context
*context
,
648 pa_alsa_ucm_device
**pdevices
,
654 pa_device_port
*port
;
658 const char *dev_name
;
659 const char *direction
;
660 pa_alsa_ucm_device
*dev
;
663 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
665 name
= pa_sprintf_malloc("%s%s", is_sink
? PA_UCM_PRE_TAG_OUTPUT
: PA_UCM_PRE_TAG_INPUT
, dev_name
);
666 desc
= num
== 1 ? pa_xstrdup(pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
))
667 : pa_sprintf_malloc("Combination port for %s", dev_name
);
669 priority
= is_sink
? dev
->playback_priority
: dev
->capture_priority
;
671 for (i
= 1; i
< num
; i
++) {
675 dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
677 tmp
= pa_sprintf_malloc("%s+%s", name
, dev_name
);
681 tmp
= pa_sprintf_malloc("%s,%s", desc
, dev_name
);
685 /* FIXME: Is this true? */
686 priority
+= (is_sink
? dev
->playback_priority
: dev
->capture_priority
);
689 port
= pa_hashmap_get(ports
, name
);
691 pa_device_port_new_data port_data
;
693 pa_device_port_new_data_init(&port_data
);
694 pa_device_port_new_data_set_name(&port_data
, name
);
695 pa_device_port_new_data_set_description(&port_data
, desc
);
696 pa_device_port_new_data_set_direction(&port_data
, is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
);
698 port
= pa_device_port_new(core
, &port_data
, 0);
699 pa_device_port_new_data_done(&port_data
);
702 pa_hashmap_put(ports
, port
->name
, port
);
703 pa_log_debug("Add port %s: %s", port
->name
, port
->description
);
704 port
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
707 port
->priority
= priority
;
712 direction
= is_sink
? "output" : "input";
713 pa_log_debug("Port %s direction %s, priority %d", port
->name
, direction
, priority
);
716 pa_log_debug("Adding port %s to profile %s", port
->name
, cp
->name
);
717 pa_hashmap_put(port
->profiles
, cp
->name
, cp
);
721 pa_hashmap_put(hash
, port
->name
, port
);
722 pa_device_port_ref(port
);
726 static int ucm_port_contains(const char *port_name
, const char *dev_name
, bool is_sink
) {
729 const char *state
= NULL
;
732 if (!port_name
|| !dev_name
)
735 port_name
+= is_sink
? strlen(PA_UCM_PRE_TAG_OUTPUT
) : strlen(PA_UCM_PRE_TAG_INPUT
);
737 while ((r
= pa_split_in_place(port_name
, "+", &len
, &state
))) {
738 if (!strncmp(r
, dev_name
, len
)) {
747 static int ucm_check_conformance(
748 pa_alsa_ucm_mapping_context
*context
,
749 pa_alsa_ucm_device
**pdevices
,
751 pa_alsa_ucm_device
*dev
) {
754 pa_alsa_ucm_device
*d
;
759 pa_log_debug("Check device %s conformance with %d other devices",
760 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
), dev_num
);
762 pa_log_debug("First device in combination, number 1");
766 if (dev
->conflicting_devices
) { /* the device defines conflicting devices */
767 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
768 for (i
= 0; i
< dev_num
; i
++) {
769 if (pdevices
[i
] == d
) {
770 pa_log_debug("Conflicting device found");
775 } else if (dev
->supported_devices
) { /* the device defines supported devices */
776 for (i
= 0; i
< dev_num
; i
++) {
777 if (!ucm_device_exists(dev
->supported_devices
, pdevices
[i
])) {
778 pa_log_debug("Supported device not found");
782 } else { /* not support any other devices */
783 pa_log_debug("Not support any other devices");
787 pa_log_debug("Device added to combination, number %d", dev_num
+ 1);
791 static inline pa_alsa_ucm_device
*get_next_device(pa_idxset
*idxset
, uint32_t *idx
) {
792 pa_alsa_ucm_device
*dev
;
794 if (*idx
== PA_IDXSET_INVALID
)
795 dev
= pa_idxset_first(idxset
, idx
);
797 dev
= pa_idxset_next(idxset
, idx
);
802 static void ucm_add_ports_combination(
804 pa_alsa_ucm_mapping_context
*context
,
806 pa_alsa_ucm_device
**pdevices
,
813 pa_alsa_ucm_device
*dev
;
814 uint32_t idx
= map_index
;
816 if ((dev
= get_next_device(context
->ucm_devices
, &idx
)) == NULL
)
819 /* check if device at map_index can combine with existing devices combination */
820 if (ucm_check_conformance(context
, pdevices
, dev_num
, dev
)) {
821 /* add device at map_index to devices combination */
822 pdevices
[dev_num
] = dev
;
823 /* add current devices combination as a new port */
824 ucm_add_port_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, ports
, cp
, core
);
825 /* try more elements combination */
826 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, idx
, ports
, cp
, core
);
829 /* try other device with current elements number */
830 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
, idx
, ports
, cp
, core
);
833 static char* merge_roles(const char *cur
, const char *add
) {
835 const char *state
= NULL
;
838 return pa_xstrdup(cur
);
839 else if (cur
== NULL
)
840 return pa_xstrdup(add
);
842 ret
= pa_xstrdup(cur
);
844 while ((r
= pa_split_spaces(add
, &state
))) {
847 if (!pa_str_in_list_spaces(ret
, r
))
848 value
= pa_sprintf_malloc("%s %s", ret
, r
);
862 void pa_alsa_ucm_add_ports_combination(
864 pa_alsa_ucm_mapping_context
*context
,
870 pa_alsa_ucm_device
**pdevices
;
872 pa_assert(context
->ucm_devices
);
874 if (pa_idxset_size(context
->ucm_devices
) > 0) {
875 pdevices
= pa_xnew(pa_alsa_ucm_device
*, pa_idxset_size(context
->ucm_devices
));
876 ucm_add_ports_combination(p
, context
, is_sink
, pdevices
, 0, PA_IDXSET_INVALID
, ports
, cp
, core
);
881 void pa_alsa_ucm_add_ports(
883 pa_proplist
*proplist
,
884 pa_alsa_ucm_mapping_context
*context
,
890 const char *role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
891 pa_alsa_ucm_device
*dev
;
892 pa_alsa_ucm_modifier
*mod
;
898 /* add ports first */
899 pa_alsa_ucm_add_ports_combination(*p
, context
, is_sink
, card
->ports
, NULL
, card
->core
);
901 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
902 merged_roles
= pa_xstrdup(pa_proplist_gets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
));
903 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
904 const char *roles
= pa_proplist_gets(dev
->proplist
, role_name
);
905 tmp
= merge_roles(merged_roles
, roles
);
906 pa_xfree(merged_roles
);
910 if (context
->ucm_modifiers
)
911 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
912 tmp
= merge_roles(merged_roles
, mod
->media_role
);
913 pa_xfree(merged_roles
);
918 pa_proplist_sets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
, merged_roles
);
920 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist
, PA_PROP_DEVICE_STRING
), pa_strnull(merged_roles
));
921 pa_xfree(merged_roles
);
924 /* Change UCM verb and device to match selected card profile */
925 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
928 pa_alsa_ucm_verb
*verb
;
930 if (new_profile
== old_profile
)
932 else if (new_profile
== NULL
|| old_profile
== NULL
)
933 profile
= new_profile
? new_profile
: SND_USE_CASE_VERB_INACTIVE
;
934 else if (!pa_streq(new_profile
, old_profile
))
935 profile
= new_profile
;
940 pa_log_info("Set UCM verb to %s", profile
);
941 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", profile
)) < 0) {
942 pa_log("Failed to set verb %s", profile
);
946 /* find active verb */
947 ucm
->active_verb
= NULL
;
948 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
949 const char *verb_name
;
950 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
951 if (pa_streq(verb_name
, profile
)) {
952 ucm
->active_verb
= verb
;
960 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
963 pa_alsa_ucm_config
*ucm
;
964 const char **enable_devs
;
967 pa_alsa_ucm_device
*dev
;
969 pa_assert(context
&& context
->ucm
);
972 pa_assert(ucm
->ucm_mgr
);
974 enable_devs
= pa_xnew(const char *, pa_idxset_size(context
->ucm_devices
));
976 /* first disable then enable */
977 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
978 const char *dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
980 if (ucm_port_contains(port
->name
, dev_name
, is_sink
))
981 enable_devs
[enable_num
++] = dev_name
;
983 pa_log_debug("Disable ucm device %s", dev_name
);
984 if (snd_use_case_set(ucm
->ucm_mgr
, "_disdev", dev_name
) > 0) {
985 pa_log("Failed to disable ucm device %s", dev_name
);
992 for (i
= 0; i
< enable_num
; i
++) {
993 pa_log_debug("Enable ucm device %s", enable_devs
[i
]);
994 if (snd_use_case_set(ucm
->ucm_mgr
, "_enadev", enable_devs
[i
]) < 0) {
995 pa_log("Failed to enable ucm device %s", enable_devs
[i
]);
1001 pa_xfree(enable_devs
);
1006 static void ucm_add_mapping(pa_alsa_profile
*p
, pa_alsa_mapping
*m
) {
1008 switch (m
->direction
) {
1009 case PA_ALSA_DIRECTION_ANY
:
1010 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1011 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1013 case PA_ALSA_DIRECTION_OUTPUT
:
1014 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1016 case PA_ALSA_DIRECTION_INPUT
:
1017 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1022 static void alsa_mapping_add_ucm_device(pa_alsa_mapping
*m
, pa_alsa_ucm_device
*device
) {
1024 const char *new_desc
;
1026 pa_idxset_put(m
->ucm_context
.ucm_devices
, device
, NULL
);
1028 new_desc
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1029 cur_desc
= m
->description
;
1031 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1033 m
->description
= pa_xstrdup(new_desc
);
1036 /* walk around null case */
1037 m
->description
= m
->description
? m
->description
: pa_xstrdup("");
1039 /* save mapping to ucm device */
1040 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1041 device
->playback_mapping
= m
;
1043 device
->capture_mapping
= m
;
1046 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping
*m
, pa_alsa_ucm_modifier
*modifier
) {
1048 const char *new_desc
, *mod_name
, *channel_str
;
1049 uint32_t channels
= 0;
1051 pa_idxset_put(m
->ucm_context
.ucm_modifiers
, modifier
, NULL
);
1053 new_desc
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1054 cur_desc
= m
->description
;
1056 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1058 m
->description
= pa_xstrdup(new_desc
);
1061 if (!m
->description
)
1064 /* Modifier sinks should not be routed to by default */
1067 mod_name
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1068 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_MODIFIER
, mod_name
);
1070 /* save mapping to ucm modifier */
1071 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1072 modifier
->playback_mapping
= m
;
1073 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
1075 modifier
->capture_mapping
= m
;
1076 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
1080 pa_assert_se(pa_atou(channel_str
, &channels
) == 0 && channels
< PA_CHANNELS_MAX
);
1081 pa_log_debug("Got channel count %" PRIu32
" for modifier", channels
);
1085 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1087 pa_channel_map_init(&m
->channel_map
);
1090 static int ucm_create_mapping_direction(
1091 pa_alsa_ucm_config
*ucm
,
1092 pa_alsa_profile_set
*ps
,
1094 pa_alsa_ucm_device
*device
,
1095 const char *verb_name
,
1096 const char *device_name
,
1097 const char *device_str
,
1102 unsigned priority
, channels
;
1104 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1106 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1108 pa_log("No mapping for %s", mapping_name
);
1109 pa_xfree(mapping_name
);
1112 pa_log_debug("UCM mapping: %s dev %s", mapping_name
, device_name
);
1113 pa_xfree(mapping_name
);
1115 priority
= is_sink
? device
->playback_priority
: device
->capture_priority
;
1116 channels
= is_sink
? device
->playback_channels
: device
->capture_channels
;
1118 if (!m
->ucm_context
.ucm_devices
) { /* new mapping */
1119 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1120 m
->ucm_context
.ucm
= ucm
;
1121 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1123 m
->device_strings
= pa_xnew0(char*, 2);
1124 m
->device_strings
[0] = pa_xstrdup(device_str
);
1125 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1127 ucm_add_mapping(p
, m
);
1128 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1131 /* mapping priority is the highest one of ucm devices */
1132 if (priority
> m
->priority
)
1133 m
->priority
= priority
;
1135 /* mapping channels is the lowest one of ucm devices */
1136 if (channels
< m
->channel_map
.channels
)
1137 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1139 alsa_mapping_add_ucm_device(m
, device
);
1144 static int ucm_create_mapping_for_modifier(
1145 pa_alsa_ucm_config
*ucm
,
1146 pa_alsa_profile_set
*ps
,
1148 pa_alsa_ucm_modifier
*modifier
,
1149 const char *verb_name
,
1150 const char *mod_name
,
1151 const char *device_str
,
1157 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1159 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1161 pa_log("no mapping for %s", mapping_name
);
1162 pa_xfree(mapping_name
);
1165 pa_log_info("ucm mapping: %s modifier %s", mapping_name
, mod_name
);
1166 pa_xfree(mapping_name
);
1168 if (!m
->ucm_context
.ucm_devices
&& !m
->ucm_context
.ucm_modifiers
) { /* new mapping */
1169 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1170 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1171 m
->ucm_context
.ucm
= ucm
;
1172 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1174 m
->device_strings
= pa_xnew0(char*, 2);
1175 m
->device_strings
[0] = pa_xstrdup(device_str
);
1176 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1177 /* Modifier sinks should not be routed to by default */
1180 ucm_add_mapping(p
, m
);
1181 } else if (!m
->ucm_context
.ucm_modifiers
) /* share pcm with device */
1182 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1184 alsa_mapping_add_ucm_modifier(m
, modifier
);
1189 static int ucm_create_mapping(
1190 pa_alsa_ucm_config
*ucm
,
1191 pa_alsa_profile_set
*ps
,
1193 pa_alsa_ucm_device
*device
,
1194 const char *verb_name
,
1195 const char *device_name
,
1197 const char *source
) {
1201 if (!sink
&& !source
) {
1202 pa_log("No sink and source at %s: %s", verb_name
, device_name
);
1207 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, sink
, TRUE
);
1208 if (ret
== 0 && source
)
1209 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, source
, FALSE
);
1214 static pa_alsa_jack
* ucm_get_jack(pa_alsa_ucm_config
*ucm
, const char *dev_name
, const char *pre_tag
) {
1216 char *name
= pa_sprintf_malloc("%s%s", pre_tag
, dev_name
);
1218 PA_LLIST_FOREACH(j
, ucm
->jacks
)
1219 if (pa_streq(j
->name
, name
))
1222 j
= pa_xnew0(pa_alsa_jack
, 1);
1223 j
->state_unplugged
= PA_AVAILABLE_NO
;
1224 j
->state_plugged
= PA_AVAILABLE_YES
;
1225 j
->name
= pa_xstrdup(name
);
1226 j
->alsa_name
= pa_sprintf_malloc("%s Jack", dev_name
);
1228 PA_LLIST_PREPEND(pa_alsa_jack
, ucm
->jacks
, j
);
1235 static int ucm_create_profile(
1236 pa_alsa_ucm_config
*ucm
,
1237 pa_alsa_profile_set
*ps
,
1238 pa_alsa_ucm_verb
*verb
,
1239 const char *verb_name
,
1240 const char *verb_desc
) {
1243 pa_alsa_ucm_device
*dev
;
1244 pa_alsa_ucm_modifier
*mod
;
1246 const char *name
, *sink
, *source
;
1251 if (pa_hashmap_get(ps
->profiles
, verb_name
)) {
1252 pa_log("Verb %s already exists", verb_name
);
1256 p
= pa_xnew0(pa_alsa_profile
, 1);
1257 p
->profile_set
= ps
;
1258 p
->name
= pa_xstrdup(verb_name
);
1259 p
->description
= pa_xstrdup(verb_desc
);
1261 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1262 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1264 p
->supported
= TRUE
;
1265 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
1267 /* TODO: get profile priority from ucm info or policy management */
1268 c
= verb_cmp
= pa_xstrdup(verb_name
);
1270 if (*c
== '_') *c
= ' ';
1274 for (i
= 0; verb_info
[i
].id
; i
++) {
1275 if (strcasecmp(verb_info
[i
].id
, verb_cmp
) == 0) {
1276 p
->priority
= verb_info
[i
].priority
;
1283 if (verb_info
[i
].id
== NULL
)
1286 PA_LLIST_FOREACH(dev
, verb
->devices
) {
1287 name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1289 sink
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1290 source
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1292 ucm_create_mapping(ucm
, ps
, p
, dev
, verb_name
, name
, sink
, source
);
1295 dev
->output_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_OUTPUT
);
1297 dev
->input_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_INPUT
);
1300 /* Now find modifiers that have their own PlaybackPCM and create
1301 * separate sinks for them. */
1302 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
1303 name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1305 sink
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1306 source
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1309 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, sink
, TRUE
);
1311 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, source
, FALSE
);
1314 pa_alsa_profile_dump(p
);
1319 static snd_pcm_t
* mapping_open_pcm(pa_alsa_ucm_config
*ucm
, pa_alsa_mapping
*m
, int mode
) {
1321 pa_sample_spec try_ss
= ucm
->core
->default_sample_spec
;
1322 pa_channel_map try_map
;
1323 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
1324 bool exact_channels
= m
->channel_map
.channels
> 0;
1326 if (exact_channels
) {
1327 try_map
= m
->channel_map
;
1328 try_ss
.channels
= try_map
.channels
;
1330 pa_channel_map_init_extend(&try_map
, try_ss
.channels
, PA_CHANNEL_MAP_ALSA
);
1333 pa_usec_to_bytes(ucm
->core
->default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
1334 pa_frame_size(&try_ss
);
1335 try_buffer_size
= ucm
->core
->default_n_fragments
* try_period_size
;
1337 pcm
= pa_alsa_open_by_device_string(m
->device_strings
[0], NULL
, &try_ss
,
1338 &try_map
, mode
, &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
, exact_channels
);
1340 if (pcm
&& !exact_channels
)
1341 m
->channel_map
= try_map
;
1346 static void profile_finalize_probing(pa_alsa_profile
*p
) {
1350 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1357 snd_pcm_close(m
->output_pcm
);
1358 m
->output_pcm
= NULL
;
1361 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1368 snd_pcm_close(m
->input_pcm
);
1369 m
->input_pcm
= NULL
;
1373 static void ucm_mapping_jack_probe(pa_alsa_mapping
*m
) {
1374 snd_pcm_t
*pcm_handle
;
1375 snd_mixer_t
*mixer_handle
;
1376 snd_hctl_t
*hctl_handle
;
1377 pa_alsa_ucm_mapping_context
*context
= &m
->ucm_context
;
1378 pa_alsa_ucm_device
*dev
;
1381 pcm_handle
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? m
->output_pcm
: m
->input_pcm
;
1382 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
1383 if (!mixer_handle
|| !hctl_handle
)
1386 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1388 jack
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? dev
->output_jack
: dev
->input_jack
;
1390 jack
->has_control
= pa_alsa_find_jack(hctl_handle
, jack
->alsa_name
) != NULL
;
1391 pa_log_info("UCM jack %s has_control=%d", jack
->name
, jack
->has_control
);
1394 snd_mixer_close(mixer_handle
);
1397 static void ucm_probe_profile_set(pa_alsa_ucm_config
*ucm
, pa_alsa_profile_set
*ps
) {
1403 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
1405 pa_log_info("Set ucm verb to %s", p
->name
);
1407 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", p
->name
)) < 0) {
1408 pa_log("Failed to set verb %s", p
->name
);
1409 p
->supported
= FALSE
;
1413 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1414 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1415 /* Skip jack probing on modifier PCMs since we expect this to
1416 * only be controlled on the main device/verb PCM. */
1420 m
->output_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_PLAYBACK
);
1421 if (!m
->output_pcm
) {
1422 p
->supported
= FALSE
;
1428 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1429 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1430 /* Skip jack probing on modifier PCMs since we expect this to
1431 * only be controlled on the main device/verb PCM. */
1435 m
->input_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_CAPTURE
);
1436 if (!m
->input_pcm
) {
1437 p
->supported
= FALSE
;
1443 if (!p
->supported
) {
1444 profile_finalize_probing(p
);
1448 pa_log_debug("Profile %s supported.", p
->name
);
1450 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
1451 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1452 ucm_mapping_jack_probe(m
);
1454 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
1455 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1456 ucm_mapping_jack_probe(m
);
1458 profile_finalize_probing(p
);
1461 /* restore ucm state */
1462 snd_use_case_set(ucm
->ucm_mgr
, "_verb", SND_USE_CASE_VERB_INACTIVE
);
1464 pa_alsa_profile_set_drop_unsupported(ps
);
1467 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1468 pa_alsa_ucm_verb
*verb
;
1469 pa_alsa_profile_set
*ps
;
1471 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
1472 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1473 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1474 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1476 /* create a profile for each verb */
1477 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
1478 const char *verb_name
;
1479 const char *verb_desc
;
1481 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1482 verb_desc
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1483 if (verb_name
== NULL
) {
1484 pa_log("Verb with no name");
1488 ucm_create_profile(ucm
, ps
, verb
, verb_name
, verb_desc
);
1491 ucm_probe_profile_set(ucm
, ps
);
1497 static void free_verb(pa_alsa_ucm_verb
*verb
) {
1498 pa_alsa_ucm_device
*di
, *dn
;
1499 pa_alsa_ucm_modifier
*mi
, *mn
;
1501 PA_LLIST_FOREACH_SAFE(di
, dn
, verb
->devices
) {
1502 PA_LLIST_REMOVE(pa_alsa_ucm_device
, verb
->devices
, di
);
1503 pa_proplist_free(di
->proplist
);
1504 if (di
->conflicting_devices
)
1505 pa_idxset_free(di
->conflicting_devices
, NULL
);
1506 if (di
->supported_devices
)
1507 pa_idxset_free(di
->supported_devices
, NULL
);
1511 PA_LLIST_FOREACH_SAFE(mi
, mn
, verb
->modifiers
) {
1512 PA_LLIST_REMOVE(pa_alsa_ucm_modifier
, verb
->modifiers
, mi
);
1513 pa_proplist_free(mi
->proplist
);
1514 if (mi
->n_suppdev
> 0)
1515 snd_use_case_free_list(mi
->supported_devices
, mi
->n_suppdev
);
1516 if (mi
->n_confdev
> 0)
1517 snd_use_case_free_list(mi
->conflicting_devices
, mi
->n_confdev
);
1518 pa_xfree(mi
->media_role
);
1521 pa_proplist_free(verb
->proplist
);
1525 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1526 pa_alsa_ucm_verb
*vi
, *vn
;
1527 pa_alsa_jack
*ji
, *jn
;
1529 PA_LLIST_FOREACH_SAFE(vi
, vn
, ucm
->verbs
) {
1530 PA_LLIST_REMOVE(pa_alsa_ucm_verb
, ucm
->verbs
, vi
);
1533 PA_LLIST_FOREACH_SAFE(ji
, jn
, ucm
->jacks
) {
1534 PA_LLIST_REMOVE(pa_alsa_jack
, ucm
->jacks
, ji
);
1535 pa_xfree(ji
->alsa_name
);
1540 snd_use_case_mgr_close(ucm
->ucm_mgr
);
1541 ucm
->ucm_mgr
= NULL
;
1545 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1546 pa_alsa_ucm_device
*dev
;
1547 pa_alsa_ucm_modifier
*mod
;
1550 if (context
->ucm_devices
) {
1551 /* clear ucm device pointer to mapping */
1552 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1553 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1554 dev
->playback_mapping
= NULL
;
1556 dev
->capture_mapping
= NULL
;
1559 pa_idxset_free(context
->ucm_devices
, NULL
);
1562 if (context
->ucm_modifiers
) {
1563 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
1564 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1565 mod
->playback_mapping
= NULL
;
1567 mod
->capture_mapping
= NULL
;
1570 pa_idxset_free(context
->ucm_modifiers
, NULL
);
1574 /* Enable the modifier when the first stream with matched role starts */
1575 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1576 pa_alsa_ucm_modifier
*mod
;
1578 if (!ucm
->active_verb
)
1581 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1582 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1583 if (mod
->enabled_counter
== 0) {
1584 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1586 pa_log_info("Enable ucm modifier %s", mod_name
);
1587 if (snd_use_case_set(ucm
->ucm_mgr
, "_enamod", mod_name
) < 0) {
1588 pa_log("Failed to enable ucm modifier %s", mod_name
);
1592 mod
->enabled_counter
++;
1598 /* Disable the modifier when the last stream with matched role ends */
1599 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1600 pa_alsa_ucm_modifier
*mod
;
1602 if (!ucm
->active_verb
)
1605 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1606 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1608 mod
->enabled_counter
--;
1609 if (mod
->enabled_counter
== 0) {
1610 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1612 pa_log_info("Disable ucm modifier %s", mod_name
);
1613 if (snd_use_case_set(ucm
->ucm_mgr
, "_dismod", mod_name
) < 0) {
1614 pa_log("Failed to disable ucm modifier %s", mod_name
);