2 This file is part of PulseAudio.
4 Copyright 2004-2009 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/types.h>
29 #include <asoundlib.h>
31 #ifdef HAVE_VALGRIND_MEMCHECK_H
32 #include <valgrind/memcheck.h>
35 #include <pulse/sample.h>
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/i18n.h>
40 #include <pulse/utf8.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/atomic.h>
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/once.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/conf-parser.h>
50 #include <pulsecore/strbuf.h>
52 #include "alsa-mixer.h"
53 #include "alsa-util.h"
55 struct description_map
{
57 const char *description
;
60 static const char *lookup_description(const char *name
, const struct description_map dm
[], unsigned n
) {
63 for (i
= 0; i
< n
; i
++)
64 if (pa_streq(dm
[i
].name
, name
))
65 return dm
[i
].description
;
70 struct pa_alsa_fdlist
{
73 /* This is a temporary buffer used to avoid lots of mallocs */
74 struct pollfd
*work_fds
;
79 pa_defer_event
*defer
;
84 void (*cb
)(void *userdata
);
88 static void io_cb(pa_mainloop_api
*a
, pa_io_event
* e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
90 struct pa_alsa_fdlist
*fdl
= userdata
;
93 unsigned short revents
;
97 pa_assert(fdl
->mixer
);
99 pa_assert(fdl
->work_fds
);
106 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
108 for (i
= 0; i
< fdl
->num_fds
; i
++) {
109 if (e
== fdl
->ios
[i
]) {
110 if (events
& PA_IO_EVENT_INPUT
)
111 fdl
->work_fds
[i
].revents
|= POLLIN
;
112 if (events
& PA_IO_EVENT_OUTPUT
)
113 fdl
->work_fds
[i
].revents
|= POLLOUT
;
114 if (events
& PA_IO_EVENT_ERROR
)
115 fdl
->work_fds
[i
].revents
|= POLLERR
;
116 if (events
& PA_IO_EVENT_HANGUP
)
117 fdl
->work_fds
[i
].revents
|= POLLHUP
;
122 pa_assert(i
!= fdl
->num_fds
);
124 if ((err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
)) < 0) {
125 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
129 a
->defer_enable(fdl
->defer
, 1);
132 snd_mixer_handle_events(fdl
->mixer
);
135 static void defer_cb(pa_mainloop_api
*a
, pa_defer_event
* e
, void *userdata
) {
136 struct pa_alsa_fdlist
*fdl
= userdata
;
143 pa_assert(fdl
->mixer
);
145 a
->defer_enable(fdl
->defer
, 0);
147 if ((n
= snd_mixer_poll_descriptors_count(fdl
->mixer
)) < 0) {
148 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
151 num_fds
= (unsigned) n
;
153 if (num_fds
!= fdl
->num_fds
) {
157 pa_xfree(fdl
->work_fds
);
158 fdl
->fds
= pa_xnew0(struct pollfd
, num_fds
);
159 fdl
->work_fds
= pa_xnew(struct pollfd
, num_fds
);
162 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
164 if ((err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
)) < 0) {
165 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
171 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
175 for (i
= 0; i
< fdl
->num_fds
; i
++)
176 a
->io_free(fdl
->ios
[i
]);
178 if (num_fds
!= fdl
->num_fds
) {
185 fdl
->ios
= pa_xnew(pa_io_event
*, num_fds
);
188 temp
= fdl
->work_fds
;
189 fdl
->work_fds
= fdl
->fds
;
192 fdl
->num_fds
= num_fds
;
194 for (i
= 0;i
< num_fds
;i
++)
195 fdl
->ios
[i
] = a
->io_new(a
, fdl
->fds
[i
].fd
,
196 ((fdl
->fds
[i
].events
& POLLIN
) ? PA_IO_EVENT_INPUT
: 0) |
197 ((fdl
->fds
[i
].events
& POLLOUT
) ? PA_IO_EVENT_OUTPUT
: 0),
201 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
202 struct pa_alsa_fdlist
*fdl
;
204 fdl
= pa_xnew0(struct pa_alsa_fdlist
, 1);
209 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
214 fdl
->m
->defer_free(fdl
->defer
);
220 for (i
= 0; i
< fdl
->num_fds
; i
++)
221 fdl
->m
->io_free(fdl
->ios
[i
]);
228 pa_xfree(fdl
->work_fds
);
233 int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist
*fdl
, snd_mixer_t
*mixer_handle
, pa_mainloop_api
* m
) {
235 pa_assert(mixer_handle
);
239 fdl
->mixer
= mixer_handle
;
241 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
246 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
252 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
253 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
257 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
258 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
262 if ((err
= snd_mixer_load(mixer
)) < 0) {
263 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
267 pa_log_info("Successfully attached to mixer '%s'", dev
);
271 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
275 snd_pcm_info_t
* info
;
276 snd_pcm_info_alloca(&info
);
280 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
281 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
285 /* First, try by name */
286 if ((dev
= snd_pcm_name(pcm
)))
287 if (prepare_mixer(m
, dev
) >= 0) {
289 *ctl_device
= pa_xstrdup(dev
);
294 /* Then, try by card index */
295 if (snd_pcm_info(pcm
, info
) >= 0) {
299 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
301 md
= pa_sprintf_malloc("hw:%i", card_idx
);
303 if (!dev
|| !pa_streq(dev
, md
))
304 if (prepare_mixer(m
, md
) >= 0) {
322 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
323 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
325 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
326 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
327 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
329 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
330 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
331 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
333 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
335 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
336 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
338 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
339 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
341 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
342 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
343 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
344 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
345 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
346 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
347 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
348 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
349 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
350 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
351 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
352 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
353 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
354 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
355 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
356 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
357 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
358 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
359 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
360 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
361 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
362 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
363 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
364 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
365 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
366 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
367 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
368 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
369 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
370 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
371 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
372 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
374 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
376 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
377 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
378 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
380 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
381 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
382 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
385 static void setting_free(pa_alsa_setting
*s
) {
389 pa_idxset_free(s
->options
, NULL
, NULL
);
392 pa_xfree(s
->description
);
396 static void option_free(pa_alsa_option
*o
) {
399 pa_xfree(o
->alsa_name
);
401 pa_xfree(o
->description
);
405 static void element_free(pa_alsa_element
*e
) {
409 while ((o
= e
->options
)) {
410 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
414 pa_xfree(e
->alsa_name
);
418 void pa_alsa_path_free(pa_alsa_path
*p
) {
424 while ((e
= p
->elements
)) {
425 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
429 while ((s
= p
->settings
)) {
430 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
435 pa_xfree(p
->description
);
439 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
443 while ((p
= ps
->paths
)) {
444 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
445 pa_alsa_path_free(p
);
451 static long to_alsa_dB(pa_volume_t v
) {
452 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
455 static pa_volume_t
from_alsa_dB(long v
) {
456 return pa_sw_volume_from_dB((double) v
/ 100.0);
459 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
462 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
463 return PA_CLAMP_UNLIKELY(w
, min
, max
);
466 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
467 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
470 #define SELEM_INIT(sid, name) \
472 snd_mixer_selem_id_alloca(&(sid)); \
473 snd_mixer_selem_id_set_name((sid), (name)); \
474 snd_mixer_selem_id_set_index((sid), 0); \
477 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
478 snd_mixer_selem_id_t
*sid
;
479 snd_mixer_elem_t
*me
;
480 snd_mixer_selem_channel_id_t c
;
481 pa_channel_position_mask_t mask
= 0;
489 SELEM_INIT(sid
, e
->alsa_name
);
490 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
491 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
495 pa_cvolume_mute(v
, cm
->channels
);
497 /* We take the highest volume of all channels that match */
499 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
506 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
507 if (snd_mixer_selem_has_playback_channel(me
, c
))
508 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
512 if (snd_mixer_selem_has_capture_channel(me
, c
))
513 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
521 #ifdef HAVE_VALGRIND_MEMCHECK_H
522 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
525 f
= from_alsa_dB(value
);
530 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
531 if (snd_mixer_selem_has_playback_channel(me
, c
))
532 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
536 if (snd_mixer_selem_has_capture_channel(me
, c
))
537 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
545 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
548 for (k
= 0; k
< cm
->channels
; k
++)
549 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
550 if (v
->values
[k
] < f
)
553 mask
|= e
->masks
[c
][e
->n_channels
-1];
556 for (k
= 0; k
< cm
->channels
; k
++)
557 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
558 v
->values
[k
] = PA_VOLUME_NORM
;
563 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
574 pa_cvolume_reset(v
, cm
->channels
);
576 PA_LLIST_FOREACH(e
, p
->elements
) {
579 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
582 pa_assert(!p
->has_dB
|| e
->has_dB
);
584 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
587 /* If we have no dB information all we can do is take the first element and leave */
593 pa_sw_cvolume_multiply(v
, v
, &ev
);
599 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
600 snd_mixer_selem_id_t
*sid
;
601 snd_mixer_elem_t
*me
;
602 snd_mixer_selem_channel_id_t c
;
608 SELEM_INIT(sid
, e
->alsa_name
);
609 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
610 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
614 /* We return muted if at least one channel is muted */
616 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
620 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
621 if (snd_mixer_selem_has_playback_channel(me
, c
))
622 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
626 if (snd_mixer_selem_has_capture_channel(me
, c
))
627 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
645 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
655 PA_LLIST_FOREACH(e
, p
->elements
) {
658 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
661 if (element_get_switch(e
, m
, &b
) < 0)
674 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
675 snd_mixer_selem_id_t
*sid
;
677 snd_mixer_elem_t
*me
;
678 snd_mixer_selem_channel_id_t c
;
679 pa_channel_position_mask_t mask
= 0;
686 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
688 SELEM_INIT(sid
, e
->alsa_name
);
689 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
690 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
694 pa_cvolume_mute(&rv
, cm
->channels
);
696 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
698 pa_volume_t f
= PA_VOLUME_MUTED
;
699 pa_bool_t found
= FALSE
;
701 for (k
= 0; k
< cm
->channels
; k
++)
702 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
704 if (v
->values
[k
] > f
)
709 /* Hmm, so this channel does not exist in the volume
710 * struct, so let's bind it to the overall max of the
712 f
= pa_cvolume_max(v
);
716 long value
= to_alsa_dB(f
);
718 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
719 /* If we call set_play_volume() without checking first
720 * if the channel is available, ALSA behaves ver
721 * strangely and doesn't fail the call */
722 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
723 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, +1)) >= 0)
724 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
728 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
729 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, +1)) >= 0)
730 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
738 #ifdef HAVE_VALGRIND_MEMCHECK_H
739 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
742 f
= from_alsa_dB(value
);
747 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
749 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
750 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
751 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
752 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
756 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
757 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
758 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
766 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
769 for (k
= 0; k
< cm
->channels
; k
++)
770 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
771 if (rv
.values
[k
] < f
)
774 mask
|= e
->masks
[c
][e
->n_channels
-1];
777 for (k
= 0; k
< cm
->channels
; k
++)
778 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
779 rv
.values
[k
] = PA_VOLUME_NORM
;
785 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
793 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
798 rv
= *v
; /* Remaining adjustment */
799 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
801 PA_LLIST_FOREACH(e
, p
->elements
) {
804 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
807 pa_assert(!p
->has_dB
|| e
->has_dB
);
810 if (element_set_volume(e
, m
, cm
, &ev
) < 0)
818 pa_sw_cvolume_multiply(v
, v
, &ev
);
819 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
825 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
826 snd_mixer_elem_t
*me
;
827 snd_mixer_selem_id_t
*sid
;
833 SELEM_INIT(sid
, e
->alsa_name
);
834 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
835 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
839 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
840 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
842 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
845 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
850 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
859 PA_LLIST_FOREACH(e
, p
->elements
) {
861 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
864 if (element_set_switch(e
, m
, !muted
) < 0)
871 static int element_mute_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
872 snd_mixer_elem_t
*me
;
873 snd_mixer_selem_id_t
*sid
;
879 SELEM_INIT(sid
, e
->alsa_name
);
880 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
881 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
885 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
886 r
= snd_mixer_selem_set_playback_volume_all(me
, e
->min_volume
);
888 r
= snd_mixer_selem_set_capture_volume_all(me
, e
->min_volume
);
891 pa_log_warn("Faile to set volume to muted of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
896 /* The volume to 0dB */
897 static int element_zero_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
898 snd_mixer_elem_t
*me
;
899 snd_mixer_selem_id_t
*sid
;
905 SELEM_INIT(sid
, e
->alsa_name
);
906 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
907 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
911 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
912 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
914 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
917 pa_log_warn("Faile to set volume to 0dB of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
922 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
929 pa_log_debug("Activating path %s", p
->name
);
930 pa_alsa_path_dump(p
);
932 PA_LLIST_FOREACH(e
, p
->elements
) {
934 switch (e
->switch_use
) {
935 case PA_ALSA_SWITCH_OFF
:
936 r
= element_set_switch(e
, m
, FALSE
);
939 case PA_ALSA_SWITCH_ON
:
940 r
= element_set_switch(e
, m
, TRUE
);
943 case PA_ALSA_SWITCH_MUTE
:
944 case PA_ALSA_SWITCH_IGNORE
:
945 case PA_ALSA_SWITCH_SELECT
:
953 switch (e
->volume_use
) {
954 case PA_ALSA_VOLUME_OFF
:
955 r
= element_mute_volume(e
, m
);
958 case PA_ALSA_VOLUME_ZERO
:
959 r
= element_zero_volume(e
, m
);
962 case PA_ALSA_VOLUME_MERGE
:
963 case PA_ALSA_VOLUME_IGNORE
:
975 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
976 pa_bool_t has_switch
;
977 pa_bool_t has_enumeration
;
978 pa_bool_t has_volume
;
983 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
985 snd_mixer_selem_has_playback_switch(me
) ||
986 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
989 snd_mixer_selem_has_capture_switch(me
) ||
990 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
993 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
995 snd_mixer_selem_has_playback_volume(me
) ||
996 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
999 snd_mixer_selem_has_capture_volume(me
) ||
1000 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1003 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1005 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1006 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1007 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1010 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1013 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1014 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1015 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1018 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1024 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1025 snd_mixer_selem_id_t
*sid
;
1026 snd_mixer_elem_t
*me
;
1031 SELEM_INIT(sid
, e
->alsa_name
);
1033 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1035 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1038 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1039 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1040 e
->enumeration_use
= PA_ALSA_VOLUME_IGNORE
;
1045 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1046 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1048 if (!snd_mixer_selem_has_playback_switch(me
)) {
1049 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1050 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1052 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1057 if (!snd_mixer_selem_has_capture_switch(me
)) {
1058 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1059 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1061 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1065 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1066 e
->direction_try_other
= FALSE
;
1069 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1071 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1073 if (!snd_mixer_selem_has_playback_volume(me
)) {
1074 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1075 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1077 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1082 if (!snd_mixer_selem_has_capture_volume(me
)) {
1083 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1084 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1086 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1090 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1091 long min_dB
= 0, max_dB
= 0;
1094 e
->direction_try_other
= FALSE
;
1096 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1097 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1099 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1102 #ifdef HAVE_VALGRIND_MEMCHECK_H
1103 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1104 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1107 e
->min_dB
= ((double) min_dB
) / 100.0;
1108 e
->max_dB
= ((double) max_dB
) / 100.0;
1110 if (min_dB
>= max_dB
) {
1111 pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e
->min_dB
, e
->max_dB
);
1116 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1117 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1119 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1122 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1127 if (e
->min_volume
>= e
->max_volume
) {
1128 pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e
->min_volume
, e
->max_volume
);
1129 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1133 pa_channel_position_t p
;
1135 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1136 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1138 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1143 if (!e
->override_map
) {
1144 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1145 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1146 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1149 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1152 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1154 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1157 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1158 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1160 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1163 if (e
->n_channels
<= 0) {
1164 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1168 if (!e
->override_map
) {
1169 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1170 pa_bool_t has_channel
;
1172 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1175 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1176 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1178 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1180 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1185 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1186 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1193 if (check_required(e
, me
) < 0)
1196 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1199 PA_LLIST_FOREACH(o
, e
->options
)
1200 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1201 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1205 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1206 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1210 PA_LLIST_FOREACH(o
, e
->options
) {
1213 for (i
= 0; i
< n
; i
++) {
1216 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1219 if (!pa_streq(buf
, o
->alsa_name
))
1230 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1237 if (!pa_startswith(section
, "Element "))
1243 /* This is not an element section, but an enum section? */
1244 if (strchr(section
, ':'))
1247 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1248 return p
->last_element
;
1250 PA_LLIST_FOREACH(e
, p
->elements
)
1251 if (pa_streq(e
->alsa_name
, section
))
1254 e
= pa_xnew0(pa_alsa_element
, 1);
1256 e
->alsa_name
= pa_xstrdup(section
);
1257 e
->direction
= p
->direction
;
1259 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1262 p
->last_element
= e
;
1266 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1272 if (!pa_startswith(section
, "Option "))
1277 /* This is not an enum section, but an element section? */
1278 if (!(on
= strchr(section
, ':')))
1281 en
= pa_xstrndup(section
, on
- section
);
1284 if (p
->last_option
&&
1285 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1286 pa_streq(p
->last_option
->alsa_name
, on
)) {
1288 return p
->last_option
;
1291 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1294 PA_LLIST_FOREACH(o
, e
->options
)
1295 if (pa_streq(o
->alsa_name
, on
))
1298 o
= pa_xnew0(pa_alsa_option
, 1);
1300 o
->alsa_name
= pa_xstrdup(on
);
1303 if (p
->last_option
&& p
->last_option
->element
== e
)
1304 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1306 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1313 static int element_parse_switch(
1314 const char *filename
,
1316 const char *section
,
1322 pa_alsa_path
*p
= userdata
;
1327 if (!(e
= element_get(p
, section
, TRUE
))) {
1328 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1332 if (pa_streq(rvalue
, "ignore"))
1333 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1334 else if (pa_streq(rvalue
, "mute"))
1335 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1336 else if (pa_streq(rvalue
, "off"))
1337 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1338 else if (pa_streq(rvalue
, "on"))
1339 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1340 else if (pa_streq(rvalue
, "select"))
1341 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1343 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1350 static int element_parse_volume(
1351 const char *filename
,
1353 const char *section
,
1359 pa_alsa_path
*p
= userdata
;
1364 if (!(e
= element_get(p
, section
, TRUE
))) {
1365 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1369 if (pa_streq(rvalue
, "ignore"))
1370 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1371 else if (pa_streq(rvalue
, "merge"))
1372 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1373 else if (pa_streq(rvalue
, "off"))
1374 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1375 else if (pa_streq(rvalue
, "zero"))
1376 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1378 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1385 static int element_parse_enumeration(
1386 const char *filename
,
1388 const char *section
,
1394 pa_alsa_path
*p
= userdata
;
1399 if (!(e
= element_get(p
, section
, TRUE
))) {
1400 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1404 if (pa_streq(rvalue
, "ignore"))
1405 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1406 else if (pa_streq(rvalue
, "select"))
1407 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1409 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1416 static int option_parse_priority(
1417 const char *filename
,
1419 const char *section
,
1425 pa_alsa_path
*p
= userdata
;
1431 if (!(o
= option_get(p
, section
))) {
1432 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1436 if (pa_atou(rvalue
, &prio
) < 0) {
1437 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1445 static int option_parse_name(
1446 const char *filename
,
1448 const char *section
,
1454 pa_alsa_path
*p
= userdata
;
1459 if (!(o
= option_get(p
, section
))) {
1460 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1465 o
->name
= pa_xstrdup(rvalue
);
1470 static int element_parse_required(
1471 const char *filename
,
1473 const char *section
,
1479 pa_alsa_path
*p
= userdata
;
1481 pa_alsa_required_t req
;
1485 if (!(e
= element_get(p
, section
, TRUE
))) {
1486 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1490 if (pa_streq(rvalue
, "ignore"))
1491 req
= PA_ALSA_REQUIRED_IGNORE
;
1492 else if (pa_streq(rvalue
, "switch"))
1493 req
= PA_ALSA_REQUIRED_SWITCH
;
1494 else if (pa_streq(rvalue
, "volume"))
1495 req
= PA_ALSA_REQUIRED_VOLUME
;
1496 else if (pa_streq(rvalue
, "enumeration"))
1497 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1498 else if (pa_streq(rvalue
, "any"))
1499 req
= PA_ALSA_REQUIRED_ANY
;
1501 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1505 if (pa_streq(lvalue
, "required-absent"))
1506 e
->required_absent
= req
;
1513 static int element_parse_direction(
1514 const char *filename
,
1516 const char *section
,
1522 pa_alsa_path
*p
= userdata
;
1527 if (!(e
= element_get(p
, section
, TRUE
))) {
1528 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1532 if (pa_streq(rvalue
, "playback"))
1533 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1534 else if (pa_streq(rvalue
, "capture"))
1535 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1537 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1544 static int element_parse_direction_try_other(
1545 const char *filename
,
1547 const char *section
,
1553 pa_alsa_path
*p
= userdata
;
1557 if (!(e
= element_get(p
, section
, TRUE
))) {
1558 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1562 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1563 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1567 e
->direction_try_other
= !!yes
;
1571 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1572 pa_channel_position_mask_t v
;
1574 if (pa_streq(m
, "all-left"))
1575 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1576 else if (pa_streq(m
, "all-right"))
1577 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1578 else if (pa_streq(m
, "all-center"))
1579 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1580 else if (pa_streq(m
, "all-front"))
1581 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1582 else if (pa_streq(m
, "all-rear"))
1583 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1584 else if (pa_streq(m
, "all-side"))
1585 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1586 else if (pa_streq(m
, "all-top"))
1587 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1588 else if (pa_streq(m
, "all-no-lfe"))
1589 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1590 else if (pa_streq(m
, "all"))
1591 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1593 pa_channel_position_t p
;
1595 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1598 v
= PA_CHANNEL_POSITION_MASK(p
);
1604 static int element_parse_override_map(
1605 const char *filename
,
1607 const char *section
,
1613 pa_alsa_path
*p
= userdata
;
1615 const char *state
= NULL
;
1619 if (!(e
= element_get(p
, section
, TRUE
))) {
1620 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1624 while ((n
= pa_split(rvalue
, ",", &state
))) {
1625 pa_channel_position_mask_t m
;
1630 if ((m
= parse_mask(n
)) == 0) {
1631 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1637 if (pa_streq(lvalue
, "override-map.1"))
1638 e
->masks
[i
++][0] = m
;
1640 e
->masks
[i
++][1] = m
;
1642 /* Later on we might add override-map.3 and so on here ... */
1647 e
->override_map
= TRUE
;
1652 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
1653 snd_mixer_selem_id_t
*sid
;
1654 snd_mixer_elem_t
*me
;
1660 SELEM_INIT(sid
, e
->alsa_name
);
1661 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1662 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1666 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1668 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1669 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
1671 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
1674 pa_log_warn("Faile to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1677 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
1679 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
1680 pa_log_warn("Faile to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1686 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
1693 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
1694 element_set_option(o
->element
, m
, o
->alsa_idx
);
1699 static int option_verify(pa_alsa_option
*o
) {
1700 static const struct description_map well_known_descriptions
[] = {
1701 { "input", N_("Input") },
1702 { "input-docking", N_("Docking Station Input") },
1703 { "input-docking-microphone", N_("Docking Station Microphone") },
1704 { "input-linein", N_("Line-In") },
1705 { "input-microphone", N_("Microphone") },
1706 { "input-microphone-external", N_("External Microphone") },
1707 { "input-microphone-internal", N_("Internal Microphone") },
1708 { "input-radio", N_("Radio") },
1709 { "input-video", N_("Video") },
1710 { "input-agc-on", N_("Automatic Gain Control") },
1711 { "input-agc-off", N_("No Automatic Gain Control") },
1712 { "input-boost-on", N_("Boost") },
1713 { "input-boost-off", N_("No Boost") },
1714 { "output-amplifier-on", N_("Amplifier") },
1715 { "output-amplifier-off", N_("No Amplifier") },
1716 { "output-bass-boost-on", N_("Bass Boost") },
1717 { "output-bass-boost-off", N_("No Bass Boost") },
1718 { "output-speaker", N_("Speaker") },
1719 { "output-headphones", N_("Headphones") }
1725 pa_log("No name set for option %s", o
->alsa_name
);
1729 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
1730 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
1731 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
1735 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
1736 !pa_streq(o
->alsa_name
, "on") &&
1737 !pa_streq(o
->alsa_name
, "off")) {
1738 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
1742 if (!o
->description
)
1743 o
->description
= pa_xstrdup(lookup_description(o
->name
,
1744 well_known_descriptions
,
1745 PA_ELEMENTSOF(well_known_descriptions
)));
1746 if (!o
->description
)
1747 o
->description
= pa_xstrdup(o
->name
);
1752 static int element_verify(pa_alsa_element
*e
) {
1757 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
1758 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
1759 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
1763 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1764 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
1768 PA_LLIST_FOREACH(o
, e
->options
)
1769 if (option_verify(o
) < 0)
1775 static int path_verify(pa_alsa_path
*p
) {
1776 static const struct description_map well_known_descriptions
[] = {
1777 { "analog-input", N_("Analog Input") },
1778 { "analog-input-microphone", N_("Analog Microphone") },
1779 { "analog-input-linein", N_("Analog Line-In") },
1780 { "analog-input-radio", N_("Analog Radio") },
1781 { "analog-input-video", N_("Analog Video") },
1782 { "analog-output", N_("Analog Output") },
1783 { "analog-output-headphones", N_("Analog Headphones") },
1784 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1785 { "analog-output-mono", N_("Analog Mono Output") },
1786 { "analog-output-headphones-2", N_("Analog Headphones 2") },
1787 { "analog-output-speaker", N_("Analog Speakers") }
1788 { "analog-output-desktop-speaker", N_("Analog Speakers 2") }
1795 PA_LLIST_FOREACH(e
, p
->elements
)
1796 if (element_verify(e
) < 0)
1799 if (!p
->description
)
1800 p
->description
= pa_xstrdup(lookup_description(p
->name
,
1801 well_known_descriptions
,
1802 PA_ELEMENTSOF(well_known_descriptions
)));
1804 if (!p
->description
)
1805 p
->description
= pa_xstrdup(p
->name
);
1810 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
1816 pa_config_item items
[] = {
1818 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
1819 { "description", pa_config_parse_string
, NULL
, "General" },
1820 { "name", pa_config_parse_string
, NULL
, "General" },
1823 { "priority", option_parse_priority
, NULL
, NULL
},
1824 { "name", option_parse_name
, NULL
, NULL
},
1827 { "switch", element_parse_switch
, NULL
, NULL
},
1828 { "volume", element_parse_volume
, NULL
, NULL
},
1829 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
1830 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
1831 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
1832 /* ... later on we might add override-map.3 and so on here ... */
1833 { "required", element_parse_required
, NULL
, NULL
},
1834 { "required-absent", element_parse_required
, NULL
, NULL
},
1835 { "direction", element_parse_direction
, NULL
, NULL
},
1836 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
1837 { NULL
, NULL
, NULL
, NULL
}
1842 p
= pa_xnew0(pa_alsa_path
, 1);
1843 n
= pa_path_get_filename(fname
);
1844 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
1845 p
->direction
= direction
;
1847 items
[0].data
= &p
->priority
;
1848 items
[1].data
= &p
->description
;
1849 items
[2].data
= &p
->name
;
1851 fn
= pa_maybe_prefix_path(fname
,
1852 #if defined(__linux__) && !defined(__OPTIMIZE__)
1853 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
1857 r
= pa_config_parse(fn
, NULL
, items
, p
);
1863 if (path_verify(p
) < 0)
1869 pa_alsa_path_free(p
);
1873 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
1879 p
= pa_xnew0(pa_alsa_path
, 1);
1880 p
->name
= pa_xstrdup(element
);
1881 p
->direction
= direction
;
1883 e
= pa_xnew0(pa_alsa_element
, 1);
1885 e
->alsa_name
= pa_xstrdup(element
);
1886 e
->direction
= direction
;
1888 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1889 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1891 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
1895 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
1896 pa_alsa_option
*o
, *n
;
1900 for (o
= e
->options
; o
; o
= n
) {
1903 if (o
->alsa_idx
< 0) {
1904 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
1910 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
1911 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
1912 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
1915 static void path_drop_unsupported(pa_alsa_path
*p
) {
1916 pa_alsa_element
*e
, *n
;
1920 for (e
= p
->elements
; e
; e
= n
) {
1923 if (!element_drop_unsupported(e
)) {
1924 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
1930 static void path_make_options_unique(pa_alsa_path
*p
) {
1932 pa_alsa_option
*o
, *u
;
1934 PA_LLIST_FOREACH(e
, p
->elements
) {
1935 PA_LLIST_FOREACH(o
, e
->options
) {
1939 for (u
= o
->next
; u
; u
= u
->next
)
1940 if (pa_streq(u
->name
, o
->name
))
1946 m
= pa_xstrdup(o
->name
);
1948 /* OK, this name is not unique, hence let's rename */
1949 for (i
= 1, u
= o
; u
; u
= u
->next
) {
1952 if (!pa_streq(u
->name
, m
))
1955 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
1959 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
1960 pa_xfree(u
->description
);
1961 u
->description
= nd
;
1971 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
1974 for (; e
; e
= e
->next
)
1975 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
1976 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
1982 for (o
= e
->options
; o
; o
= o
->next
) {
1986 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
1987 s
->options
= pa_idxset_copy(template->options
);
1988 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
1990 (template->description
[0] && o
->description
[0])
1991 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
1992 : (template->description
[0]
1993 ? pa_xstrdup(template->description
)
1994 : pa_xstrdup(o
->description
));
1996 s
->priority
= PA_MAX(template->priority
, o
->priority
);
1998 s
= pa_xnew0(pa_alsa_setting
, 1);
1999 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2000 s
->name
= pa_xstrdup(o
->name
);
2001 s
->description
= pa_xstrdup(o
->description
);
2002 s
->priority
= o
->priority
;
2005 pa_idxset_put(s
->options
, o
, NULL
);
2007 if (element_create_settings(e
->next
, s
))
2008 /* This is not a leaf, so let's get rid of it */
2011 /* This is a leaf, so let's add it */
2012 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2014 e
->path
->last_setting
= s
;
2021 static void path_create_settings(pa_alsa_path
*p
) {
2024 element_create_settings(p
->elements
, NULL
);
2027 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2029 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2030 pa_channel_position_t t
;
2041 pa_log_debug("Probing path '%s'", p
->name
);
2043 PA_LLIST_FOREACH(e
, p
->elements
) {
2044 if (element_probe(e
, m
) < 0) {
2045 p
->supported
= FALSE
;
2046 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2053 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2055 if (!p
->has_volume
) {
2056 p
->min_volume
= e
->min_volume
;
2057 p
->max_volume
= e
->max_volume
;
2061 if (!p
->has_volume
) {
2062 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2063 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2064 min_dB
[t
] = e
->min_dB
;
2065 max_dB
[t
] = e
->max_dB
;
2072 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2073 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2074 min_dB
[t
] += e
->min_dB
;
2075 max_dB
[t
] += e
->max_dB
;
2078 /* Hmm, there's another element before us
2079 * which cannot do dB volumes, so we we need
2080 * to 'neutralize' this slider */
2081 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2083 } else if (p
->has_volume
)
2084 /* We can't use this volume, so let's ignore it */
2085 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2087 p
->has_volume
= TRUE
;
2090 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2094 path_drop_unsupported(p
);
2095 path_make_options_unique(p
);
2096 path_create_settings(p
);
2098 p
->supported
= TRUE
;
2101 p
->min_dB
= INFINITY
;
2102 p
->max_dB
= -INFINITY
;
2104 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2105 if (p
->min_dB
> min_dB
[t
])
2106 p
->min_dB
= min_dB
[t
];
2108 if (p
->max_dB
< max_dB
[t
])
2109 p
->max_dB
= max_dB
[t
];
2115 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2118 pa_log_debug("Setting %s (%s) priority=%u",
2120 pa_strnull(s
->description
),
2124 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2127 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2129 pa_strnull(o
->name
),
2130 pa_strnull(o
->description
),
2135 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2139 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2147 (long long unsigned) e
->merged_mask
,
2149 pa_yes_no(e
->override_map
));
2151 PA_LLIST_FOREACH(o
, e
->options
)
2152 pa_alsa_option_dump(o
);
2155 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2160 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2161 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2163 pa_strnull(p
->description
),
2166 pa_yes_no(p
->probed
),
2167 pa_yes_no(p
->supported
),
2168 pa_yes_no(p
->has_mute
),
2169 pa_yes_no(p
->has_volume
),
2170 pa_yes_no(p
->has_dB
),
2171 p
->min_volume
, p
->max_volume
,
2172 p
->min_dB
, p
->max_dB
);
2174 PA_LLIST_FOREACH(e
, p
->elements
)
2175 pa_alsa_element_dump(e
);
2177 PA_LLIST_FOREACH(s
, p
->settings
)
2178 pa_alsa_setting_dump(s
);
2181 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2182 snd_mixer_selem_id_t
*sid
;
2183 snd_mixer_elem_t
*me
;
2189 SELEM_INIT(sid
, e
->alsa_name
);
2190 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2191 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2195 snd_mixer_elem_set_callback(me
, cb
);
2196 snd_mixer_elem_set_callback_private(me
, userdata
);
2199 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2206 PA_LLIST_FOREACH(e
, p
->elements
)
2207 element_set_callback(e
, m
, cb
, userdata
);
2210 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2217 PA_LLIST_FOREACH(p
, ps
->paths
)
2218 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2221 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2222 pa_alsa_path_set
*ps
;
2223 char **pn
= NULL
, **en
= NULL
, **ie
;
2226 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2228 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2231 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2232 ps
->direction
= direction
;
2234 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2235 pn
= m
->output_path_names
;
2236 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2237 pn
= m
->input_path_names
;
2242 for (in
= pn
; *in
; in
++) {
2244 pa_bool_t duplicate
= FALSE
;
2247 for (kn
= pn
; kn
!= in
; kn
++)
2248 if (pa_streq(*kn
, *in
)) {
2256 fn
= pa_sprintf_malloc("%s.conf", *in
);
2258 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2260 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2270 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2271 en
= m
->output_element
;
2272 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2273 en
= m
->input_element
;
2276 pa_alsa_path_set_free(ps
);
2280 for (ie
= en
; *ie
; ie
++) {
2284 p
= pa_alsa_path_synthesize(*ie
, direction
);
2287 /* Mark all other passed elements for require-absent */
2288 for (je
= en
; *je
; je
++) {
2290 e
= pa_xnew0(pa_alsa_element
, 1);
2292 e
->alsa_name
= pa_xstrdup(*je
);
2293 e
->direction
= direction
;
2294 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2296 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2297 p
->last_element
= e
;
2300 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2307 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2311 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2314 pa_yes_no(ps
->probed
));
2316 PA_LLIST_FOREACH(p
, ps
->paths
)
2317 pa_alsa_path_dump(p
);
2320 static void path_set_unify(pa_alsa_path_set
*ps
) {
2322 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2325 /* We have issues dealing with paths that vary too wildly. That
2326 * means for now we have to have all paths support volume/mute/dB
2329 PA_LLIST_FOREACH(p
, ps
->paths
) {
2330 pa_assert(p
->probed
);
2334 else if (!p
->has_dB
)
2341 if (!has_volume
|| !has_dB
|| !has_mute
) {
2344 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2346 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2349 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2351 PA_LLIST_FOREACH(p
, ps
->paths
) {
2353 p
->has_volume
= FALSE
;
2358 p
->has_mute
= FALSE
;
2363 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2364 pa_alsa_path
*p
, *q
;
2366 PA_LLIST_FOREACH(p
, ps
->paths
) {
2370 for (q
= p
->next
; q
; q
= q
->next
)
2371 if (pa_streq(q
->name
, p
->name
))
2377 m
= pa_xstrdup(p
->name
);
2379 /* OK, this name is not unique, hence let's rename */
2380 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2383 if (!pa_streq(q
->name
, m
))
2386 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2390 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2391 pa_xfree(q
->description
);
2392 q
->description
= nd
;
2401 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2402 pa_alsa_path
*p
, *n
;
2409 for (p
= ps
->paths
; p
; p
= n
) {
2412 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2413 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2414 pa_alsa_path_free(p
);
2419 path_set_make_paths_unique(ps
);
2423 static void mapping_free(pa_alsa_mapping
*m
) {
2427 pa_xfree(m
->description
);
2429 pa_xstrfreev(m
->device_strings
);
2430 pa_xstrfreev(m
->input_path_names
);
2431 pa_xstrfreev(m
->output_path_names
);
2432 pa_xstrfreev(m
->input_element
);
2433 pa_xstrfreev(m
->output_element
);
2435 pa_assert(!m
->input_pcm
);
2436 pa_assert(!m
->output_pcm
);
2441 static void profile_free(pa_alsa_profile
*p
) {
2445 pa_xfree(p
->description
);
2447 pa_xstrfreev(p
->input_mapping_names
);
2448 pa_xstrfreev(p
->output_mapping_names
);
2450 if (p
->input_mappings
)
2451 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2453 if (p
->output_mappings
)
2454 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2459 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2465 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2468 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2474 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2477 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2483 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2486 if (!pa_startswith(name
, "Mapping "))
2491 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2494 m
= pa_xnew0(pa_alsa_mapping
, 1);
2495 m
->profile_set
= ps
;
2496 m
->name
= pa_xstrdup(name
);
2497 pa_channel_map_init(&m
->channel_map
);
2499 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2504 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2507 if (!pa_startswith(name
, "Profile "))
2512 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2515 p
= pa_xnew0(pa_alsa_profile
, 1);
2516 p
->profile_set
= ps
;
2517 p
->name
= pa_xstrdup(name
);
2519 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2524 static int mapping_parse_device_strings(
2525 const char *filename
,
2527 const char *section
,
2533 pa_alsa_profile_set
*ps
= userdata
;
2538 if (!(m
= mapping_get(ps
, section
))) {
2539 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2543 pa_xstrfreev(m
->device_strings
);
2544 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2545 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2552 static int mapping_parse_channel_map(
2553 const char *filename
,
2555 const char *section
,
2561 pa_alsa_profile_set
*ps
= userdata
;
2566 if (!(m
= mapping_get(ps
, section
))) {
2567 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2571 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2572 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2579 static int mapping_parse_paths(
2580 const char *filename
,
2582 const char *section
,
2588 pa_alsa_profile_set
*ps
= userdata
;
2593 if (!(m
= mapping_get(ps
, section
))) {
2594 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2598 if (pa_streq(lvalue
, "paths-input")) {
2599 pa_xstrfreev(m
->input_path_names
);
2600 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2602 pa_xstrfreev(m
->output_path_names
);
2603 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2609 static int mapping_parse_element(
2610 const char *filename
,
2612 const char *section
,
2618 pa_alsa_profile_set
*ps
= userdata
;
2623 if (!(m
= mapping_get(ps
, section
))) {
2624 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2628 if (pa_streq(lvalue
, "element-input")) {
2629 pa_xstrfreev(m
->input_element
);
2630 m
->input_element
= pa_split_spaces_strv(rvalue
);
2632 pa_xstrfreev(m
->output_element
);
2633 m
->output_element
= pa_split_spaces_strv(rvalue
);
2639 static int mapping_parse_direction(
2640 const char *filename
,
2642 const char *section
,
2648 pa_alsa_profile_set
*ps
= userdata
;
2653 if (!(m
= mapping_get(ps
, section
))) {
2654 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2658 if (pa_streq(rvalue
, "input"))
2659 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2660 else if (pa_streq(rvalue
, "output"))
2661 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2662 else if (pa_streq(rvalue
, "any"))
2663 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2665 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2672 static int mapping_parse_description(
2673 const char *filename
,
2675 const char *section
,
2681 pa_alsa_profile_set
*ps
= userdata
;
2687 if ((m
= mapping_get(ps
, section
))) {
2688 pa_xstrdup(m
->description
);
2689 m
->description
= pa_xstrdup(rvalue
);
2690 } else if ((p
= profile_get(ps
, section
))) {
2691 pa_xfree(p
->description
);
2692 p
->description
= pa_xstrdup(rvalue
);
2694 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2701 static int mapping_parse_priority(
2702 const char *filename
,
2704 const char *section
,
2710 pa_alsa_profile_set
*ps
= userdata
;
2717 if (pa_atou(rvalue
, &prio
) < 0) {
2718 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2722 if ((m
= mapping_get(ps
, section
)))
2724 else if ((p
= profile_get(ps
, section
)))
2727 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2734 static int profile_parse_mappings(
2735 const char *filename
,
2737 const char *section
,
2743 pa_alsa_profile_set
*ps
= userdata
;
2748 if (!(p
= profile_get(ps
, section
))) {
2749 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2753 if (pa_streq(lvalue
, "input-mappings")) {
2754 pa_xstrfreev(p
->input_mapping_names
);
2755 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2757 pa_xstrfreev(p
->output_mapping_names
);
2758 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2764 static int profile_parse_skip_probe(
2765 const char *filename
,
2767 const char *section
,
2773 pa_alsa_profile_set
*ps
= userdata
;
2779 if (!(p
= profile_get(ps
, section
))) {
2780 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2784 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
2785 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
2794 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
2796 static const struct description_map well_known_descriptions
[] = {
2797 { "analog-mono", N_("Analog Mono") },
2798 { "analog-stereo", N_("Analog Stereo") },
2799 { "analog-surround-21", N_("Analog Surround 2.1") },
2800 { "analog-surround-30", N_("Analog Surround 3.0") },
2801 { "analog-surround-31", N_("Analog Surround 3.1") },
2802 { "analog-surround-40", N_("Analog Surround 4.0") },
2803 { "analog-surround-41", N_("Analog Surround 4.1") },
2804 { "analog-surround-50", N_("Analog Surround 5.0") },
2805 { "analog-surround-51", N_("Analog Surround 5.1") },
2806 { "analog-surround-61", N_("Analog Surround 6.0") },
2807 { "analog-surround-61", N_("Analog Surround 6.1") },
2808 { "analog-surround-70", N_("Analog Surround 7.0") },
2809 { "analog-surround-71", N_("Analog Surround 7.1") },
2810 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2811 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2812 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2813 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2814 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2819 if (!pa_channel_map_valid(&m
->channel_map
)) {
2820 pa_log("Mapping %s is missing channel map.", m
->name
);
2824 if (!m
->device_strings
) {
2825 pa_log("Mapping %s is missing device strings.", m
->name
);
2829 if ((m
->input_path_names
&& m
->input_element
) ||
2830 (m
->output_path_names
&& m
->output_element
)) {
2831 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m
->name
);
2835 if (!m
->description
)
2836 m
->description
= pa_xstrdup(lookup_description(m
->name
,
2837 well_known_descriptions
,
2838 PA_ELEMENTSOF(well_known_descriptions
)));
2840 if (!m
->description
)
2841 m
->description
= pa_xstrdup(m
->name
);
2844 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
2846 else if (m
->channel_map
.channels
== bonus
->channels
)
2853 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
2854 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
2858 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2860 pa_strnull(m
->description
),
2862 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
2863 pa_yes_no(m
->supported
),
2867 static void profile_set_add_auto_pair(
2868 pa_alsa_profile_set
*ps
,
2869 pa_alsa_mapping
*m
, /* output */
2870 pa_alsa_mapping
*n
/* input */) {
2878 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
2881 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2885 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
2887 name
= pa_sprintf_malloc("output:%s", m
->name
);
2889 name
= pa_sprintf_malloc("input:%s", n
->name
);
2891 if (pa_hashmap_get(ps
->profiles
, name
)) {
2896 p
= pa_xnew0(pa_alsa_profile
, 1);
2897 p
->profile_set
= ps
;
2901 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2902 pa_idxset_put(p
->output_mappings
, m
, NULL
);
2903 p
->priority
+= m
->priority
* 100;
2907 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2908 pa_idxset_put(p
->input_mappings
, n
, NULL
);
2909 p
->priority
+= n
->priority
;
2912 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2915 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
2916 pa_alsa_mapping
*m
, *n
;
2917 void *m_state
, *n_state
;
2921 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
2922 profile_set_add_auto_pair(ps
, m
, NULL
);
2924 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
2925 profile_set_add_auto_pair(ps
, m
, n
);
2928 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
2929 profile_set_add_auto_pair(ps
, NULL
, n
);
2932 static int profile_verify(pa_alsa_profile
*p
) {
2934 static const struct description_map well_known_descriptions
[] = {
2935 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2936 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2937 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2938 { "off", N_("Off") }
2943 /* Replace the output mapping names by the actual mappings */
2944 if (p
->output_mapping_names
) {
2947 pa_assert(!p
->output_mappings
);
2948 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2950 for (name
= p
->output_mapping_names
; *name
; name
++) {
2953 pa_bool_t duplicate
= FALSE
;
2955 for (in
= name
+ 1; *in
; in
++)
2956 if (pa_streq(*name
, *in
)) {
2964 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
2965 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
2969 pa_idxset_put(p
->output_mappings
, m
, NULL
);
2975 pa_xstrfreev(p
->output_mapping_names
);
2976 p
->output_mapping_names
= NULL
;
2979 /* Replace the input mapping names by the actual mappings */
2980 if (p
->input_mapping_names
) {
2983 pa_assert(!p
->input_mappings
);
2984 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2986 for (name
= p
->input_mapping_names
; *name
; name
++) {
2989 pa_bool_t duplicate
= FALSE
;
2991 for (in
= name
+ 1; *in
; in
++)
2992 if (pa_streq(*name
, *in
)) {
3000 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3001 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3005 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3011 pa_xstrfreev(p
->input_mapping_names
);
3012 p
->input_mapping_names
= NULL
;
3015 if (!p
->input_mappings
&& !p
->output_mappings
) {
3016 pa_log("Profile '%s' lacks mappings.", p
->name
);
3020 if (!p
->description
)
3021 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3022 well_known_descriptions
,
3023 PA_ELEMENTSOF(well_known_descriptions
)));
3025 if (!p
->description
) {
3030 sb
= pa_strbuf_new();
3032 if (p
->output_mappings
)
3033 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3034 if (!pa_strbuf_isempty(sb
))
3035 pa_strbuf_puts(sb
, " + ");
3037 pa_strbuf_printf(sb
, "%s Output", m
->description
);
3040 if (p
->input_mappings
)
3041 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3042 if (!pa_strbuf_isempty(sb
))
3043 pa_strbuf_puts(sb
, " + ");
3045 pa_strbuf_printf(sb
, "%s Input", m
->description
);
3048 p
->description
= pa_strbuf_tostring_free(sb
);
3054 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3059 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3061 pa_strnull(p
->description
),
3063 pa_yes_no(p
->supported
),
3064 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3065 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3067 if (p
->input_mappings
)
3068 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3069 pa_log_debug("Input %s", m
->name
);
3071 if (p
->output_mappings
)
3072 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3073 pa_log_debug("Output %s", m
->name
);
3076 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3077 pa_alsa_profile_set
*ps
;
3084 static pa_config_item items
[] = {
3086 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3089 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3090 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3091 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3092 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3093 { "element-input", mapping_parse_element
, NULL
, NULL
},
3094 { "element-output", mapping_parse_element
, NULL
, NULL
},
3095 { "direction", mapping_parse_direction
, NULL
, NULL
},
3097 /* Shared by [Mapping ...] and [Profile ...] */
3098 { "description", mapping_parse_description
, NULL
, NULL
},
3099 { "priority", mapping_parse_priority
, NULL
, NULL
},
3102 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3103 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3104 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3105 { NULL
, NULL
, NULL
, NULL
}
3108 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3109 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3110 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3112 items
[0].data
= &ps
->auto_profiles
;
3115 fname
= "default.conf";
3117 fn
= pa_maybe_prefix_path(fname
,
3118 #if defined(__linux__) && !defined(__OPTIMIZE__)
3119 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3121 PA_ALSA_PROFILE_SETS_DIR
);
3123 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3129 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3130 if (mapping_verify(m
, bonus
) < 0)
3133 if (ps
->auto_profiles
)
3134 profile_set_add_auto(ps
);
3136 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3137 if (profile_verify(p
) < 0)
3143 pa_alsa_profile_set_free(ps
);
3147 void pa_alsa_profile_set_probe(
3148 pa_alsa_profile_set
*ps
,
3150 const pa_sample_spec
*ss
,
3151 unsigned default_n_fragments
,
3152 unsigned default_fragment_size_msec
) {
3155 pa_alsa_profile
*p
, *last
= NULL
;
3165 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3166 pa_sample_spec try_ss
;
3167 pa_channel_map try_map
;
3168 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3171 /* Is this already marked that it is supported? (i.e. from the config file) */
3175 pa_log_debug("Looking at profile %s", p
->name
);
3177 /* Close PCMs from the last iteration we don't need anymore */
3178 if (last
&& last
->output_mappings
)
3179 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3184 if (last
->supported
)
3187 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3188 snd_pcm_close(m
->output_pcm
);
3189 m
->output_pcm
= NULL
;
3193 if (last
&& last
->input_mappings
)
3194 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3199 if (last
->supported
)
3202 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3203 snd_pcm_close(m
->input_pcm
);
3204 m
->input_pcm
= NULL
;
3208 p
->supported
= TRUE
;
3210 /* Check if we can open all new ones */
3211 if (p
->output_mappings
)
3212 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3217 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3218 try_map
= m
->channel_map
;
3220 try_ss
.channels
= try_map
.channels
;
3223 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3224 pa_frame_size(&try_ss
);
3225 try_buffer_size
= default_n_fragments
* try_period_size
;
3227 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3232 SND_PCM_STREAM_PLAYBACK
,
3233 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3235 p
->supported
= FALSE
;
3240 if (p
->input_mappings
&& p
->supported
)
3241 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3246 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3247 try_map
= m
->channel_map
;
3249 try_ss
.channels
= try_map
.channels
;
3252 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3253 pa_frame_size(&try_ss
);
3254 try_buffer_size
= default_n_fragments
* try_period_size
;
3256 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3261 SND_PCM_STREAM_CAPTURE
,
3262 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3264 p
->supported
= FALSE
;
3272 pa_log_debug("Profile %s supported.", p
->name
);
3279 if (last
->output_mappings
)
3280 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3281 if (m
->output_pcm
) {
3283 if (last
->supported
)
3286 snd_pcm_close(m
->output_pcm
);
3287 m
->output_pcm
= NULL
;
3290 if (last
->input_mappings
)
3291 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3294 if (last
->supported
)
3297 snd_pcm_close(m
->input_pcm
);
3298 m
->input_pcm
= NULL
;
3302 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3303 if (!p
->supported
) {
3304 pa_hashmap_remove(ps
->profiles
, p
->name
);
3308 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3309 if (m
->supported
<= 0) {
3310 pa_hashmap_remove(ps
->mappings
, m
->name
);
3317 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3324 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3327 pa_yes_no(ps
->auto_profiles
),
3328 pa_yes_no(ps
->probed
),
3329 pa_hashmap_size(ps
->mappings
),
3330 pa_hashmap_size(ps
->profiles
));
3332 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3333 pa_alsa_mapping_dump(m
);
3335 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3336 pa_alsa_profile_dump(p
);
3339 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3346 /* if there is no path, we don't want a port list */
3350 if (!ps
->paths
->next
){
3353 /* If there is only one path, but no or only one setting, then
3354 * we want a port list either */
3355 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3358 /* Ok, there is only one path, however with multiple settings,
3359 * so let's create a port for each setting */
3360 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3362 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3363 pa_device_port
*port
;
3364 pa_alsa_port_data
*data
;
3366 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3367 port
->priority
= s
->priority
;
3369 data
= PA_DEVICE_PORT_DATA(port
);
3370 data
->path
= ps
->paths
;
3373 pa_hashmap_put(*p
, port
->name
, port
);
3378 /* We have multiple paths, so let's create a port for each
3379 * one, and each of each settings */
3380 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3382 PA_LLIST_FOREACH(path
, ps
->paths
) {
3384 if (!path
->settings
|| !path
->settings
->next
) {
3385 pa_device_port
*port
;
3386 pa_alsa_port_data
*data
;
3388 /* If there is no or just one setting we only need a
3391 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3392 port
->priority
= path
->priority
* 100;
3395 data
= PA_DEVICE_PORT_DATA(port
);
3397 data
->setting
= path
->settings
;
3399 pa_hashmap_put(*p
, port
->name
, port
);
3403 PA_LLIST_FOREACH(s
, path
->settings
) {
3404 pa_device_port
*port
;
3405 pa_alsa_port_data
*data
;
3408 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3410 if (s
->description
[0])
3411 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3413 d
= pa_xstrdup(path
->description
);
3415 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3416 port
->priority
= path
->priority
* 100 + s
->priority
;
3421 data
= PA_DEVICE_PORT_DATA(port
);
3425 pa_hashmap_put(*p
, port
->name
, port
);
3431 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));