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