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 port
= pa_device_port_new(core
, pa_strna(name
), desc
, 0);
694 pa_hashmap_put(ports
, port
->name
, port
);
695 pa_log_debug("Add port %s: %s", port
->name
, port
->description
);
696 port
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
699 port
->priority
= priority
;
701 port
->is_output
= TRUE
;
703 port
->is_input
= TRUE
;
708 direction
= is_sink
? "output" : "input";
709 pa_log_debug("Port %s direction %s, priority %d", port
->name
, direction
, priority
);
712 pa_log_debug("Adding port %s to profile %s", port
->name
, cp
->name
);
713 pa_hashmap_put(port
->profiles
, cp
->name
, cp
);
717 pa_hashmap_put(hash
, port
->name
, port
);
718 pa_device_port_ref(port
);
722 static int ucm_port_contains(const char *port_name
, const char *dev_name
, bool is_sink
) {
725 const char *state
= NULL
;
728 if (!port_name
|| !dev_name
)
731 port_name
+= is_sink
? strlen(PA_UCM_PRE_TAG_OUTPUT
) : strlen(PA_UCM_PRE_TAG_INPUT
);
733 while ((r
= pa_split_in_place(port_name
, "+", &len
, &state
))) {
734 if (!strncmp(r
, dev_name
, len
)) {
743 static int ucm_check_conformance(
744 pa_alsa_ucm_mapping_context
*context
,
745 pa_alsa_ucm_device
**pdevices
,
747 pa_alsa_ucm_device
*dev
) {
750 pa_alsa_ucm_device
*d
;
755 pa_log_debug("Check device %s conformance with %d other devices",
756 pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
), dev_num
);
758 pa_log_debug("First device in combination, number 1");
762 if (dev
->conflicting_devices
) { /* the device defines conflicting devices */
763 PA_IDXSET_FOREACH(d
, dev
->conflicting_devices
, idx
) {
764 for (i
= 0; i
< dev_num
; i
++) {
765 if (pdevices
[i
] == d
) {
766 pa_log_debug("Conflicting device found");
771 } else if (dev
->supported_devices
) { /* the device defines supported devices */
772 for (i
= 0; i
< dev_num
; i
++) {
773 if (!ucm_device_exists(dev
->supported_devices
, pdevices
[i
])) {
774 pa_log_debug("Supported device not found");
778 } else { /* not support any other devices */
779 pa_log_debug("Not support any other devices");
783 pa_log_debug("Device added to combination, number %d", dev_num
+ 1);
787 static inline pa_alsa_ucm_device
*get_next_device(pa_idxset
*idxset
, uint32_t *idx
) {
788 pa_alsa_ucm_device
*dev
;
790 if (*idx
== PA_IDXSET_INVALID
)
791 dev
= pa_idxset_first(idxset
, idx
);
793 dev
= pa_idxset_next(idxset
, idx
);
798 static void ucm_add_ports_combination(
800 pa_alsa_ucm_mapping_context
*context
,
802 pa_alsa_ucm_device
**pdevices
,
809 pa_alsa_ucm_device
*dev
;
810 uint32_t idx
= map_index
;
812 if ((dev
= get_next_device(context
->ucm_devices
, &idx
)) == NULL
)
815 /* check if device at map_index can combine with existing devices combination */
816 if (ucm_check_conformance(context
, pdevices
, dev_num
, dev
)) {
817 /* add device at map_index to devices combination */
818 pdevices
[dev_num
] = dev
;
819 /* add current devices combination as a new port */
820 ucm_add_port_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, ports
, cp
, core
);
821 /* try more elements combination */
822 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
+ 1, idx
, ports
, cp
, core
);
825 /* try other device with current elements number */
826 ucm_add_ports_combination(hash
, context
, is_sink
, pdevices
, dev_num
, idx
, ports
, cp
, core
);
829 static char* merge_roles(const char *cur
, const char *add
) {
831 const char *state
= NULL
;
834 return pa_xstrdup(cur
);
835 else if (cur
== NULL
)
836 return pa_xstrdup(add
);
838 ret
= pa_xstrdup(cur
);
840 while ((r
= pa_split_spaces(add
, &state
))) {
843 if (!pa_str_in_list_spaces(ret
, r
))
844 value
= pa_sprintf_malloc("%s %s", ret
, r
);
858 void pa_alsa_ucm_add_ports_combination(
860 pa_alsa_ucm_mapping_context
*context
,
866 pa_alsa_ucm_device
**pdevices
;
868 pa_assert(context
->ucm_devices
);
870 if (pa_idxset_size(context
->ucm_devices
) > 0) {
871 pdevices
= pa_xnew(pa_alsa_ucm_device
*, pa_idxset_size(context
->ucm_devices
));
872 ucm_add_ports_combination(p
, context
, is_sink
, pdevices
, 0, PA_IDXSET_INVALID
, ports
, cp
, core
);
877 void pa_alsa_ucm_add_ports(
879 pa_proplist
*proplist
,
880 pa_alsa_ucm_mapping_context
*context
,
886 const char *role_name
= is_sink
? PA_ALSA_PROP_UCM_PLAYBACK_ROLES
: PA_ALSA_PROP_UCM_CAPTURE_ROLES
;
887 pa_alsa_ucm_device
*dev
;
888 pa_alsa_ucm_modifier
*mod
;
894 /* add ports first */
895 pa_alsa_ucm_add_ports_combination(*p
, context
, is_sink
, card
->ports
, NULL
, card
->core
);
897 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
898 merged_roles
= pa_xstrdup(pa_proplist_gets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
));
899 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
900 const char *roles
= pa_proplist_gets(dev
->proplist
, role_name
);
901 tmp
= merge_roles(merged_roles
, roles
);
902 pa_xfree(merged_roles
);
906 if (context
->ucm_modifiers
)
907 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
908 tmp
= merge_roles(merged_roles
, mod
->media_role
);
909 pa_xfree(merged_roles
);
914 pa_proplist_sets(proplist
, PA_PROP_DEVICE_INTENDED_ROLES
, merged_roles
);
916 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist
, PA_PROP_DEVICE_STRING
), pa_strnull(merged_roles
));
917 pa_xfree(merged_roles
);
920 /* Change UCM verb and device to match selected card profile */
921 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config
*ucm
, const char *new_profile
, const char *old_profile
) {
924 pa_alsa_ucm_verb
*verb
;
926 if (new_profile
== old_profile
)
928 else if (new_profile
== NULL
|| old_profile
== NULL
)
929 profile
= new_profile
? new_profile
: SND_USE_CASE_VERB_INACTIVE
;
930 else if (!pa_streq(new_profile
, old_profile
))
931 profile
= new_profile
;
936 pa_log_info("Set UCM verb to %s", profile
);
937 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", profile
)) < 0) {
938 pa_log("Failed to set verb %s", profile
);
942 /* find active verb */
943 ucm
->active_verb
= NULL
;
944 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
945 const char *verb_name
;
946 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
947 if (pa_streq(verb_name
, profile
)) {
948 ucm
->active_verb
= verb
;
956 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context
*context
, pa_device_port
*port
, bool is_sink
) {
959 pa_alsa_ucm_config
*ucm
;
960 const char **enable_devs
;
963 pa_alsa_ucm_device
*dev
;
965 pa_assert(context
&& context
->ucm
);
968 pa_assert(ucm
->ucm_mgr
);
970 enable_devs
= pa_xnew(const char *, pa_idxset_size(context
->ucm_devices
));
972 /* first disable then enable */
973 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
974 const char *dev_name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
976 if (ucm_port_contains(port
->name
, dev_name
, is_sink
))
977 enable_devs
[enable_num
++] = dev_name
;
979 pa_log_debug("Disable ucm device %s", dev_name
);
980 if (snd_use_case_set(ucm
->ucm_mgr
, "_disdev", dev_name
) > 0) {
981 pa_log("Failed to disable ucm device %s", dev_name
);
988 for (i
= 0; i
< enable_num
; i
++) {
989 pa_log_debug("Enable ucm device %s", enable_devs
[i
]);
990 if (snd_use_case_set(ucm
->ucm_mgr
, "_enadev", enable_devs
[i
]) < 0) {
991 pa_log("Failed to enable ucm device %s", enable_devs
[i
]);
997 pa_xfree(enable_devs
);
1002 static void ucm_add_mapping(pa_alsa_profile
*p
, pa_alsa_mapping
*m
) {
1004 switch (m
->direction
) {
1005 case PA_ALSA_DIRECTION_ANY
:
1006 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1007 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1009 case PA_ALSA_DIRECTION_OUTPUT
:
1010 pa_idxset_put(p
->output_mappings
, m
, NULL
);
1012 case PA_ALSA_DIRECTION_INPUT
:
1013 pa_idxset_put(p
->input_mappings
, m
, NULL
);
1018 static void alsa_mapping_add_ucm_device(pa_alsa_mapping
*m
, pa_alsa_ucm_device
*device
) {
1020 const char *new_desc
;
1022 pa_idxset_put(m
->ucm_context
.ucm_devices
, device
, NULL
);
1024 new_desc
= pa_proplist_gets(device
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1025 cur_desc
= m
->description
;
1027 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1029 m
->description
= pa_xstrdup(new_desc
);
1032 /* walk around null case */
1033 m
->description
= m
->description
? m
->description
: pa_xstrdup("");
1035 /* save mapping to ucm device */
1036 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1037 device
->playback_mapping
= m
;
1039 device
->capture_mapping
= m
;
1042 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping
*m
, pa_alsa_ucm_modifier
*modifier
) {
1044 const char *new_desc
, *mod_name
, *channel_str
;
1045 uint32_t channels
= 0;
1047 pa_idxset_put(m
->ucm_context
.ucm_modifiers
, modifier
, NULL
);
1049 new_desc
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1050 cur_desc
= m
->description
;
1052 m
->description
= pa_sprintf_malloc("%s + %s", cur_desc
, new_desc
);
1054 m
->description
= pa_xstrdup(new_desc
);
1057 if (!m
->description
)
1060 /* Modifier sinks should not be routed to by default */
1063 mod_name
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1064 pa_proplist_sets(m
->proplist
, PA_ALSA_PROP_UCM_MODIFIER
, mod_name
);
1066 /* save mapping to ucm modifier */
1067 if (m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1068 modifier
->playback_mapping
= m
;
1069 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS
);
1071 modifier
->capture_mapping
= m
;
1072 channel_str
= pa_proplist_gets(modifier
->proplist
, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS
);
1076 pa_assert_se(pa_atou(channel_str
, &channels
) == 0 && channels
< PA_CHANNELS_MAX
);
1077 pa_log_debug("Got channel count %" PRIu32
" for modifier", channels
);
1081 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1083 pa_channel_map_init(&m
->channel_map
);
1086 static int ucm_create_mapping_direction(
1087 pa_alsa_ucm_config
*ucm
,
1088 pa_alsa_profile_set
*ps
,
1090 pa_alsa_ucm_device
*device
,
1091 const char *verb_name
,
1092 const char *device_name
,
1093 const char *device_str
,
1098 unsigned priority
, channels
;
1100 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1102 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1104 pa_log("No mapping for %s", mapping_name
);
1105 pa_xfree(mapping_name
);
1108 pa_log_debug("UCM mapping: %s dev %s", mapping_name
, device_name
);
1109 pa_xfree(mapping_name
);
1111 priority
= is_sink
? device
->playback_priority
: device
->capture_priority
;
1112 channels
= is_sink
? device
->playback_channels
: device
->capture_channels
;
1114 if (!m
->ucm_context
.ucm_devices
) { /* new mapping */
1115 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1116 m
->ucm_context
.ucm
= ucm
;
1117 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1119 m
->device_strings
= pa_xnew0(char*, 2);
1120 m
->device_strings
[0] = pa_xstrdup(device_str
);
1121 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1123 ucm_add_mapping(p
, m
);
1124 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1127 /* mapping priority is the highest one of ucm devices */
1128 if (priority
> m
->priority
)
1129 m
->priority
= priority
;
1131 /* mapping channels is the lowest one of ucm devices */
1132 if (channels
< m
->channel_map
.channels
)
1133 pa_channel_map_init_extend(&m
->channel_map
, channels
, PA_CHANNEL_MAP_ALSA
);
1135 alsa_mapping_add_ucm_device(m
, device
);
1140 static int ucm_create_mapping_for_modifier(
1141 pa_alsa_ucm_config
*ucm
,
1142 pa_alsa_profile_set
*ps
,
1144 pa_alsa_ucm_modifier
*modifier
,
1145 const char *verb_name
,
1146 const char *mod_name
,
1147 const char *device_str
,
1153 mapping_name
= pa_sprintf_malloc("Mapping %s: %s: %s", verb_name
, device_str
, is_sink
? "sink" : "source");
1155 m
= pa_alsa_mapping_get(ps
, mapping_name
);
1157 pa_log("no mapping for %s", mapping_name
);
1158 pa_xfree(mapping_name
);
1161 pa_log_info("ucm mapping: %s modifier %s", mapping_name
, mod_name
);
1162 pa_xfree(mapping_name
);
1164 if (!m
->ucm_context
.ucm_devices
&& !m
->ucm_context
.ucm_modifiers
) { /* new mapping */
1165 m
->ucm_context
.ucm_devices
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1166 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1167 m
->ucm_context
.ucm
= ucm
;
1168 m
->ucm_context
.direction
= is_sink
? PA_DIRECTION_OUTPUT
: PA_DIRECTION_INPUT
;
1170 m
->device_strings
= pa_xnew0(char*, 2);
1171 m
->device_strings
[0] = pa_xstrdup(device_str
);
1172 m
->direction
= is_sink
? PA_ALSA_DIRECTION_OUTPUT
: PA_ALSA_DIRECTION_INPUT
;
1173 /* Modifier sinks should not be routed to by default */
1176 ucm_add_mapping(p
, m
);
1177 } else if (!m
->ucm_context
.ucm_modifiers
) /* share pcm with device */
1178 m
->ucm_context
.ucm_modifiers
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1180 alsa_mapping_add_ucm_modifier(m
, modifier
);
1185 static int ucm_create_mapping(
1186 pa_alsa_ucm_config
*ucm
,
1187 pa_alsa_profile_set
*ps
,
1189 pa_alsa_ucm_device
*device
,
1190 const char *verb_name
,
1191 const char *device_name
,
1193 const char *source
) {
1197 if (!sink
&& !source
) {
1198 pa_log("No sink and source at %s: %s", verb_name
, device_name
);
1203 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, sink
, TRUE
);
1204 if (ret
== 0 && source
)
1205 ret
= ucm_create_mapping_direction(ucm
, ps
, p
, device
, verb_name
, device_name
, source
, FALSE
);
1210 static pa_alsa_jack
* ucm_get_jack(pa_alsa_ucm_config
*ucm
, const char *dev_name
, const char *pre_tag
) {
1212 char *name
= pa_sprintf_malloc("%s%s", pre_tag
, dev_name
);
1214 PA_LLIST_FOREACH(j
, ucm
->jacks
)
1215 if (pa_streq(j
->name
, name
))
1218 j
= pa_xnew0(pa_alsa_jack
, 1);
1219 j
->state_unplugged
= PA_AVAILABLE_NO
;
1220 j
->state_plugged
= PA_AVAILABLE_YES
;
1221 j
->name
= pa_xstrdup(name
);
1222 j
->alsa_name
= pa_sprintf_malloc("%s Jack", dev_name
);
1224 PA_LLIST_PREPEND(pa_alsa_jack
, ucm
->jacks
, j
);
1231 static int ucm_create_profile(
1232 pa_alsa_ucm_config
*ucm
,
1233 pa_alsa_profile_set
*ps
,
1234 pa_alsa_ucm_verb
*verb
,
1235 const char *verb_name
,
1236 const char *verb_desc
) {
1239 pa_alsa_ucm_device
*dev
;
1240 pa_alsa_ucm_modifier
*mod
;
1242 const char *name
, *sink
, *source
;
1247 if (pa_hashmap_get(ps
->profiles
, verb_name
)) {
1248 pa_log("Verb %s already exists", verb_name
);
1252 p
= pa_xnew0(pa_alsa_profile
, 1);
1253 p
->profile_set
= ps
;
1254 p
->name
= pa_xstrdup(verb_name
);
1255 p
->description
= pa_xstrdup(verb_desc
);
1257 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1258 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1260 p
->supported
= TRUE
;
1261 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
1263 /* TODO: get profile priority from ucm info or policy management */
1264 c
= verb_cmp
= pa_xstrdup(verb_name
);
1266 if (*c
== '_') *c
= ' ';
1270 for (i
= 0; verb_info
[i
].id
; i
++) {
1271 if (strcasecmp(verb_info
[i
].id
, verb_cmp
) == 0) {
1272 p
->priority
= verb_info
[i
].priority
;
1279 if (verb_info
[i
].id
== NULL
)
1282 PA_LLIST_FOREACH(dev
, verb
->devices
) {
1283 name
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1285 sink
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1286 source
= pa_proplist_gets(dev
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1288 ucm_create_mapping(ucm
, ps
, p
, dev
, verb_name
, name
, sink
, source
);
1291 dev
->output_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_OUTPUT
);
1293 dev
->input_jack
= ucm_get_jack(ucm
, name
, PA_UCM_PRE_TAG_INPUT
);
1296 /* Now find modifiers that have their own PlaybackPCM and create
1297 * separate sinks for them. */
1298 PA_LLIST_FOREACH(mod
, verb
->modifiers
) {
1299 name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1301 sink
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SINK
);
1302 source
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_SOURCE
);
1305 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, sink
, TRUE
);
1307 ucm_create_mapping_for_modifier(ucm
, ps
, p
, mod
, verb_name
, name
, source
, FALSE
);
1310 pa_alsa_profile_dump(p
);
1315 static snd_pcm_t
* mapping_open_pcm(pa_alsa_ucm_config
*ucm
, pa_alsa_mapping
*m
, int mode
) {
1317 pa_sample_spec try_ss
= ucm
->core
->default_sample_spec
;
1318 pa_channel_map try_map
;
1319 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
1320 bool exact_channels
= m
->channel_map
.channels
> 0;
1322 if (exact_channels
) {
1323 try_map
= m
->channel_map
;
1324 try_ss
.channels
= try_map
.channels
;
1326 pa_channel_map_init_extend(&try_map
, try_ss
.channels
, PA_CHANNEL_MAP_ALSA
);
1329 pa_usec_to_bytes(ucm
->core
->default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
1330 pa_frame_size(&try_ss
);
1331 try_buffer_size
= ucm
->core
->default_n_fragments
* try_period_size
;
1333 pcm
= pa_alsa_open_by_device_string(m
->device_strings
[0], NULL
, &try_ss
,
1334 &try_map
, mode
, &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
, exact_channels
);
1336 if (pcm
&& !exact_channels
)
1337 m
->channel_map
= try_map
;
1342 static void profile_finalize_probing(pa_alsa_profile
*p
) {
1346 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1353 snd_pcm_close(m
->output_pcm
);
1354 m
->output_pcm
= NULL
;
1357 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1364 snd_pcm_close(m
->input_pcm
);
1365 m
->input_pcm
= NULL
;
1369 static void ucm_mapping_jack_probe(pa_alsa_mapping
*m
) {
1370 snd_pcm_t
*pcm_handle
;
1371 snd_mixer_t
*mixer_handle
;
1372 snd_hctl_t
*hctl_handle
;
1373 pa_alsa_ucm_mapping_context
*context
= &m
->ucm_context
;
1374 pa_alsa_ucm_device
*dev
;
1377 pcm_handle
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? m
->output_pcm
: m
->input_pcm
;
1378 mixer_handle
= pa_alsa_open_mixer_for_pcm(pcm_handle
, NULL
, &hctl_handle
);
1379 if (!mixer_handle
|| !hctl_handle
)
1382 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1384 jack
= m
->direction
== PA_ALSA_DIRECTION_OUTPUT
? dev
->output_jack
: dev
->input_jack
;
1386 jack
->has_control
= pa_alsa_find_jack(hctl_handle
, jack
->alsa_name
) != NULL
;
1387 pa_log_info("UCM jack %s has_control=%d", jack
->name
, jack
->has_control
);
1390 snd_mixer_close(mixer_handle
);
1393 static void ucm_probe_profile_set(pa_alsa_ucm_config
*ucm
, pa_alsa_profile_set
*ps
) {
1399 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
1401 pa_log_info("Set ucm verb to %s", p
->name
);
1403 if ((snd_use_case_set(ucm
->ucm_mgr
, "_verb", p
->name
)) < 0) {
1404 pa_log("Failed to set verb %s", p
->name
);
1405 p
->supported
= FALSE
;
1409 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
1410 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1411 /* Skip jack probing on modifier PCMs since we expect this to
1412 * only be controlled on the main device/verb PCM. */
1416 m
->output_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_PLAYBACK
);
1417 if (!m
->output_pcm
) {
1418 p
->supported
= FALSE
;
1424 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
1425 if (PA_UCM_IS_MODIFIER_MAPPING(m
)) {
1426 /* Skip jack probing on modifier PCMs since we expect this to
1427 * only be controlled on the main device/verb PCM. */
1431 m
->input_pcm
= mapping_open_pcm(ucm
, m
, SND_PCM_STREAM_CAPTURE
);
1432 if (!m
->input_pcm
) {
1433 p
->supported
= FALSE
;
1439 if (!p
->supported
) {
1440 profile_finalize_probing(p
);
1444 pa_log_debug("Profile %s supported.", p
->name
);
1446 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
1447 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1448 ucm_mapping_jack_probe(m
);
1450 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
1451 if (!PA_UCM_IS_MODIFIER_MAPPING(m
))
1452 ucm_mapping_jack_probe(m
);
1454 profile_finalize_probing(p
);
1457 /* restore ucm state */
1458 snd_use_case_set(ucm
->ucm_mgr
, "_verb", SND_USE_CASE_VERB_INACTIVE
);
1460 pa_alsa_profile_set_drop_unsupported(ps
);
1463 pa_alsa_profile_set
* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config
*ucm
, pa_channel_map
*default_channel_map
) {
1464 pa_alsa_ucm_verb
*verb
;
1465 pa_alsa_profile_set
*ps
;
1467 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
1468 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1469 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1470 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
1472 /* create a profile for each verb */
1473 PA_LLIST_FOREACH(verb
, ucm
->verbs
) {
1474 const char *verb_name
;
1475 const char *verb_desc
;
1477 verb_name
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1478 verb_desc
= pa_proplist_gets(verb
->proplist
, PA_ALSA_PROP_UCM_DESCRIPTION
);
1479 if (verb_name
== NULL
) {
1480 pa_log("Verb with no name");
1484 ucm_create_profile(ucm
, ps
, verb
, verb_name
, verb_desc
);
1487 ucm_probe_profile_set(ucm
, ps
);
1493 static void free_verb(pa_alsa_ucm_verb
*verb
) {
1494 pa_alsa_ucm_device
*di
, *dn
;
1495 pa_alsa_ucm_modifier
*mi
, *mn
;
1497 PA_LLIST_FOREACH_SAFE(di
, dn
, verb
->devices
) {
1498 PA_LLIST_REMOVE(pa_alsa_ucm_device
, verb
->devices
, di
);
1499 pa_proplist_free(di
->proplist
);
1500 if (di
->conflicting_devices
)
1501 pa_idxset_free(di
->conflicting_devices
, NULL
);
1502 if (di
->supported_devices
)
1503 pa_idxset_free(di
->supported_devices
, NULL
);
1507 PA_LLIST_FOREACH_SAFE(mi
, mn
, verb
->modifiers
) {
1508 PA_LLIST_REMOVE(pa_alsa_ucm_modifier
, verb
->modifiers
, mi
);
1509 pa_proplist_free(mi
->proplist
);
1510 if (mi
->n_suppdev
> 0)
1511 snd_use_case_free_list(mi
->supported_devices
, mi
->n_suppdev
);
1512 if (mi
->n_confdev
> 0)
1513 snd_use_case_free_list(mi
->conflicting_devices
, mi
->n_confdev
);
1514 pa_xfree(mi
->media_role
);
1517 pa_proplist_free(verb
->proplist
);
1521 void pa_alsa_ucm_free(pa_alsa_ucm_config
*ucm
) {
1522 pa_alsa_ucm_verb
*vi
, *vn
;
1523 pa_alsa_jack
*ji
, *jn
;
1525 PA_LLIST_FOREACH_SAFE(vi
, vn
, ucm
->verbs
) {
1526 PA_LLIST_REMOVE(pa_alsa_ucm_verb
, ucm
->verbs
, vi
);
1529 PA_LLIST_FOREACH_SAFE(ji
, jn
, ucm
->jacks
) {
1530 PA_LLIST_REMOVE(pa_alsa_jack
, ucm
->jacks
, ji
);
1531 pa_xfree(ji
->alsa_name
);
1536 snd_use_case_mgr_close(ucm
->ucm_mgr
);
1537 ucm
->ucm_mgr
= NULL
;
1541 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context
*context
) {
1542 pa_alsa_ucm_device
*dev
;
1543 pa_alsa_ucm_modifier
*mod
;
1546 if (context
->ucm_devices
) {
1547 /* clear ucm device pointer to mapping */
1548 PA_IDXSET_FOREACH(dev
, context
->ucm_devices
, idx
) {
1549 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1550 dev
->playback_mapping
= NULL
;
1552 dev
->capture_mapping
= NULL
;
1555 pa_idxset_free(context
->ucm_devices
, NULL
);
1558 if (context
->ucm_modifiers
) {
1559 PA_IDXSET_FOREACH(mod
, context
->ucm_modifiers
, idx
) {
1560 if (context
->direction
== PA_DIRECTION_OUTPUT
)
1561 mod
->playback_mapping
= NULL
;
1563 mod
->capture_mapping
= NULL
;
1566 pa_idxset_free(context
->ucm_modifiers
, NULL
);
1570 /* Enable the modifier when the first stream with matched role starts */
1571 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1572 pa_alsa_ucm_modifier
*mod
;
1574 if (!ucm
->active_verb
)
1577 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1578 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1579 if (mod
->enabled_counter
== 0) {
1580 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1582 pa_log_info("Enable ucm modifier %s", mod_name
);
1583 if (snd_use_case_set(ucm
->ucm_mgr
, "_enamod", mod_name
) < 0) {
1584 pa_log("Failed to enable ucm modifier %s", mod_name
);
1588 mod
->enabled_counter
++;
1594 /* Disable the modifier when the last stream with matched role ends */
1595 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config
*ucm
, const char *role
, pa_direction_t dir
) {
1596 pa_alsa_ucm_modifier
*mod
;
1598 if (!ucm
->active_verb
)
1601 PA_LLIST_FOREACH(mod
, ucm
->active_verb
->modifiers
) {
1602 if ((mod
->action_direction
== dir
) && (pa_streq(mod
->media_role
, role
))) {
1604 mod
->enabled_counter
--;
1605 if (mod
->enabled_counter
== 0) {
1606 const char *mod_name
= pa_proplist_gets(mod
->proplist
, PA_ALSA_PROP_UCM_NAME
);
1608 pa_log_info("Disable ucm modifier %s", mod_name
);
1609 if (snd_use_case_set(ucm
->ucm_mgr
, "_dismod", mod_name
) < 0) {
1610 pa_log("Failed to disable ucm modifier %s", mod_name
);