1 /***
2 This file is part of PulseAudio.
3
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.
7
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.
12
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.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <limits.h>
30 #include <alsa/asoundlib.h>
31
32 #ifdef HAVE_VALGRIND_MEMCHECK_H
33 #include <valgrind/memcheck.h>
34 #endif
35
36 #include <pulse/sample.h>
37 #include <pulse/xmalloc.h>
38 #include <pulse/timeval.h>
39 #include <pulse/util.h>
40
41 #include <pulsecore/log.h>
42 #include <pulsecore/macro.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/atomic.h>
45 #include <pulsecore/core-error.h>
46 #include <pulsecore/once.h>
47 #include <pulsecore/thread.h>
48 #include <pulsecore/conf-parser.h>
49 #include <pulsecore/strbuf.h>
50
51 #include "alsa-mixer.h"
52 #include "alsa-util.h"
53 #include "alsa-ucm.h"
54
55 #define PA_UCM_PRE_TAG_OUTPUT "[Out] "
56 #define PA_UCM_PRE_TAG_INPUT "[In] "
57
58 #define PA_UCM_PLAYBACK_PRIORITY_UNSET(device) ((device)->playback_channels && !(device)->playback_priority)
59 #define PA_UCM_CAPTURE_PRIORITY_UNSET(device) ((device)->capture_channels && !(device)->capture_priority)
60 #define PA_UCM_DEVICE_PRIORITY_SET(device, priority) \
61 do { \
62 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \
63 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \
64 } while (0)
65 #define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
66
67 #ifdef HAVE_ALSA_UCM
68
69 struct ucm_type {
70 const char *prefix;
71 pa_device_port_type_t type;
72 };
73
74 struct ucm_items {
75 const char *id;
76 const char *property;
77 };
78
79 struct ucm_info {
80 const char *id;
81 unsigned priority;
82 };
83
84 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device);
85 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
86 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
87
88 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name);
89
90
91 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
92 pa_alsa_ucm_device **devices, unsigned n_devices);
93 static void ucm_port_data_free(pa_device_port *port);
94 static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
95
96 static struct ucm_type types[] = {
97 {"None", PA_DEVICE_PORT_TYPE_UNKNOWN},
98 {"Speaker", PA_DEVICE_PORT_TYPE_SPEAKER},
99 {"Line", PA_DEVICE_PORT_TYPE_LINE},
100 {"Mic", PA_DEVICE_PORT_TYPE_MIC},
101 {"Headphones", PA_DEVICE_PORT_TYPE_HEADPHONES},
102 {"Headset", PA_DEVICE_PORT_TYPE_HEADSET},
103 {"Handset", PA_DEVICE_PORT_TYPE_HANDSET},
104 {"Bluetooth", PA_DEVICE_PORT_TYPE_BLUETOOTH},
105 {"Earpiece", PA_DEVICE_PORT_TYPE_EARPIECE},
106 {"SPDIF", PA_DEVICE_PORT_TYPE_SPDIF},
107 {"HDMI", PA_DEVICE_PORT_TYPE_HDMI},
108 {NULL, 0}
109 };
110
111 static struct ucm_items item[] = {
112 {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
113 {"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
114 {"PlaybackCTL", PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE},
115 {"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
116 {"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
117 {"PlaybackMixer", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE},
118 {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
119 {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
120 {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
121 {"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
122 {"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
123 {"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
124 {"CaptureCTL", PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE},
125 {"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
126 {"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
127 {"CaptureMixer", PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE},
128 {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
129 {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
130 {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
131 {"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
132 {"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
133 {"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
134 {"TQ", PA_ALSA_PROP_UCM_QOS},
135 {"JackCTL", PA_ALSA_PROP_UCM_JACK_DEVICE},
136 {"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
137 {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
138 {NULL, NULL},
139 };
140
141 /* UCM verb info - this should eventually be part of policy manangement */
142 static struct ucm_info verb_info[] = {
143 {SND_USE_CASE_VERB_INACTIVE, 0},
144 {SND_USE_CASE_VERB_HIFI, 8000},
145 {SND_USE_CASE_VERB_HIFI_LOW_POWER, 7000},
146 {SND_USE_CASE_VERB_VOICE, 6000},
147 {SND_USE_CASE_VERB_VOICE_LOW_POWER, 5000},
148 {SND_USE_CASE_VERB_VOICECALL, 4000},
149 {SND_USE_CASE_VERB_IP_VOICECALL, 4000},
150 {SND_USE_CASE_VERB_ANALOG_RADIO, 3000},
151 {SND_USE_CASE_VERB_DIGITAL_RADIO, 3000},
152 {NULL, 0}
153 };
154
155 /* UCM device info - should be overwritten by ucm property */
156 static struct ucm_info dev_info[] = {
157 {SND_USE_CASE_DEV_SPEAKER, 100},
158 {SND_USE_CASE_DEV_LINE, 100},
159 {SND_USE_CASE_DEV_HEADPHONES, 100},
160 {SND_USE_CASE_DEV_HEADSET, 300},
161 {SND_USE_CASE_DEV_HANDSET, 200},
162 {SND_USE_CASE_DEV_BLUETOOTH, 400},
163 {SND_USE_CASE_DEV_EARPIECE, 100},
164 {SND_USE_CASE_DEV_SPDIF, 100},
165 {SND_USE_CASE_DEV_HDMI, 100},
166 {SND_USE_CASE_DEV_NONE, 100},
167 {NULL, 0}
168 };
169
170
ucm_verb_value(snd_use_case_mgr_t * uc_mgr,const char * verb_name,const char * id)171 static char *ucm_verb_value(
172 snd_use_case_mgr_t *uc_mgr,
173 const char *verb_name,
174 const char *id) {
175
176 const char *value;
177 char *_id = pa_sprintf_malloc("=%s//%s", id, verb_name);
178 int err = snd_use_case_get(uc_mgr, _id, &value);
179 pa_xfree(_id);
180 if (err < 0)
181 return NULL;
182 pa_log_debug("Got %s for verb %s: %s", id, verb_name, value);
183 /* Use the cast here to allow free() call without casting for callers.
184 * The snd_use_case_get() returns mallocated string.
185 * See the Note: in use-case.h for snd_use_case_get().
186 */
187 return (char *)value;
188 }
189
ucm_device_exists(pa_idxset * idxset,pa_alsa_ucm_device * dev)190 static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
191 pa_alsa_ucm_device *d;
192 uint32_t idx;
193
194 PA_IDXSET_FOREACH(d, idxset, idx)
195 if (d == dev)
196 return 1;
197
198 return 0;
199 }
200
ucm_add_devices_to_idxset(pa_idxset * idxset,pa_alsa_ucm_device * me,pa_alsa_ucm_device * devices,const char ** dev_names,int n)201 static void ucm_add_devices_to_idxset(
202 pa_idxset *idxset,
203 pa_alsa_ucm_device *me,
204 pa_alsa_ucm_device *devices,
205 const char **dev_names,
206 int n) {
207
208 pa_alsa_ucm_device *d;
209
210 PA_LLIST_FOREACH(d, devices) {
211 const char *name;
212 int i;
213
214 if (d == me)
215 continue;
216
217 name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
218
219 for (i = 0; i < n; i++)
220 if (pa_streq(dev_names[i], name))
221 pa_idxset_put(idxset, d, NULL);
222 }
223 }
224
225 /* Split a string into words. Like pa_split_spaces() but handle '' and "". */
ucm_split_devnames(const char * c,const char ** state)226 static char *ucm_split_devnames(const char *c, const char **state) {
227 const char *current = *state ? *state : c;
228 char h;
229 size_t l;
230
231 if (!*current || *c == 0)
232 return NULL;
233
234 current += strspn(current, "\n\r \t");
235 h = *current;
236 if (h == '\'' || h =='"') {
237 c = ++current;
238 for (l = 0; *c && *c != h; l++) c++;
239 if (*c != h)
240 return NULL;
241 *state = c + 1;
242 } else {
243 l = strcspn(current, "\n\r \t");
244 *state = current+l;
245 }
246
247 return pa_xstrndup(current, l);
248 }
249
250
ucm_volume_free(pa_alsa_ucm_volume * vol)251 static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
252 pa_assert(vol);
253 pa_xfree(vol->mixer_elem);
254 pa_xfree(vol->master_elem);
255 pa_xfree(vol->master_type);
256 pa_xfree(vol);
257 }
258
259 /* Get the volume identifier */
ucm_get_mixer_id(pa_alsa_ucm_device * device,const char * mprop,const char * cprop,const char * cid)260 static char *ucm_get_mixer_id(
261 pa_alsa_ucm_device *device,
262 const char *mprop,
263 const char *cprop,
264 const char *cid)
265 {
266 #if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
267 snd_ctl_elem_id_t *ctl;
268 int err;
269 #endif
270 const char *value;
271 char *value2;
272 int index;
273
274 /* mixer element as first, if it's found, return it without modifications */
275 value = pa_proplist_gets(device->proplist, mprop);
276 if (value)
277 return pa_xstrdup(value);
278 /* fallback, get the control element identifier */
279 /* and try to do some heuristic to determine the mixer element name */
280 value = pa_proplist_gets(device->proplist, cprop);
281 if (value == NULL)
282 return NULL;
283 #if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
284 /* The new parser may return also element index. */
285 snd_ctl_elem_id_alloca(&ctl);
286 err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
287 if (err < 0)
288 return NULL;
289 value = snd_ctl_elem_id_get_name(ctl);
290 index = snd_ctl_elem_id_get_index(ctl);
291 #else
292 #warning "Upgrade to alsa-lib 1.2.1!"
293 index = 0;
294 #endif
295 if (!(value2 = pa_str_strip_suffix(value, " Playback Volume")))
296 if (!(value2 = pa_str_strip_suffix(value, " Capture Volume")))
297 if (!(value2 = pa_str_strip_suffix(value, " Volume")))
298 value2 = pa_xstrdup(value);
299 if (index > 0) {
300 char *mix = pa_sprintf_malloc("'%s',%d", value2, index);
301 pa_xfree(value2);
302 return mix;
303 }
304 return value2;
305 }
306
307 /* Get the volume identifier */
ucm_get_mixer_volume(pa_alsa_ucm_device * device,const char * mprop,const char * cprop,const char * cid,const char * masterid,const char * mastertype)308 static pa_alsa_ucm_volume *ucm_get_mixer_volume(
309 pa_alsa_ucm_device *device,
310 const char *mprop,
311 const char *cprop,
312 const char *cid,
313 const char *masterid,
314 const char *mastertype)
315 {
316 pa_alsa_ucm_volume *vol;
317 char *mixer_elem;
318
319 mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid);
320 if (mixer_elem == NULL)
321 return NULL;
322 vol = pa_xnew0(pa_alsa_ucm_volume, 1);
323 if (vol == NULL) {
324 pa_xfree(mixer_elem);
325 return NULL;
326 }
327 vol->mixer_elem = mixer_elem;
328 vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, masterid));
329 vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, mastertype));
330 return vol;
331 }
332
333 /* Get the ALSA mixer device for the UCM device */
get_mixer_device(pa_alsa_ucm_device * dev,bool is_sink)334 static const char *get_mixer_device(pa_alsa_ucm_device *dev, bool is_sink)
335 {
336 const char *dev_name;
337
338 if (is_sink) {
339 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE);
340 if (!dev_name)
341 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE);
342 } else {
343 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE);
344 if (!dev_name)
345 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE);
346 }
347 return dev_name;
348 }
349
350 /* Get the ALSA mixer device for the UCM jack */
get_jack_mixer_device(pa_alsa_ucm_device * dev,bool is_sink)351 static const char *get_jack_mixer_device(pa_alsa_ucm_device *dev, bool is_sink) {
352 const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_DEVICE);
353 if (!dev_name)
354 return get_mixer_device(dev, is_sink);
355 return dev_name;
356 }
357
358 /* Create a property list for this ucm device */
ucm_get_device_property(pa_alsa_ucm_device * device,snd_use_case_mgr_t * uc_mgr,pa_alsa_ucm_verb * verb,const char * device_name)359 static int ucm_get_device_property(
360 pa_alsa_ucm_device *device,
361 snd_use_case_mgr_t *uc_mgr,
362 pa_alsa_ucm_verb *verb,
363 const char *device_name) {
364
365 const char *value;
366 const char **devices;
367 char *id, *s;
368 int i;
369 int err;
370 uint32_t ui;
371 int n_confdev, n_suppdev;
372 pa_alsa_ucm_volume *vol;
373
374 /* determine the device type */
375 device->type = PA_DEVICE_PORT_TYPE_UNKNOWN;
376 id = s = pa_xstrdup(device_name);
377 while (s && *s && isalpha(*s)) s++;
378 if (s)
379 *s = '\0';
380 for (i = 0; types[i].prefix; i++)
381 if (pa_streq(id, types[i].prefix)) {
382 device->type = types[i].type;
383 break;
384 }
385 pa_xfree(id);
386
387 /* set properties */
388 for (i = 0; item[i].id; i++) {
389 id = pa_sprintf_malloc("%s/%s", item[i].id, device_name);
390 err = snd_use_case_get(uc_mgr, id, &value);
391 pa_xfree(id);
392 if (err < 0)
393 continue;
394
395 pa_log_debug("Got %s for device %s: %s", item[i].id, device_name, value);
396 pa_proplist_sets(device->proplist, item[i].property, value);
397 free((void*)value);
398 }
399
400 /* get direction and channels */
401 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
402 if (value) { /* output */
403 /* get channels */
404 if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
405 device->playback_channels = ui;
406 else
407 pa_log("UCM playback channels %s for device %s out of range", value, device_name);
408
409 /* get pcm */
410 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
411 if (!value) /* take pcm from verb playback default */
412 pa_log("UCM playback device %s fetch pcm failed", device_name);
413 }
414
415 if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK) &&
416 device->playback_channels == 0) {
417 pa_log_info("UCM file does not specify 'PlaybackChannels' "
418 "for device %s, assuming stereo.", device_name);
419 device->playback_channels = 2;
420 }
421
422 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
423 if (value) { /* input */
424 /* get channels */
425 if (pa_atou(value, &ui) == 0 && pa_channels_valid(ui))
426 device->capture_channels = ui;
427 else
428 pa_log("UCM capture channels %s for device %s out of range", value, device_name);
429
430 /* get pcm */
431 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
432 if (!value) /* take pcm from verb capture default */
433 pa_log("UCM capture device %s fetch pcm failed", device_name);
434 }
435
436 if (pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE) &&
437 device->capture_channels == 0) {
438 pa_log_info("UCM file does not specify 'CaptureChannels' "
439 "for device %s, assuming stereo.", device_name);
440 device->capture_channels = 2;
441 }
442
443 /* get rate and priority of device */
444 if (device->playback_channels) { /* sink device */
445 /* get rate */
446 if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
447 if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
448 pa_log_debug("UCM playback device %s rate %d", device_name, ui);
449 device->playback_rate = ui;
450 } else
451 pa_log_debug("UCM playback device %s has bad rate %s", device_name, value);
452 }
453
454 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY);
455 if (value) {
456 /* get priority from ucm config */
457 if (pa_atou(value, &ui) == 0)
458 device->playback_priority = ui;
459 else
460 pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
461 }
462
463 vol = ucm_get_mixer_volume(device,
464 PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
465 PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
466 "PlaybackVolume",
467 PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM,
468 PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE);
469 if (vol)
470 pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
471 }
472
473 if (device->capture_channels) { /* source device */
474 /* get rate */
475 if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
476 if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
477 pa_log_debug("UCM capture device %s rate %d", device_name, ui);
478 device->capture_rate = ui;
479 } else
480 pa_log_debug("UCM capture device %s has bad rate %s", device_name, value);
481 }
482
483 value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY);
484 if (value) {
485 /* get priority from ucm config */
486 if (pa_atou(value, &ui) == 0)
487 device->capture_priority = ui;
488 else
489 pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
490 }
491
492 vol = ucm_get_mixer_volume(device,
493 PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
494 PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
495 "CaptureVolume",
496 PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM,
497 PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE);
498 if (vol)
499 pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
500 }
501
502 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
503 /* get priority from static table */
504 for (i = 0; dev_info[i].id; i++) {
505 if (strcasecmp(dev_info[i].id, device_name) == 0) {
506 PA_UCM_DEVICE_PRIORITY_SET(device, dev_info[i].priority);
507 break;
508 }
509 }
510 }
511
512 if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) {
513 /* fall through to default priority */
514 device->playback_priority = 100;
515 }
516
517 if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
518 /* fall through to default priority */
519 device->capture_priority = 100;
520 }
521
522 id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", device_name);
523 n_confdev = snd_use_case_get_list(uc_mgr, id, &devices);
524 pa_xfree(id);
525
526 if (n_confdev <= 0)
527 pa_log_debug("No %s for device %s", "_conflictingdevs", device_name);
528 else {
529 device->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
530 ucm_add_devices_to_idxset(device->conflicting_devices, device, verb->devices, devices, n_confdev);
531 snd_use_case_free_list(devices, n_confdev);
532 }
533
534 id = pa_sprintf_malloc("%s/%s", "_supporteddevs", device_name);
535 n_suppdev = snd_use_case_get_list(uc_mgr, id, &devices);
536 pa_xfree(id);
537
538 if (n_suppdev <= 0)
539 pa_log_debug("No %s for device %s", "_supporteddevs", device_name);
540 else {
541 device->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
542 ucm_add_devices_to_idxset(device->supported_devices, device, verb->devices, devices, n_suppdev);
543 snd_use_case_free_list(devices, n_suppdev);
544 }
545
546 return 0;
547 };
548
549 /* Create a property list for this ucm modifier */
ucm_get_modifier_property(pa_alsa_ucm_modifier * modifier,snd_use_case_mgr_t * uc_mgr,const char * modifier_name)550 static int ucm_get_modifier_property(pa_alsa_ucm_modifier *modifier, snd_use_case_mgr_t *uc_mgr, const char *modifier_name) {
551 const char *value;
552 char *id;
553 int i;
554
555 for (i = 0; item[i].id; i++) {
556 int err;
557
558 id = pa_sprintf_malloc("=%s/%s", item[i].id, modifier_name);
559 err = snd_use_case_get(uc_mgr, id, &value);
560 pa_xfree(id);
561 if (err < 0)
562 continue;
563
564 pa_log_debug("Got %s for modifier %s: %s", item[i].id, modifier_name, value);
565 pa_proplist_sets(modifier->proplist, item[i].property, value);
566 free((void*)value);
567 }
568
569 id = pa_sprintf_malloc("%s/%s", "_conflictingdevs", modifier_name);
570 modifier->n_confdev = snd_use_case_get_list(uc_mgr, id, &modifier->conflicting_devices);
571 pa_xfree(id);
572 if (modifier->n_confdev < 0)
573 pa_log_debug("No %s for modifier %s", "_conflictingdevs", modifier_name);
574
575 id = pa_sprintf_malloc("%s/%s", "_supporteddevs", modifier_name);
576 modifier->n_suppdev = snd_use_case_get_list(uc_mgr, id, &modifier->supported_devices);
577 pa_xfree(id);
578 if (modifier->n_suppdev < 0)
579 pa_log_debug("No %s for modifier %s", "_supporteddevs", modifier_name);
580
581 return 0;
582 };
583
584 /* Create a list of devices for this verb */
ucm_get_devices(pa_alsa_ucm_verb * verb,snd_use_case_mgr_t * uc_mgr)585 static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
586 const char **dev_list;
587 int num_dev, i;
588
589 num_dev = snd_use_case_get_list(uc_mgr, "_devices", &dev_list);
590 if (num_dev < 0)
591 return num_dev;
592
593 for (i = 0; i < num_dev; i += 2) {
594 pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
595
596 d->proplist = pa_proplist_new();
597 pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i]));
598 pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1]));
599 d->ucm_ports = pa_dynarray_new(NULL);
600 d->hw_mute_jacks = pa_dynarray_new(NULL);
601 d->available = PA_AVAILABLE_UNKNOWN;
602
603 d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
604 (pa_free_cb_t) ucm_volume_free);
605 d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
606 (pa_free_cb_t) ucm_volume_free);
607
608 PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
609 }
610
611 snd_use_case_free_list(dev_list, num_dev);
612
613 return 0;
614 };
615
ucm_get_modifiers(pa_alsa_ucm_verb * verb,snd_use_case_mgr_t * uc_mgr)616 static int ucm_get_modifiers(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
617 const char **mod_list;
618 int num_mod, i;
619
620 num_mod = snd_use_case_get_list(uc_mgr, "_modifiers", &mod_list);
621 if (num_mod < 0)
622 return num_mod;
623
624 for (i = 0; i < num_mod; i += 2) {
625 pa_alsa_ucm_modifier *m;
626
627 if (!mod_list[i]) {
628 pa_log_warn("Got a modifier with a null name. Skipping.");
629 continue;
630 }
631
632 m = pa_xnew0(pa_alsa_ucm_modifier, 1);
633 m->proplist = pa_proplist_new();
634
635 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_NAME, mod_list[i]);
636 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(mod_list[i + 1]));
637
638 PA_LLIST_PREPEND(pa_alsa_ucm_modifier, verb->modifiers, m);
639 }
640
641 snd_use_case_free_list(mod_list, num_mod);
642
643 return 0;
644 };
645
add_role_to_device(pa_alsa_ucm_device * dev,const char * dev_name,const char * role_name,const char * role)646 static void add_role_to_device(pa_alsa_ucm_device *dev, const char *dev_name, const char *role_name, const char *role) {
647 const char *cur = pa_proplist_gets(dev->proplist, role_name);
648
649 if (!cur)
650 pa_proplist_sets(dev->proplist, role_name, role);
651 else if (!pa_str_in_list_spaces(cur, role)) { /* does not exist */
652 char *value = pa_sprintf_malloc("%s %s", cur, role);
653
654 pa_proplist_sets(dev->proplist, role_name, value);
655 pa_xfree(value);
656 }
657
658 pa_log_info("Add role %s to device %s(%s), result %s", role, dev_name, role_name, pa_proplist_gets(dev->proplist,
659 role_name));
660 }
661
add_media_role(const char * name,pa_alsa_ucm_device * list,const char * role_name,const char * role,bool is_sink)662 static void add_media_role(const char *name, pa_alsa_ucm_device *list, const char *role_name, const char *role, bool is_sink) {
663 pa_alsa_ucm_device *d;
664
665 PA_LLIST_FOREACH(d, list) {
666 const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
667
668 if (pa_streq(dev_name, name)) {
669 const char *sink = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SINK);
670 const char *source = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_SOURCE);
671
672 if (is_sink && sink)
673 add_role_to_device(d, dev_name, role_name, role);
674 else if (!is_sink && source)
675 add_role_to_device(d, dev_name, role_name, role);
676 break;
677 }
678 }
679 }
680
modifier_name_to_role(const char * mod_name,bool * is_sink)681 static char *modifier_name_to_role(const char *mod_name, bool *is_sink) {
682 char *sub = NULL, *tmp;
683
684 *is_sink = false;
685
686 if (pa_startswith(mod_name, "Play")) {
687 *is_sink = true;
688 sub = pa_xstrdup(mod_name + 4);
689 } else if (pa_startswith(mod_name, "Capture"))
690 sub = pa_xstrdup(mod_name + 7);
691
692 if (!sub || !*sub) {
693 pa_xfree(sub);
694 pa_log_warn("Can't match media roles for modifer %s", mod_name);
695 return NULL;
696 }
697
698 tmp = sub;
699
700 do {
701 *tmp = tolower(*tmp);
702 } while (*(++tmp));
703
704 return sub;
705 }
706
ucm_set_media_roles(pa_alsa_ucm_modifier * modifier,pa_alsa_ucm_device * list,const char * mod_name)707 static void ucm_set_media_roles(pa_alsa_ucm_modifier *modifier, pa_alsa_ucm_device *list, const char *mod_name) {
708 int i;
709 bool is_sink = false;
710 char *sub = NULL;
711 const char *role_name;
712
713 sub = modifier_name_to_role(mod_name, &is_sink);
714 if (!sub)
715 return;
716
717 modifier->action_direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
718 modifier->media_role = sub;
719
720 role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
721 for (i = 0; i < modifier->n_suppdev; i++) {
722 /* if modifier has no specific pcm, we add role intent to its supported devices */
723 if (!pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SINK) &&
724 !pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_SOURCE))
725 add_media_role(modifier->supported_devices[i], list, role_name, sub, is_sink);
726 }
727 }
728
append_lost_relationship(pa_alsa_ucm_device * dev)729 static void append_lost_relationship(pa_alsa_ucm_device *dev) {
730 uint32_t idx;
731 pa_alsa_ucm_device *d;
732
733 if (dev->conflicting_devices) {
734 PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
735 if (!d->conflicting_devices)
736 d->conflicting_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
737
738 if (pa_idxset_put(d->conflicting_devices, dev, NULL) == 0)
739 pa_log_warn("Add lost conflicting device %s to %s",
740 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
741 pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
742 }
743 }
744
745 if (dev->supported_devices) {
746 PA_IDXSET_FOREACH(d, dev->supported_devices, idx) {
747 if (!d->supported_devices)
748 d->supported_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
749
750 if (pa_idxset_put(d->supported_devices, dev, NULL) == 0)
751 pa_log_warn("Add lost supported device %s to %s",
752 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME),
753 pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME));
754 }
755 }
756 }
757
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)758 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
759 char *card_name;
760 const char **verb_list, *value;
761 int num_verbs, i, err = 0;
762
763 /* support multiple card instances, address card directly by index */
764 card_name = pa_sprintf_malloc("hw:%i", card_index);
765 err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
766 if (err < 0) {
767 /* fallback longname: is UCM available for this card ? */
768 pa_xfree(card_name);
769 err = snd_card_get_name(card_index, &card_name);
770 if (err < 0) {
771 pa_log("Card can't get card_name from card_index %d", card_index);
772 err = -PA_ALSA_ERR_UNSPECIFIED;
773 goto name_fail;
774 }
775
776 err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
777 if (err < 0) {
778 pa_log_info("UCM not available for card %s", card_name);
779 err = -PA_ALSA_ERR_UCM_OPEN;
780 goto ucm_mgr_fail;
781 }
782 }
783
784 err = snd_use_case_get(ucm->ucm_mgr, "=Linked", &value);
785 if (err >= 0) {
786 if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
787 free((void *)value);
788 pa_log_info("Empty (linked) UCM for card %s", card_name);
789 err = -PA_ALSA_ERR_UCM_LINKED;
790 goto ucm_verb_fail;
791 }
792 free((void *)value);
793 }
794
795 pa_log_info("UCM available for card %s", card_name);
796
797 /* get a list of all UCM verbs (profiles) for this card */
798 num_verbs = snd_use_case_verb_list(ucm->ucm_mgr, &verb_list);
799 if (num_verbs < 0) {
800 pa_log("UCM verb list not found for %s", card_name);
801 err = -PA_ALSA_ERR_UNSPECIFIED;
802 goto ucm_verb_fail;
803 }
804
805 /* get the properties of each UCM verb */
806 for (i = 0; i < num_verbs; i += 2) {
807 pa_alsa_ucm_verb *verb;
808
809 /* Get devices and modifiers for each verb */
810 err = pa_alsa_ucm_get_verb(ucm->ucm_mgr, verb_list[i], verb_list[i+1], &verb);
811 if (err < 0) {
812 pa_log("Failed to get the verb %s", verb_list[i]);
813 continue;
814 }
815
816 PA_LLIST_PREPEND(pa_alsa_ucm_verb, ucm->verbs, verb);
817 }
818
819 if (!ucm->verbs) {
820 pa_log("No UCM verb is valid for %s", card_name);
821 err = -PA_ALSA_ERR_UCM_NO_VERB;
822 }
823
824 snd_use_case_free_list(verb_list, num_verbs);
825
826 ucm_verb_fail:
827 if (err < 0) {
828 snd_use_case_mgr_close(ucm->ucm_mgr);
829 ucm->ucm_mgr = NULL;
830 }
831
832 ucm_mgr_fail:
833 pa_xfree(card_name);
834
835 name_fail:
836 return err;
837 }
838
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)839 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) {
840 pa_alsa_ucm_device *d;
841 pa_alsa_ucm_modifier *mod;
842 pa_alsa_ucm_verb *verb;
843 char *value;
844 unsigned ui;
845 int err = 0;
846
847 *p_verb = NULL;
848 pa_log_info("Set UCM verb to %s", verb_name);
849 err = snd_use_case_set(uc_mgr, "_verb", verb_name);
850 if (err < 0)
851 return err;
852
853 verb = pa_xnew0(pa_alsa_ucm_verb, 1);
854 verb->proplist = pa_proplist_new();
855
856 pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
857 pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
858
859 value = ucm_verb_value(uc_mgr, verb_name, "Priority");
860 if (value && !pa_atou(value, &ui))
861 verb->priority = ui > 10000 ? 10000 : ui;
862 free(value);
863
864 err = ucm_get_devices(verb, uc_mgr);
865 if (err < 0)
866 pa_log("No UCM devices for verb %s", verb_name);
867
868 err = ucm_get_modifiers(verb, uc_mgr);
869 if (err < 0)
870 pa_log("No UCM modifiers for verb %s", verb_name);
871
872 PA_LLIST_FOREACH(d, verb->devices) {
873 const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
874
875 /* Devices properties */
876 ucm_get_device_property(d, uc_mgr, verb, dev_name);
877 }
878 /* make conflicting or supported device mutual */
879 PA_LLIST_FOREACH(d, verb->devices)
880 append_lost_relationship(d);
881
882 PA_LLIST_FOREACH(mod, verb->modifiers) {
883 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
884
885 /* Modifier properties */
886 ucm_get_modifier_property(mod, uc_mgr, mod_name);
887
888 /* Set PA_PROP_DEVICE_INTENDED_ROLES property to devices */
889 pa_log_debug("Set media roles for verb %s, modifier %s", verb_name, mod_name);
890 ucm_set_media_roles(mod, verb->devices, mod_name);
891 }
892
893 *p_verb = verb;
894 return 0;
895 }
896
pa_alsa_ucm_device_cmp(const void * a,const void * b)897 static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
898 const pa_alsa_ucm_device *d1 = *(pa_alsa_ucm_device **)a;
899 const pa_alsa_ucm_device *d2 = *(pa_alsa_ucm_device **)b;
900
901 return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
902 }
903
set_eld_devices(pa_hashmap * hash)904 static void set_eld_devices(pa_hashmap *hash)
905 {
906 pa_device_port *port;
907 pa_alsa_ucm_port_data *data;
908 pa_alsa_ucm_device *dev;
909 const char *eld_mixer_device_name;
910 void *state;
911 int idx, eld_device;
912
913 PA_HASHMAP_FOREACH(port, hash, state) {
914 data = PA_DEVICE_PORT_DATA(port);
915 eld_mixer_device_name = NULL;
916 eld_device = -1;
917 PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
918 if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
919 if (eld_device >= 0 && eld_device != dev->eld_device) {
920 pa_log_error("The ELD device is already set!");
921 } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
922 pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
923 } else {
924 eld_mixer_device_name = dev->eld_mixer_device_name;
925 eld_device = dev->eld_device;
926 }
927 }
928 }
929 data->eld_device = eld_device;
930 data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
931 }
932 }
933
probe_volumes(pa_hashmap * hash,bool is_sink,snd_pcm_t * pcm_handle,pa_hashmap * mixers,bool ignore_dB)934 static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
935 pa_device_port *port;
936 pa_alsa_path *path;
937 pa_alsa_ucm_port_data *data;
938 pa_alsa_ucm_device *dev;
939 snd_mixer_t *mixer_handle;
940 const char *profile, *mdev, *mdev2;
941 void *state, *state2;
942 int idx;
943
944 PA_HASHMAP_FOREACH(port, hash, state) {
945 data = PA_DEVICE_PORT_DATA(port);
946
947 mdev = NULL;
948 PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
949 mdev2 = get_mixer_device(dev, is_sink);
950 if (mdev && mdev2 && !pa_streq(mdev, mdev2)) {
951 pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2);
952 goto fail;
953 }
954 if (mdev2)
955 mdev = mdev2;
956 }
957
958 if (mdev == NULL || !(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
959 pa_log_error("Failed to find a working mixer device (%s).", mdev);
960 goto fail;
961 }
962
963 PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
964 if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
965 pa_log_warn("Could not probe path: %s, using s/w volume", data->path->name);
966 pa_hashmap_remove(data->paths, profile);
967 } else if (!path->has_volume) {
968 pa_log_warn("Path %s is not a volume control", data->path->name);
969 pa_hashmap_remove(data->paths, profile);
970 } else
971 pa_log_debug("Set up h/w volume using '%s' for %s:%s", path->name, profile, port->name);
972 }
973 }
974
975 return;
976
977 fail:
978 /* We could not probe the paths we created. Free them and revert to software volumes. */
979 PA_HASHMAP_FOREACH(port, hash, state) {
980 data = PA_DEVICE_PORT_DATA(port);
981 pa_hashmap_remove_all(data->paths);
982 }
983 }
984
ucm_add_port_combination(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_alsa_ucm_device ** pdevices,int num,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)985 static void ucm_add_port_combination(
986 pa_hashmap *hash,
987 pa_alsa_ucm_mapping_context *context,
988 bool is_sink,
989 pa_alsa_ucm_device **pdevices,
990 int num,
991 pa_hashmap *ports,
992 pa_card_profile *cp,
993 pa_core *core) {
994
995 pa_device_port *port;
996 int i;
997 unsigned priority;
998 double prio2;
999 char *name, *desc;
1000 const char *dev_name;
1001 const char *direction;
1002 const char *profile;
1003 pa_alsa_ucm_device *sorted[num], *dev;
1004 pa_alsa_ucm_port_data *data;
1005 pa_alsa_ucm_volume *vol;
1006 pa_alsa_jack *jack, *jack2;
1007 pa_device_port_type_t type, type2;
1008 void *state;
1009
1010 for (i = 0; i < num; i++)
1011 sorted[i] = pdevices[i];
1012
1013 /* Sort by alphabetical order so as to have a deterministic naming scheme
1014 * for combination ports */
1015 qsort(&sorted[0], num, sizeof(pa_alsa_ucm_device *), pa_alsa_ucm_device_cmp);
1016
1017 dev = sorted[0];
1018 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1019
1020 name = pa_sprintf_malloc("%s%s", is_sink ? PA_UCM_PRE_TAG_OUTPUT : PA_UCM_PRE_TAG_INPUT, dev_name);
1021 desc = num == 1 ? pa_xstrdup(pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_DESCRIPTION))
1022 : pa_sprintf_malloc("Combination port for %s", dev_name);
1023
1024 priority = is_sink ? dev->playback_priority : dev->capture_priority;
1025 prio2 = (priority == 0 ? 0 : 1.0/priority);
1026 jack = ucm_get_jack(context->ucm, dev);
1027 type = dev->type;
1028
1029 for (i = 1; i < num; i++) {
1030 char *tmp;
1031
1032 dev = sorted[i];
1033 dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1034
1035 tmp = pa_sprintf_malloc("%s+%s", name, dev_name);
1036 pa_xfree(name);
1037 name = tmp;
1038
1039 tmp = pa_sprintf_malloc("%s,%s", desc, dev_name);
1040 pa_xfree(desc);
1041 desc = tmp;
1042
1043 priority = is_sink ? dev->playback_priority : dev->capture_priority;
1044 if (priority != 0 && prio2 > 0)
1045 prio2 += 1.0/priority;
1046
1047 jack2 = ucm_get_jack(context->ucm, dev);
1048 if (jack2) {
1049 if (jack && jack != jack2)
1050 pa_log_warn("Multiple jacks per combined device '%s': '%s' '%s'", name, jack->name, jack2->name);
1051 jack = jack2;
1052 }
1053
1054 type2 = dev->type;
1055 if (type2 != PA_DEVICE_PORT_TYPE_UNKNOWN) {
1056 if (type != PA_DEVICE_PORT_TYPE_UNKNOWN && type != type2)
1057 pa_log_warn("Multiple device types per combined device '%s': %d %d", name, type, type2);
1058 type = type2;
1059 }
1060 }
1061
1062 /* Make combination ports always have lower priority, and use the formula
1063 1/p = 1/p1 + 1/p2 + ... 1/pn.
1064 This way, the result will always be less than the individual components,
1065 yet higher components will lead to higher result. */
1066
1067 if (num > 1)
1068 priority = prio2 > 0 ? 1.0/prio2 : 0;
1069
1070 port = pa_hashmap_get(ports, name);
1071 if (!port) {
1072 pa_device_port_new_data port_data;
1073
1074 pa_device_port_new_data_init(&port_data);
1075 pa_device_port_new_data_set_name(&port_data, name);
1076 pa_device_port_new_data_set_description(&port_data, desc);
1077 pa_device_port_new_data_set_type(&port_data, type);
1078 pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
1079 if (jack)
1080 pa_device_port_new_data_set_availability_group(&port_data, jack->name);
1081
1082 port = pa_device_port_new(core, &port_data, sizeof(pa_alsa_ucm_port_data));
1083 pa_device_port_new_data_done(&port_data);
1084
1085 data = PA_DEVICE_PORT_DATA(port);
1086 ucm_port_data_init(data, context->ucm, port, pdevices, num);
1087 port->impl_free = ucm_port_data_free;
1088
1089 pa_hashmap_put(ports, port->name, port);
1090 pa_log_debug("Add port %s: %s", port->name, port->description);
1091
1092 if (num == 1) {
1093 /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
1094 * ports. */
1095 data = PA_DEVICE_PORT_DATA(port);
1096
1097 PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
1098 pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
1099 is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
1100
1101 if (!path)
1102 pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
1103 else {
1104 if (vol->master_elem) {
1105 pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
1106 e->switch_use = PA_ALSA_SWITCH_MUTE;
1107 e->volume_use = PA_ALSA_VOLUME_MERGE;
1108 }
1109
1110 pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
1111
1112 /* Add path also to already created empty path set */
1113 dev = sorted[0];
1114 if (is_sink)
1115 pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1116 else
1117 pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
1118 }
1119 }
1120 }
1121 }
1122
1123 port->priority = priority;
1124
1125 pa_xfree(name);
1126 pa_xfree(desc);
1127
1128 direction = is_sink ? "output" : "input";
1129 pa_log_debug("Port %s direction %s, priority %d", port->name, direction, priority);
1130
1131 if (cp) {
1132 pa_log_debug("Adding profile %s to port %s.", cp->name, port->name);
1133 pa_hashmap_put(port->profiles, cp->name, cp);
1134 }
1135
1136 if (hash) {
1137 pa_hashmap_put(hash, port->name, port);
1138 pa_device_port_ref(port);
1139 }
1140 }
1141
ucm_port_contains(const char * port_name,const char * dev_name,bool is_sink)1142 static int ucm_port_contains(const char *port_name, const char *dev_name, bool is_sink) {
1143 int ret = 0;
1144 const char *r;
1145 const char *state = NULL;
1146 size_t len;
1147
1148 if (!port_name || !dev_name)
1149 return false;
1150
1151 port_name += is_sink ? strlen(PA_UCM_PRE_TAG_OUTPUT) : strlen(PA_UCM_PRE_TAG_INPUT);
1152
1153 while ((r = pa_split_in_place(port_name, "+", &len, &state))) {
1154 if (strlen(dev_name) == len && !strncmp(r, dev_name, len)) {
1155 ret = 1;
1156 break;
1157 }
1158 }
1159
1160 return ret;
1161 }
1162
ucm_check_conformance(pa_alsa_ucm_mapping_context * context,pa_alsa_ucm_device ** pdevices,int dev_num,pa_alsa_ucm_device * dev)1163 static int ucm_check_conformance(
1164 pa_alsa_ucm_mapping_context *context,
1165 pa_alsa_ucm_device **pdevices,
1166 int dev_num,
1167 pa_alsa_ucm_device *dev) {
1168
1169 uint32_t idx;
1170 pa_alsa_ucm_device *d;
1171 int i;
1172
1173 pa_assert(dev);
1174
1175 pa_log_debug("Check device %s conformance with %d other devices",
1176 pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME), dev_num);
1177 if (dev_num == 0) {
1178 pa_log_debug("First device in combination, number 1");
1179 return 1;
1180 }
1181
1182 if (dev->conflicting_devices) { /* the device defines conflicting devices */
1183 PA_IDXSET_FOREACH(d, dev->conflicting_devices, idx) {
1184 for (i = 0; i < dev_num; i++) {
1185 if (pdevices[i] == d) {
1186 pa_log_debug("Conflicting device found");
1187 return 0;
1188 }
1189 }
1190 }
1191 } else if (dev->supported_devices) { /* the device defines supported devices */
1192 for (i = 0; i < dev_num; i++) {
1193 if (!ucm_device_exists(dev->supported_devices, pdevices[i])) {
1194 pa_log_debug("Supported device not found");
1195 return 0;
1196 }
1197 }
1198 } else { /* not support any other devices */
1199 pa_log_debug("Not support any other devices");
1200 return 0;
1201 }
1202
1203 pa_log_debug("Device added to combination, number %d", dev_num + 1);
1204 return 1;
1205 }
1206
get_next_device(pa_idxset * idxset,uint32_t * idx)1207 static inline pa_alsa_ucm_device *get_next_device(pa_idxset *idxset, uint32_t *idx) {
1208 pa_alsa_ucm_device *dev;
1209
1210 if (*idx == PA_IDXSET_INVALID)
1211 dev = pa_idxset_first(idxset, idx);
1212 else
1213 dev = pa_idxset_next(idxset, idx);
1214
1215 return dev;
1216 }
1217
ucm_add_ports_combination(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_alsa_ucm_device ** pdevices,int dev_num,uint32_t map_index,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)1218 static void ucm_add_ports_combination(
1219 pa_hashmap *hash,
1220 pa_alsa_ucm_mapping_context *context,
1221 bool is_sink,
1222 pa_alsa_ucm_device **pdevices,
1223 int dev_num,
1224 uint32_t map_index,
1225 pa_hashmap *ports,
1226 pa_card_profile *cp,
1227 pa_core *core) {
1228
1229 pa_alsa_ucm_device *dev;
1230 uint32_t idx = map_index;
1231
1232 if ((dev = get_next_device(context->ucm_devices, &idx)) == NULL)
1233 return;
1234
1235 /* check if device at map_index can combine with existing devices combination */
1236 if (ucm_check_conformance(context, pdevices, dev_num, dev)) {
1237 /* add device at map_index to devices combination */
1238 pdevices[dev_num] = dev;
1239 /* add current devices combination as a new port */
1240 ucm_add_port_combination(hash, context, is_sink, pdevices, dev_num + 1, ports, cp, core);
1241 /* try more elements combination */
1242 ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num + 1, idx, ports, cp, core);
1243 }
1244
1245 /* try other device with current elements number */
1246 ucm_add_ports_combination(hash, context, is_sink, pdevices, dev_num, idx, ports, cp, core);
1247 }
1248
merge_roles(const char * cur,const char * add)1249 static char* merge_roles(const char *cur, const char *add) {
1250 char *r, *ret;
1251 const char *state = NULL;
1252
1253 if (add == NULL)
1254 return pa_xstrdup(cur);
1255 else if (cur == NULL)
1256 return pa_xstrdup(add);
1257
1258 ret = pa_xstrdup(cur);
1259
1260 while ((r = pa_split_spaces(add, &state))) {
1261 char *value;
1262
1263 if (!pa_str_in_list_spaces(ret, r))
1264 value = pa_sprintf_malloc("%s %s", ret, r);
1265 else {
1266 pa_xfree(r);
1267 continue;
1268 }
1269
1270 pa_xfree(ret);
1271 ret = value;
1272 pa_xfree(r);
1273 }
1274
1275 return ret;
1276 }
1277
pa_alsa_ucm_add_ports_combination(pa_hashmap * p,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)1278 void pa_alsa_ucm_add_ports_combination(
1279 pa_hashmap *p,
1280 pa_alsa_ucm_mapping_context *context,
1281 bool is_sink,
1282 pa_hashmap *ports,
1283 pa_card_profile *cp,
1284 pa_core *core) {
1285
1286 pa_alsa_ucm_device **pdevices;
1287
1288 pa_assert(context->ucm_devices);
1289
1290 if (pa_idxset_size(context->ucm_devices) > 0) {
1291 pdevices = pa_xnew(pa_alsa_ucm_device *, pa_idxset_size(context->ucm_devices));
1292 ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
1293 pa_xfree(pdevices);
1294 }
1295
1296 /* ELD devices */
1297 set_eld_devices(ports);
1298 }
1299
pa_alsa_ucm_add_ports(pa_hashmap ** p,pa_proplist * proplist,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_card * card,snd_pcm_t * pcm_handle,bool ignore_dB)1300 void pa_alsa_ucm_add_ports(
1301 pa_hashmap **p,
1302 pa_proplist *proplist,
1303 pa_alsa_ucm_mapping_context *context,
1304 bool is_sink,
1305 pa_card *card,
1306 snd_pcm_t *pcm_handle,
1307 bool ignore_dB) {
1308
1309 uint32_t idx;
1310 char *merged_roles;
1311 const char *role_name = is_sink ? PA_ALSA_PROP_UCM_PLAYBACK_ROLES : PA_ALSA_PROP_UCM_CAPTURE_ROLES;
1312 pa_alsa_ucm_device *dev;
1313 pa_alsa_ucm_modifier *mod;
1314 char *tmp;
1315
1316 pa_assert(p);
1317 pa_assert(*p);
1318
1319 /* add ports first */
1320 pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
1321
1322 /* now set up volume paths if any */
1323 probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
1324
1325 /* then set property PA_PROP_DEVICE_INTENDED_ROLES */
1326 merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
1327 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1328 const char *roles = pa_proplist_gets(dev->proplist, role_name);
1329 tmp = merge_roles(merged_roles, roles);
1330 pa_xfree(merged_roles);
1331 merged_roles = tmp;
1332 }
1333
1334 if (context->ucm_modifiers)
1335 PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
1336 tmp = merge_roles(merged_roles, mod->media_role);
1337 pa_xfree(merged_roles);
1338 merged_roles = tmp;
1339 }
1340
1341 if (merged_roles)
1342 pa_proplist_sets(proplist, PA_PROP_DEVICE_INTENDED_ROLES, merged_roles);
1343
1344 pa_log_info("ALSA device %s roles: %s", pa_proplist_gets(proplist, PA_PROP_DEVICE_STRING), pa_strnull(merged_roles));
1345 pa_xfree(merged_roles);
1346 }
1347
1348 /* Change UCM verb and device to match selected card profile */
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,const char * new_profile,const char * old_profile)1349 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
1350 int ret = 0;
1351 const char *profile;
1352 pa_alsa_ucm_verb *verb;
1353 pa_device_port *port;
1354 pa_alsa_ucm_port_data *data;
1355 void *state;
1356
1357 if (new_profile == old_profile)
1358 return ret;
1359 else if (new_profile == NULL || old_profile == NULL)
1360 profile = new_profile ? new_profile : SND_USE_CASE_VERB_INACTIVE;
1361 else if (!pa_streq(new_profile, old_profile))
1362 profile = new_profile;
1363 else
1364 return ret;
1365
1366 /* change verb */
1367 pa_log_info("Set UCM verb to %s", profile);
1368 if ((snd_use_case_set(ucm->ucm_mgr, "_verb", profile)) < 0) {
1369 pa_log("Failed to set verb %s", profile);
1370 ret = -1;
1371 }
1372
1373 /* find active verb */
1374 ucm->active_verb = NULL;
1375 PA_LLIST_FOREACH(verb, ucm->verbs) {
1376 const char *verb_name;
1377 verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
1378 if (pa_streq(verb_name, profile)) {
1379 ucm->active_verb = verb;
1380 break;
1381 }
1382 }
1383
1384 /* select volume controls on ports */
1385 PA_HASHMAP_FOREACH(port, card->ports, state) {
1386 data = PA_DEVICE_PORT_DATA(port);
1387 data->path = pa_hashmap_get(data->paths, profile);
1388 }
1389
1390 return ret;
1391 }
1392
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port,bool is_sink)1393 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
1394 int i;
1395 int ret = 0;
1396 pa_alsa_ucm_config *ucm;
1397 const char **enable_devs;
1398 int enable_num = 0;
1399 uint32_t idx;
1400 pa_alsa_ucm_device *dev;
1401
1402 pa_assert(context && context->ucm);
1403
1404 ucm = context->ucm;
1405 pa_assert(ucm->ucm_mgr);
1406
1407 enable_devs = pa_xnew(const char *, pa_idxset_size(context->ucm_devices));
1408
1409 /* first disable then enable */
1410 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1411 const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1412
1413 if (ucm_port_contains(port->name, dev_name, is_sink))
1414 enable_devs[enable_num++] = dev_name;
1415 else {
1416 pa_log_debug("Disable ucm device %s", dev_name);
1417 if (snd_use_case_set(ucm->ucm_mgr, "_disdev", dev_name) > 0) {
1418 pa_log("Failed to disable ucm device %s", dev_name);
1419 ret = -1;
1420 break;
1421 }
1422 }
1423 }
1424
1425 for (i = 0; i < enable_num; i++) {
1426 pa_log_debug("Enable ucm device %s", enable_devs[i]);
1427 if (snd_use_case_set(ucm->ucm_mgr, "_enadev", enable_devs[i]) < 0) {
1428 pa_log("Failed to enable ucm device %s", enable_devs[i]);
1429 ret = -1;
1430 break;
1431 }
1432 }
1433
1434 pa_xfree(enable_devs);
1435
1436 return ret;
1437 }
1438
ucm_add_mapping(pa_alsa_profile * p,pa_alsa_mapping * m)1439 static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
1440
1441 pa_alsa_path_set *ps;
1442
1443 /* create empty path set for the future path additions */
1444 ps = pa_xnew0(pa_alsa_path_set, 1);
1445 ps->direction = m->direction;
1446 ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1447
1448 switch (m->direction) {
1449 case PA_ALSA_DIRECTION_ANY:
1450 pa_idxset_put(p->output_mappings, m, NULL);
1451 pa_idxset_put(p->input_mappings, m, NULL);
1452 m->output_path_set = ps;
1453 m->input_path_set = ps;
1454 break;
1455 case PA_ALSA_DIRECTION_OUTPUT:
1456 pa_idxset_put(p->output_mappings, m, NULL);
1457 m->output_path_set = ps;
1458 break;
1459 case PA_ALSA_DIRECTION_INPUT:
1460 pa_idxset_put(p->input_mappings, m, NULL);
1461 m->input_path_set = ps;
1462 break;
1463 }
1464 }
1465
alsa_mapping_add_ucm_device(pa_alsa_mapping * m,pa_alsa_ucm_device * device)1466 static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
1467 char *cur_desc;
1468 const char *new_desc, *mdev;
1469 bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
1470
1471 pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
1472
1473 new_desc = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1474 cur_desc = m->description;
1475 if (cur_desc)
1476 m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1477 else
1478 m->description = pa_xstrdup(new_desc);
1479 pa_xfree(cur_desc);
1480
1481 /* walk around null case */
1482 m->description = m->description ? m->description : pa_xstrdup("");
1483
1484 /* save mapping to ucm device */
1485 if (is_sink)
1486 device->playback_mapping = m;
1487 else
1488 device->capture_mapping = m;
1489
1490 mdev = get_mixer_device(device, is_sink);
1491 if (mdev)
1492 pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev);
1493 }
1494
alsa_mapping_add_ucm_modifier(pa_alsa_mapping * m,pa_alsa_ucm_modifier * modifier)1495 static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
1496 char *cur_desc;
1497 const char *new_desc, *mod_name, *channel_str;
1498 uint32_t channels = 0;
1499
1500 pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
1501
1502 new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
1503 cur_desc = m->description;
1504 if (cur_desc)
1505 m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
1506 else
1507 m->description = pa_xstrdup(new_desc);
1508 pa_xfree(cur_desc);
1509
1510 m->description = m->description ? m->description : pa_xstrdup("");
1511
1512 /* Modifier sinks should not be routed to by default */
1513 m->priority = 0;
1514
1515 mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
1516 pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
1517
1518 /* save mapping to ucm modifier */
1519 if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
1520 modifier->playback_mapping = m;
1521 channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
1522 } else {
1523 modifier->capture_mapping = m;
1524 channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
1525 }
1526
1527 if (channel_str) {
1528 /* FIXME: channel_str is unsanitized input from the UCM configuration,
1529 * we should do proper error handling instead of asserting.
1530 * https://bugs.freedesktop.org/show_bug.cgi?id=71823 */
1531 pa_assert_se(pa_atou(channel_str, &channels) == 0 && pa_channels_valid(channels));
1532 pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
1533 }
1534
1535 if (channels)
1536 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1537 else
1538 pa_channel_map_init(&m->channel_map);
1539 }
1540
ucm_create_mapping_direction(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_profile * p,pa_alsa_ucm_device * device,const char * verb_name,const char * device_name,const char * device_str,bool is_sink)1541 static int ucm_create_mapping_direction(
1542 pa_alsa_ucm_config *ucm,
1543 pa_alsa_profile_set *ps,
1544 pa_alsa_profile *p,
1545 pa_alsa_ucm_device *device,
1546 const char *verb_name,
1547 const char *device_name,
1548 const char *device_str,
1549 bool is_sink) {
1550
1551 pa_alsa_mapping *m;
1552 char *mapping_name;
1553 unsigned priority, rate, channels;
1554
1555 mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
1556
1557 m = pa_alsa_mapping_get(ps, mapping_name);
1558 if (!m) {
1559 pa_log("No mapping for %s", mapping_name);
1560 pa_xfree(mapping_name);
1561 return -1;
1562 }
1563 pa_log_debug("UCM mapping: %s dev %s", mapping_name, device_name);
1564 pa_xfree(mapping_name);
1565
1566 priority = is_sink ? device->playback_priority : device->capture_priority;
1567 rate = is_sink ? device->playback_rate : device->capture_rate;
1568 channels = is_sink ? device->playback_channels : device->capture_channels;
1569
1570 if (!m->ucm_context.ucm_devices) { /* new mapping */
1571 m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1572 m->ucm_context.ucm = ucm;
1573 m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1574
1575 m->device_strings = pa_xnew0(char*, 2);
1576 m->device_strings[0] = pa_xstrdup(device_str);
1577 m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1578
1579 ucm_add_mapping(p, m);
1580 if (rate)
1581 m->sample_spec.rate = rate;
1582 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1583 }
1584
1585 /* mapping priority is the highest one of ucm devices */
1586 if (priority > m->priority)
1587 m->priority = priority;
1588
1589 /* mapping channels is the lowest one of ucm devices */
1590 if (channels < m->channel_map.channels)
1591 pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
1592
1593 alsa_mapping_add_ucm_device(m, device);
1594
1595 return 0;
1596 }
1597
ucm_create_mapping_for_modifier(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_profile * p,pa_alsa_ucm_modifier * modifier,const char * verb_name,const char * mod_name,const char * device_str,bool is_sink)1598 static int ucm_create_mapping_for_modifier(
1599 pa_alsa_ucm_config *ucm,
1600 pa_alsa_profile_set *ps,
1601 pa_alsa_profile *p,
1602 pa_alsa_ucm_modifier *modifier,
1603 const char *verb_name,
1604 const char *mod_name,
1605 const char *device_str,
1606 bool is_sink) {
1607
1608 pa_alsa_mapping *m;
1609 char *mapping_name;
1610
1611 mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
1612
1613 m = pa_alsa_mapping_get(ps, mapping_name);
1614 if (!m) {
1615 pa_log("no mapping for %s", mapping_name);
1616 pa_xfree(mapping_name);
1617 return -1;
1618 }
1619 pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
1620 pa_xfree(mapping_name);
1621
1622 if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */
1623 m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1624 m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1625 m->ucm_context.ucm = ucm;
1626 m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
1627
1628 m->device_strings = pa_xnew0(char*, 2);
1629 m->device_strings[0] = pa_xstrdup(device_str);
1630 m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
1631 /* Modifier sinks should not be routed to by default */
1632 m->priority = 0;
1633
1634 ucm_add_mapping(p, m);
1635 } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
1636 m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1637
1638 alsa_mapping_add_ucm_modifier(m, modifier);
1639
1640 return 0;
1641 }
1642
ucm_create_mapping(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_profile * p,pa_alsa_ucm_device * device,const char * verb_name,const char * device_name,const char * sink,const char * source)1643 static int ucm_create_mapping(
1644 pa_alsa_ucm_config *ucm,
1645 pa_alsa_profile_set *ps,
1646 pa_alsa_profile *p,
1647 pa_alsa_ucm_device *device,
1648 const char *verb_name,
1649 const char *device_name,
1650 const char *sink,
1651 const char *source) {
1652
1653 int ret = 0;
1654
1655 if (!sink && !source) {
1656 pa_log("No sink and source at %s: %s", verb_name, device_name);
1657 return -1;
1658 }
1659
1660 if (sink)
1661 ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, sink, true);
1662 if (ret == 0 && source)
1663 ret = ucm_create_mapping_direction(ucm, ps, p, device, verb_name, device_name, source, false);
1664
1665 return ret;
1666 }
1667
ucm_get_jack(pa_alsa_ucm_config * ucm,pa_alsa_ucm_device * device)1668 static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device) {
1669 pa_alsa_jack *j;
1670 const char *device_name;
1671 const char *jack_control;
1672 const char *mixer_device_name;
1673 char *name;
1674
1675 pa_assert(ucm);
1676 pa_assert(device);
1677
1678 device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
1679
1680 jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
1681 if (jack_control) {
1682 #if SND_LIB_VERSION >= 0x10201
1683 snd_ctl_elem_id_t *ctl;
1684 int err, index;
1685 snd_ctl_elem_id_alloca(&ctl);
1686 err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control);
1687 if (err < 0)
1688 return NULL;
1689 jack_control = snd_ctl_elem_id_get_name(ctl);
1690 index = snd_ctl_elem_id_get_index(ctl);
1691 if (index > 0) {
1692 pa_log("[%s] Invalid JackControl index value: \"%s\",%d", device_name, jack_control, index);
1693 return NULL;
1694 }
1695 #else
1696 #warning "Upgrade to alsa-lib 1.2.1!"
1697 #endif
1698 if (!pa_endswith(jack_control, " Jack")) {
1699 pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control);
1700 return NULL;
1701 }
1702
1703 /* pa_alsa_jack_new() expects a jack name without " Jack" at the
1704 * end, so drop the trailing " Jack". */
1705 name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
1706 } else {
1707 /* The jack control hasn't been explicitly configured, fail. */
1708 return NULL;
1709 }
1710
1711 PA_LLIST_FOREACH(j, ucm->jacks)
1712 if (pa_streq(j->name, name))
1713 goto finish;
1714
1715 mixer_device_name = get_jack_mixer_device(device, true);
1716 if (!mixer_device_name)
1717 mixer_device_name = get_jack_mixer_device(device, false);
1718 if (!mixer_device_name) {
1719 pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control);
1720 return NULL;
1721 }
1722 j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0);
1723 PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
1724
1725 finish:
1726 pa_xfree(name);
1727
1728 return j;
1729 }
1730
ucm_create_profile(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps,pa_alsa_ucm_verb * verb,const char * verb_name,const char * verb_desc)1731 static int ucm_create_profile(
1732 pa_alsa_ucm_config *ucm,
1733 pa_alsa_profile_set *ps,
1734 pa_alsa_ucm_verb *verb,
1735 const char *verb_name,
1736 const char *verb_desc) {
1737
1738 pa_alsa_profile *p;
1739 pa_alsa_ucm_device *dev;
1740 pa_alsa_ucm_modifier *mod;
1741 int i = 0;
1742 const char *name, *sink, *source;
1743 unsigned int priority;
1744
1745 pa_assert(ps);
1746
1747 if (pa_hashmap_get(ps->profiles, verb_name)) {
1748 pa_log("Verb %s already exists", verb_name);
1749 return -1;
1750 }
1751
1752 p = pa_xnew0(pa_alsa_profile, 1);
1753 p->profile_set = ps;
1754 p->name = pa_xstrdup(verb_name);
1755 p->description = pa_xstrdup(verb_desc);
1756
1757 p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1758 p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1759
1760 p->supported = true;
1761 pa_hashmap_put(ps->profiles, p->name, p);
1762
1763 /* TODO: get profile priority from policy management */
1764 priority = verb->priority;
1765
1766 if (priority == 0) {
1767 char *verb_cmp, *c;
1768 c = verb_cmp = pa_xstrdup(verb_name);
1769 while (*c) {
1770 if (*c == '_') *c = ' ';
1771 c++;
1772 }
1773 for (i = 0; verb_info[i].id; i++) {
1774 if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
1775 priority = verb_info[i].priority;
1776 break;
1777 }
1778 }
1779 pa_xfree(verb_cmp);
1780 }
1781
1782 p->priority = priority;
1783
1784 PA_LLIST_FOREACH(dev, verb->devices) {
1785 pa_alsa_jack *jack;
1786 const char *jack_hw_mute;
1787
1788 name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
1789
1790 sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
1791 source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
1792
1793 ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
1794
1795 jack = ucm_get_jack(ucm, dev);
1796 if (jack)
1797 device_set_jack(dev, jack);
1798
1799 /* JackHWMute contains a list of device names. Each listed device must
1800 * be associated with the jack object that we just created. */
1801 jack_hw_mute = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE);
1802 if (jack_hw_mute && !jack) {
1803 pa_log("[%s] JackHWMute set, but JackControl is missing", name);
1804 jack_hw_mute = NULL;
1805 }
1806 if (jack_hw_mute) {
1807 char *hw_mute_device_name;
1808 const char *state = NULL;
1809
1810 while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, &state))) {
1811 pa_alsa_ucm_verb *verb2;
1812 bool device_found = false;
1813
1814 /* Search the referenced device from all verbs. If there are
1815 * multiple verbs that have a device with this name, we add the
1816 * hw mute association to each of those devices. */
1817 PA_LLIST_FOREACH(verb2, ucm->verbs) {
1818 pa_alsa_ucm_device *hw_mute_device;
1819
1820 hw_mute_device = verb_find_device(verb2, hw_mute_device_name);
1821 if (hw_mute_device) {
1822 device_found = true;
1823 device_add_hw_mute_jack(hw_mute_device, jack);
1824 }
1825 }
1826
1827 if (!device_found)
1828 pa_log("[%s] JackHWMute references an unknown device: %s", name, hw_mute_device_name);
1829
1830 pa_xfree(hw_mute_device_name);
1831 }
1832 }
1833 }
1834
1835 /* Now find modifiers that have their own PlaybackPCM and create
1836 * separate sinks for them. */
1837 PA_LLIST_FOREACH(mod, verb->modifiers) {
1838 name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
1839
1840 sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
1841 source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
1842
1843 if (sink)
1844 ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, true);
1845 else if (source)
1846 ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, false);
1847 }
1848
1849 pa_alsa_profile_dump(p);
1850
1851 return 0;
1852 }
1853
mapping_init_eld(pa_alsa_mapping * m,snd_pcm_t * pcm)1854 static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm)
1855 {
1856 pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1857 pa_alsa_ucm_device *dev;
1858 uint32_t idx;
1859 char *mdev;
1860 snd_pcm_info_t *info;
1861 int pcm_card, pcm_device;
1862
1863 snd_pcm_info_alloca(&info);
1864 if (snd_pcm_info(pcm, info) < 0)
1865 return;
1866
1867 if ((pcm_card = snd_pcm_info_get_card(info)) < 0)
1868 return;
1869 if ((pcm_device = snd_pcm_info_get_device(info)) < 0)
1870 return;
1871
1872 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1873 mdev = pa_sprintf_malloc("hw:%i", pcm_card);
1874 if (mdev == NULL)
1875 continue;
1876 dev->eld_mixer_device_name = mdev;
1877 dev->eld_device = pcm_device;
1878 }
1879 }
1880
mapping_open_pcm(pa_alsa_ucm_config * ucm,pa_alsa_mapping * m,int mode)1881 static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
1882 snd_pcm_t* pcm;
1883 pa_sample_spec try_ss = ucm->core->default_sample_spec;
1884 pa_channel_map try_map;
1885 snd_pcm_uframes_t try_period_size, try_buffer_size;
1886 bool exact_channels = m->channel_map.channels > 0;
1887
1888 if (exact_channels) {
1889 try_map = m->channel_map;
1890 try_ss.channels = try_map.channels;
1891 } else
1892 pa_channel_map_init_extend(&try_map, try_ss.channels, PA_CHANNEL_MAP_ALSA);
1893
1894 try_period_size =
1895 pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
1896 pa_frame_size(&try_ss);
1897 try_buffer_size = ucm->core->default_n_fragments * try_period_size;
1898
1899 pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
1900 &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
1901
1902 if (pcm) {
1903 if (!exact_channels)
1904 m->channel_map = try_map;
1905 mapping_init_eld(m, pcm);
1906 }
1907
1908 return pcm;
1909 }
1910
profile_finalize_probing(pa_alsa_profile * p)1911 static void profile_finalize_probing(pa_alsa_profile *p) {
1912 pa_alsa_mapping *m;
1913 uint32_t idx;
1914
1915 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
1916 if (p->supported)
1917 m->supported++;
1918
1919 if (!m->output_pcm)
1920 continue;
1921
1922 snd_pcm_close(m->output_pcm);
1923 m->output_pcm = NULL;
1924 }
1925
1926 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
1927 if (p->supported)
1928 m->supported++;
1929
1930 if (!m->input_pcm)
1931 continue;
1932
1933 snd_pcm_close(m->input_pcm);
1934 m->input_pcm = NULL;
1935 }
1936 }
1937
ucm_mapping_jack_probe(pa_alsa_mapping * m,pa_hashmap * mixers)1938 static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
1939 snd_mixer_t *mixer_handle;
1940 pa_alsa_ucm_mapping_context *context = &m->ucm_context;
1941 pa_alsa_ucm_device *dev;
1942 uint32_t idx;
1943
1944 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
1945 bool has_control;
1946
1947 if (!dev->jack || !dev->jack->mixer_device_name)
1948 continue;
1949
1950 mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
1951 if (!mixer_handle) {
1952 pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
1953 continue;
1954 }
1955
1956 has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
1957 pa_alsa_jack_set_has_control(dev->jack, has_control);
1958 pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
1959 }
1960 }
1961
ucm_probe_profile_set(pa_alsa_ucm_config * ucm,pa_alsa_profile_set * ps)1962 static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
1963 void *state;
1964 pa_alsa_profile *p;
1965 pa_alsa_mapping *m;
1966 uint32_t idx;
1967
1968 PA_HASHMAP_FOREACH(p, ps->profiles, state) {
1969 /* change verb */
1970 pa_log_info("Set ucm verb to %s", p->name);
1971
1972 if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
1973 pa_log("Failed to set verb %s", p->name);
1974 p->supported = false;
1975 continue;
1976 }
1977
1978 PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
1979 if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
1980 /* Skip jack probing on modifier PCMs since we expect this to
1981 * only be controlled on the main device/verb PCM. */
1982 continue;
1983 }
1984
1985 m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
1986 if (!m->output_pcm) {
1987 p->supported = false;
1988 break;
1989 }
1990 }
1991
1992 if (p->supported) {
1993 PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
1994 if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
1995 /* Skip jack probing on modifier PCMs since we expect this to
1996 * only be controlled on the main device/verb PCM. */
1997 continue;
1998 }
1999
2000 m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
2001 if (!m->input_pcm) {
2002 p->supported = false;
2003 break;
2004 }
2005 }
2006 }
2007
2008 if (!p->supported) {
2009 profile_finalize_probing(p);
2010 continue;
2011 }
2012
2013 pa_log_debug("Profile %s supported.", p->name);
2014
2015 PA_IDXSET_FOREACH(m, p->output_mappings, idx)
2016 if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2017 ucm_mapping_jack_probe(m, ucm->mixers);
2018
2019 PA_IDXSET_FOREACH(m, p->input_mappings, idx)
2020 if (!PA_UCM_IS_MODIFIER_MAPPING(m))
2021 ucm_mapping_jack_probe(m, ucm->mixers);
2022
2023 profile_finalize_probing(p);
2024 }
2025
2026 /* restore ucm state */
2027 snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
2028
2029 pa_alsa_profile_set_drop_unsupported(ps);
2030 }
2031
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2032 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2033 pa_alsa_ucm_verb *verb;
2034 pa_alsa_profile_set *ps;
2035
2036 ps = pa_xnew0(pa_alsa_profile_set, 1);
2037 ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2038 ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2039 ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2040
2041 /* create a profile for each verb */
2042 PA_LLIST_FOREACH(verb, ucm->verbs) {
2043 const char *verb_name;
2044 const char *verb_desc;
2045
2046 verb_name = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
2047 verb_desc = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
2048 if (verb_name == NULL) {
2049 pa_log("Verb with no name");
2050 continue;
2051 }
2052
2053 ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
2054 }
2055
2056 ucm_probe_profile_set(ucm, ps);
2057 ps->probed = true;
2058
2059 return ps;
2060 }
2061
free_verb(pa_alsa_ucm_verb * verb)2062 static void free_verb(pa_alsa_ucm_verb *verb) {
2063 pa_alsa_ucm_device *di, *dn;
2064 pa_alsa_ucm_modifier *mi, *mn;
2065
2066 PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
2067 PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
2068
2069 if (di->hw_mute_jacks)
2070 pa_dynarray_free(di->hw_mute_jacks);
2071
2072 if (di->ucm_ports)
2073 pa_dynarray_free(di->ucm_ports);
2074
2075 if (di->playback_volumes)
2076 pa_hashmap_free(di->playback_volumes);
2077 if (di->capture_volumes)
2078 pa_hashmap_free(di->capture_volumes);
2079
2080 pa_proplist_free(di->proplist);
2081
2082 if (di->conflicting_devices)
2083 pa_idxset_free(di->conflicting_devices, NULL);
2084 if (di->supported_devices)
2085 pa_idxset_free(di->supported_devices, NULL);
2086
2087 pa_xfree(di->eld_mixer_device_name);
2088
2089 pa_xfree(di);
2090 }
2091
2092 PA_LLIST_FOREACH_SAFE(mi, mn, verb->modifiers) {
2093 PA_LLIST_REMOVE(pa_alsa_ucm_modifier, verb->modifiers, mi);
2094 pa_proplist_free(mi->proplist);
2095 if (mi->n_suppdev > 0)
2096 snd_use_case_free_list(mi->supported_devices, mi->n_suppdev);
2097 if (mi->n_confdev > 0)
2098 snd_use_case_free_list(mi->conflicting_devices, mi->n_confdev);
2099 pa_xfree(mi->media_role);
2100 pa_xfree(mi);
2101 }
2102 pa_proplist_free(verb->proplist);
2103 pa_xfree(verb);
2104 }
2105
verb_find_device(pa_alsa_ucm_verb * verb,const char * device_name)2106 static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name) {
2107 pa_alsa_ucm_device *device;
2108
2109 pa_assert(verb);
2110 pa_assert(device_name);
2111
2112 PA_LLIST_FOREACH(device, verb->devices) {
2113 const char *name;
2114
2115 name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
2116 if (pa_streq(name, device_name))
2117 return device;
2118 }
2119
2120 return NULL;
2121 }
2122
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2123 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2124 pa_alsa_ucm_verb *vi, *vn;
2125 pa_alsa_jack *ji, *jn;
2126
2127 PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
2128 PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
2129 free_verb(vi);
2130 }
2131 PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
2132 PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
2133 pa_alsa_jack_free(ji);
2134 }
2135 if (ucm->ucm_mgr) {
2136 snd_use_case_mgr_close(ucm->ucm_mgr);
2137 ucm->ucm_mgr = NULL;
2138 }
2139 }
2140
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2141 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2142 pa_alsa_ucm_device *dev;
2143 pa_alsa_ucm_modifier *mod;
2144 uint32_t idx;
2145
2146 if (context->ucm_devices) {
2147 /* clear ucm device pointer to mapping */
2148 PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
2149 if (context->direction == PA_DIRECTION_OUTPUT)
2150 dev->playback_mapping = NULL;
2151 else
2152 dev->capture_mapping = NULL;
2153 }
2154
2155 pa_idxset_free(context->ucm_devices, NULL);
2156 }
2157
2158 if (context->ucm_modifiers) {
2159 PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
2160 if (context->direction == PA_DIRECTION_OUTPUT)
2161 mod->playback_mapping = NULL;
2162 else
2163 mod->capture_mapping = NULL;
2164 }
2165
2166 pa_idxset_free(context->ucm_modifiers, NULL);
2167 }
2168 }
2169
2170 /* Enable the modifier when the first stream with matched role starts */
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2171 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2172 pa_alsa_ucm_modifier *mod;
2173
2174 if (!ucm->active_verb)
2175 return;
2176
2177 PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2178 if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2179 if (mod->enabled_counter == 0) {
2180 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
2181
2182 pa_log_info("Enable ucm modifier %s", mod_name);
2183 if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) {
2184 pa_log("Failed to enable ucm modifier %s", mod_name);
2185 }
2186 }
2187
2188 mod->enabled_counter++;
2189 break;
2190 }
2191 }
2192 }
2193
2194 /* Disable the modifier when the last stream with matched role ends */
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2195 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2196 pa_alsa_ucm_modifier *mod;
2197
2198 if (!ucm->active_verb)
2199 return;
2200
2201 PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) {
2202 if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) {
2203
2204 mod->enabled_counter--;
2205 if (mod->enabled_counter == 0) {
2206 const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
2207
2208 pa_log_info("Disable ucm modifier %s", mod_name);
2209 if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) {
2210 pa_log("Failed to disable ucm modifier %s", mod_name);
2211 }
2212 }
2213
2214 break;
2215 }
2216 }
2217 }
2218
device_add_ucm_port(pa_alsa_ucm_device * device,pa_alsa_ucm_port_data * port)2219 static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) {
2220 pa_assert(device);
2221 pa_assert(port);
2222
2223 pa_dynarray_append(device->ucm_ports, port);
2224 }
2225
device_set_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2226 static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2227 pa_assert(device);
2228 pa_assert(jack);
2229
2230 device->jack = jack;
2231 pa_alsa_jack_add_ucm_device(jack, device);
2232
2233 pa_alsa_ucm_device_update_available(device);
2234 }
2235
device_add_hw_mute_jack(pa_alsa_ucm_device * device,pa_alsa_jack * jack)2236 static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
2237 pa_assert(device);
2238 pa_assert(jack);
2239
2240 pa_dynarray_append(device->hw_mute_jacks, jack);
2241 pa_alsa_jack_add_ucm_hw_mute_device(jack, device);
2242
2243 pa_alsa_ucm_device_update_available(device);
2244 }
2245
device_set_available(pa_alsa_ucm_device * device,pa_available_t available)2246 static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) {
2247 pa_alsa_ucm_port_data *port;
2248 unsigned idx;
2249
2250 pa_assert(device);
2251
2252 if (available == device->available)
2253 return;
2254
2255 device->available = available;
2256
2257 PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx)
2258 ucm_port_update_available(port);
2259 }
2260
pa_alsa_ucm_device_update_available(pa_alsa_ucm_device * device)2261 void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
2262 pa_available_t available = PA_AVAILABLE_UNKNOWN;
2263 pa_alsa_jack *jack;
2264 unsigned idx;
2265
2266 pa_assert(device);
2267
2268 if (device->jack && device->jack->has_control)
2269 available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO;
2270
2271 PA_DYNARRAY_FOREACH(jack, device->hw_mute_jacks, idx) {
2272 if (jack->plugged_in) {
2273 available = PA_AVAILABLE_NO;
2274 break;
2275 }
2276 }
2277
2278 device_set_available(device, available);
2279 }
2280
ucm_port_data_init(pa_alsa_ucm_port_data * port,pa_alsa_ucm_config * ucm,pa_device_port * core_port,pa_alsa_ucm_device ** devices,unsigned n_devices)2281 static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
2282 pa_alsa_ucm_device **devices, unsigned n_devices) {
2283 unsigned i;
2284
2285 pa_assert(ucm);
2286 pa_assert(core_port);
2287 pa_assert(devices);
2288
2289 port->ucm = ucm;
2290 port->core_port = core_port;
2291 port->devices = pa_dynarray_new(NULL);
2292 port->eld_device = -1;
2293
2294 for (i = 0; i < n_devices; i++) {
2295 pa_dynarray_append(port->devices, devices[i]);
2296 device_add_ucm_port(devices[i], port);
2297 }
2298
2299 port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
2300 (pa_free_cb_t) pa_alsa_path_free);
2301
2302 ucm_port_update_available(port);
2303 }
2304
ucm_port_data_free(pa_device_port * port)2305 static void ucm_port_data_free(pa_device_port *port) {
2306 pa_alsa_ucm_port_data *ucm_port;
2307
2308 pa_assert(port);
2309
2310 ucm_port = PA_DEVICE_PORT_DATA(port);
2311
2312 if (ucm_port->devices)
2313 pa_dynarray_free(ucm_port->devices);
2314
2315 if (ucm_port->paths)
2316 pa_hashmap_free(ucm_port->paths);
2317
2318 pa_xfree(ucm_port->eld_mixer_device_name);
2319 }
2320
ucm_port_update_available(pa_alsa_ucm_port_data * port)2321 static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
2322 pa_alsa_ucm_device *device;
2323 unsigned idx;
2324 pa_available_t available = PA_AVAILABLE_YES;
2325
2326 pa_assert(port);
2327
2328 PA_DYNARRAY_FOREACH(device, port->devices, idx) {
2329 if (device->available == PA_AVAILABLE_UNKNOWN)
2330 available = PA_AVAILABLE_UNKNOWN;
2331 else if (device->available == PA_AVAILABLE_NO) {
2332 available = PA_AVAILABLE_NO;
2333 break;
2334 }
2335 }
2336
2337 pa_device_port_set_available(port->core_port, available);
2338 }
2339
2340 #else /* HAVE_ALSA_UCM */
2341
2342 /* Dummy functions for systems without UCM support */
2343
pa_alsa_ucm_query_profiles(pa_alsa_ucm_config * ucm,int card_index)2344 int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
2345 pa_log_info("UCM not available.");
2346 return -1;
2347 }
2348
pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config * ucm,pa_channel_map * default_channel_map)2349 pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
2350 return NULL;
2351 }
2352
pa_alsa_ucm_set_profile(pa_alsa_ucm_config * ucm,pa_card * card,const char * new_profile,const char * old_profile)2353 int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
2354 return -1;
2355 }
2356
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)2357 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) {
2358 return -1;
2359 }
2360
pa_alsa_ucm_add_ports(pa_hashmap ** hash,pa_proplist * proplist,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_card * card,snd_pcm_t * pcm_handle,bool ignore_dB)2361 void pa_alsa_ucm_add_ports(
2362 pa_hashmap **hash,
2363 pa_proplist *proplist,
2364 pa_alsa_ucm_mapping_context *context,
2365 bool is_sink,
2366 pa_card *card,
2367 snd_pcm_t *pcm_handle,
2368 bool ignore_dB) {
2369 }
2370
pa_alsa_ucm_add_ports_combination(pa_hashmap * hash,pa_alsa_ucm_mapping_context * context,bool is_sink,pa_hashmap * ports,pa_card_profile * cp,pa_core * core)2371 void pa_alsa_ucm_add_ports_combination(
2372 pa_hashmap *hash,
2373 pa_alsa_ucm_mapping_context *context,
2374 bool is_sink,
2375 pa_hashmap *ports,
2376 pa_card_profile *cp,
2377 pa_core *core) {
2378 }
2379
pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context * context,pa_device_port * port,bool is_sink)2380 int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink) {
2381 return -1;
2382 }
2383
pa_alsa_ucm_free(pa_alsa_ucm_config * ucm)2384 void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
2385 }
2386
pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context * context)2387 void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
2388 }
2389
pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2390 void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2391 }
2392
pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config * ucm,const char * role,pa_direction_t dir)2393 void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
2394 }
2395
2396 #endif
2397