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 Speaker") }
1794 PA_LLIST_FOREACH(e
, p
->elements
)
1795 if (element_verify(e
) < 0)
1798 if (!p
->description
)
1799 p
->description
= pa_xstrdup(lookup_description(p
->name
,
1800 well_known_descriptions
,
1801 PA_ELEMENTSOF(well_known_descriptions
)));
1803 if (!p
->description
)
1804 p
->description
= pa_xstrdup(p
->name
);
1809 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
1815 pa_config_item items
[] = {
1817 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
1818 { "description", pa_config_parse_string
, NULL
, "General" },
1819 { "name", pa_config_parse_string
, NULL
, "General" },
1822 { "priority", option_parse_priority
, NULL
, NULL
},
1823 { "name", option_parse_name
, NULL
, NULL
},
1826 { "switch", element_parse_switch
, NULL
, NULL
},
1827 { "volume", element_parse_volume
, NULL
, NULL
},
1828 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
1829 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
1830 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
1831 /* ... later on we might add override-map.3 and so on here ... */
1832 { "required", element_parse_required
, NULL
, NULL
},
1833 { "required-absent", element_parse_required
, NULL
, NULL
},
1834 { "direction", element_parse_direction
, NULL
, NULL
},
1835 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
1836 { NULL
, NULL
, NULL
, NULL
}
1841 p
= pa_xnew0(pa_alsa_path
, 1);
1842 n
= pa_path_get_filename(fname
);
1843 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
1844 p
->direction
= direction
;
1846 items
[0].data
= &p
->priority
;
1847 items
[1].data
= &p
->description
;
1848 items
[2].data
= &p
->name
;
1850 fn
= pa_maybe_prefix_path(fname
,
1851 #if defined(__linux__) && !defined(__OPTIMIZE__)
1852 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
1856 r
= pa_config_parse(fn
, NULL
, items
, p
);
1862 if (path_verify(p
) < 0)
1868 pa_alsa_path_free(p
);
1872 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
1878 p
= pa_xnew0(pa_alsa_path
, 1);
1879 p
->name
= pa_xstrdup(element
);
1880 p
->direction
= direction
;
1882 e
= pa_xnew0(pa_alsa_element
, 1);
1884 e
->alsa_name
= pa_xstrdup(element
);
1885 e
->direction
= direction
;
1887 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1888 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1890 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
1894 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
1895 pa_alsa_option
*o
, *n
;
1899 for (o
= e
->options
; o
; o
= n
) {
1902 if (o
->alsa_idx
< 0) {
1903 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
1909 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
1910 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
1911 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
1914 static void path_drop_unsupported(pa_alsa_path
*p
) {
1915 pa_alsa_element
*e
, *n
;
1919 for (e
= p
->elements
; e
; e
= n
) {
1922 if (!element_drop_unsupported(e
)) {
1923 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
1929 static void path_make_options_unique(pa_alsa_path
*p
) {
1931 pa_alsa_option
*o
, *u
;
1933 PA_LLIST_FOREACH(e
, p
->elements
) {
1934 PA_LLIST_FOREACH(o
, e
->options
) {
1938 for (u
= o
->next
; u
; u
= u
->next
)
1939 if (pa_streq(u
->name
, o
->name
))
1945 m
= pa_xstrdup(o
->name
);
1947 /* OK, this name is not unique, hence let's rename */
1948 for (i
= 1, u
= o
; u
; u
= u
->next
) {
1951 if (!pa_streq(u
->name
, m
))
1954 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
1958 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
1959 pa_xfree(u
->description
);
1960 u
->description
= nd
;
1970 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
1973 for (; e
; e
= e
->next
)
1974 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
1975 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
1981 for (o
= e
->options
; o
; o
= o
->next
) {
1985 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
1986 s
->options
= pa_idxset_copy(template->options
);
1987 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
1989 (template->description
[0] && o
->description
[0])
1990 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
1991 : (template->description
[0]
1992 ? pa_xstrdup(template->description
)
1993 : pa_xstrdup(o
->description
));
1995 s
->priority
= PA_MAX(template->priority
, o
->priority
);
1997 s
= pa_xnew0(pa_alsa_setting
, 1);
1998 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1999 s
->name
= pa_xstrdup(o
->name
);
2000 s
->description
= pa_xstrdup(o
->description
);
2001 s
->priority
= o
->priority
;
2004 pa_idxset_put(s
->options
, o
, NULL
);
2006 if (element_create_settings(e
->next
, s
))
2007 /* This is not a leaf, so let's get rid of it */
2010 /* This is a leaf, so let's add it */
2011 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2013 e
->path
->last_setting
= s
;
2020 static void path_create_settings(pa_alsa_path
*p
) {
2023 element_create_settings(p
->elements
, NULL
);
2026 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2028 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2029 pa_channel_position_t t
;
2040 pa_log_debug("Probing path '%s'", p
->name
);
2042 PA_LLIST_FOREACH(e
, p
->elements
) {
2043 if (element_probe(e
, m
) < 0) {
2044 p
->supported
= FALSE
;
2045 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2052 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2054 if (!p
->has_volume
) {
2055 p
->min_volume
= e
->min_volume
;
2056 p
->max_volume
= e
->max_volume
;
2060 if (!p
->has_volume
) {
2061 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2062 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2063 min_dB
[t
] = e
->min_dB
;
2064 max_dB
[t
] = e
->max_dB
;
2071 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2072 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2073 min_dB
[t
] += e
->min_dB
;
2074 max_dB
[t
] += e
->max_dB
;
2077 /* Hmm, there's another element before us
2078 * which cannot do dB volumes, so we we need
2079 * to 'neutralize' this slider */
2080 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2082 } else if (p
->has_volume
)
2083 /* We can't use this volume, so let's ignore it */
2084 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2086 p
->has_volume
= TRUE
;
2089 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2093 path_drop_unsupported(p
);
2094 path_make_options_unique(p
);
2095 path_create_settings(p
);
2097 p
->supported
= TRUE
;
2100 p
->min_dB
= INFINITY
;
2101 p
->max_dB
= -INFINITY
;
2103 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2104 if (p
->min_dB
> min_dB
[t
])
2105 p
->min_dB
= min_dB
[t
];
2107 if (p
->max_dB
< max_dB
[t
])
2108 p
->max_dB
= max_dB
[t
];
2114 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2117 pa_log_debug("Setting %s (%s) priority=%u",
2119 pa_strnull(s
->description
),
2123 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2126 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2128 pa_strnull(o
->name
),
2129 pa_strnull(o
->description
),
2134 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2138 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",
2146 (long long unsigned) e
->merged_mask
,
2148 pa_yes_no(e
->override_map
));
2150 PA_LLIST_FOREACH(o
, e
->options
)
2151 pa_alsa_option_dump(o
);
2154 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2159 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2160 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2162 pa_strnull(p
->description
),
2165 pa_yes_no(p
->probed
),
2166 pa_yes_no(p
->supported
),
2167 pa_yes_no(p
->has_mute
),
2168 pa_yes_no(p
->has_volume
),
2169 pa_yes_no(p
->has_dB
),
2170 p
->min_volume
, p
->max_volume
,
2171 p
->min_dB
, p
->max_dB
);
2173 PA_LLIST_FOREACH(e
, p
->elements
)
2174 pa_alsa_element_dump(e
);
2176 PA_LLIST_FOREACH(s
, p
->settings
)
2177 pa_alsa_setting_dump(s
);
2180 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2181 snd_mixer_selem_id_t
*sid
;
2182 snd_mixer_elem_t
*me
;
2188 SELEM_INIT(sid
, e
->alsa_name
);
2189 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2190 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2194 snd_mixer_elem_set_callback(me
, cb
);
2195 snd_mixer_elem_set_callback_private(me
, userdata
);
2198 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2205 PA_LLIST_FOREACH(e
, p
->elements
)
2206 element_set_callback(e
, m
, cb
, userdata
);
2209 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2216 PA_LLIST_FOREACH(p
, ps
->paths
)
2217 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2220 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2221 pa_alsa_path_set
*ps
;
2222 char **pn
= NULL
, **en
= NULL
, **ie
;
2225 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2227 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2230 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2231 ps
->direction
= direction
;
2233 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2234 pn
= m
->output_path_names
;
2235 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2236 pn
= m
->input_path_names
;
2241 for (in
= pn
; *in
; in
++) {
2243 pa_bool_t duplicate
= FALSE
;
2246 for (kn
= pn
; kn
!= in
; kn
++)
2247 if (pa_streq(*kn
, *in
)) {
2255 fn
= pa_sprintf_malloc("%s.conf", *in
);
2257 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2259 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2269 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2270 en
= m
->output_element
;
2271 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2272 en
= m
->input_element
;
2275 pa_alsa_path_set_free(ps
);
2279 for (ie
= en
; *ie
; ie
++) {
2283 p
= pa_alsa_path_synthesize(*ie
, direction
);
2286 /* Mark all other passed elements for require-absent */
2287 for (je
= en
; *je
; je
++) {
2289 e
= pa_xnew0(pa_alsa_element
, 1);
2291 e
->alsa_name
= pa_xstrdup(*je
);
2292 e
->direction
= direction
;
2293 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2295 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2296 p
->last_element
= e
;
2299 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2306 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2310 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2313 pa_yes_no(ps
->probed
));
2315 PA_LLIST_FOREACH(p
, ps
->paths
)
2316 pa_alsa_path_dump(p
);
2319 static void path_set_unify(pa_alsa_path_set
*ps
) {
2321 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2324 /* We have issues dealing with paths that vary too wildly. That
2325 * means for now we have to have all paths support volume/mute/dB
2328 PA_LLIST_FOREACH(p
, ps
->paths
) {
2329 pa_assert(p
->probed
);
2333 else if (!p
->has_dB
)
2340 if (!has_volume
|| !has_dB
|| !has_mute
) {
2343 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2345 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2348 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2350 PA_LLIST_FOREACH(p
, ps
->paths
) {
2352 p
->has_volume
= FALSE
;
2357 p
->has_mute
= FALSE
;
2362 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2363 pa_alsa_path
*p
, *q
;
2365 PA_LLIST_FOREACH(p
, ps
->paths
) {
2369 for (q
= p
->next
; q
; q
= q
->next
)
2370 if (pa_streq(q
->name
, p
->name
))
2376 m
= pa_xstrdup(p
->name
);
2378 /* OK, this name is not unique, hence let's rename */
2379 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2382 if (!pa_streq(q
->name
, m
))
2385 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2389 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2390 pa_xfree(q
->description
);
2391 q
->description
= nd
;
2400 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2401 pa_alsa_path
*p
, *n
;
2408 for (p
= ps
->paths
; p
; p
= n
) {
2411 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2412 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2413 pa_alsa_path_free(p
);
2418 path_set_make_paths_unique(ps
);
2422 static void mapping_free(pa_alsa_mapping
*m
) {
2426 pa_xfree(m
->description
);
2428 pa_xstrfreev(m
->device_strings
);
2429 pa_xstrfreev(m
->input_path_names
);
2430 pa_xstrfreev(m
->output_path_names
);
2431 pa_xstrfreev(m
->input_element
);
2432 pa_xstrfreev(m
->output_element
);
2434 pa_assert(!m
->input_pcm
);
2435 pa_assert(!m
->output_pcm
);
2440 static void profile_free(pa_alsa_profile
*p
) {
2444 pa_xfree(p
->description
);
2446 pa_xstrfreev(p
->input_mapping_names
);
2447 pa_xstrfreev(p
->output_mapping_names
);
2449 if (p
->input_mappings
)
2450 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2452 if (p
->output_mappings
)
2453 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2458 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2464 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2467 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2473 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2476 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2482 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2485 if (!pa_startswith(name
, "Mapping "))
2490 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2493 m
= pa_xnew0(pa_alsa_mapping
, 1);
2494 m
->profile_set
= ps
;
2495 m
->name
= pa_xstrdup(name
);
2496 pa_channel_map_init(&m
->channel_map
);
2498 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2503 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2506 if (!pa_startswith(name
, "Profile "))
2511 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2514 p
= pa_xnew0(pa_alsa_profile
, 1);
2515 p
->profile_set
= ps
;
2516 p
->name
= pa_xstrdup(name
);
2518 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2523 static int mapping_parse_device_strings(
2524 const char *filename
,
2526 const char *section
,
2532 pa_alsa_profile_set
*ps
= userdata
;
2537 if (!(m
= mapping_get(ps
, section
))) {
2538 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2542 pa_xstrfreev(m
->device_strings
);
2543 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2544 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2551 static int mapping_parse_channel_map(
2552 const char *filename
,
2554 const char *section
,
2560 pa_alsa_profile_set
*ps
= userdata
;
2565 if (!(m
= mapping_get(ps
, section
))) {
2566 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2570 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2571 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2578 static int mapping_parse_paths(
2579 const char *filename
,
2581 const char *section
,
2587 pa_alsa_profile_set
*ps
= userdata
;
2592 if (!(m
= mapping_get(ps
, section
))) {
2593 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2597 if (pa_streq(lvalue
, "paths-input")) {
2598 pa_xstrfreev(m
->input_path_names
);
2599 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2601 pa_xstrfreev(m
->output_path_names
);
2602 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2608 static int mapping_parse_element(
2609 const char *filename
,
2611 const char *section
,
2617 pa_alsa_profile_set
*ps
= userdata
;
2622 if (!(m
= mapping_get(ps
, section
))) {
2623 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2627 if (pa_streq(lvalue
, "element-input")) {
2628 pa_xstrfreev(m
->input_element
);
2629 m
->input_element
= pa_split_spaces_strv(rvalue
);
2631 pa_xstrfreev(m
->output_element
);
2632 m
->output_element
= pa_split_spaces_strv(rvalue
);
2638 static int mapping_parse_direction(
2639 const char *filename
,
2641 const char *section
,
2647 pa_alsa_profile_set
*ps
= userdata
;
2652 if (!(m
= mapping_get(ps
, section
))) {
2653 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2657 if (pa_streq(rvalue
, "input"))
2658 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2659 else if (pa_streq(rvalue
, "output"))
2660 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2661 else if (pa_streq(rvalue
, "any"))
2662 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2664 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2671 static int mapping_parse_description(
2672 const char *filename
,
2674 const char *section
,
2680 pa_alsa_profile_set
*ps
= userdata
;
2686 if ((m
= mapping_get(ps
, section
))) {
2687 pa_xstrdup(m
->description
);
2688 m
->description
= pa_xstrdup(rvalue
);
2689 } else if ((p
= profile_get(ps
, section
))) {
2690 pa_xfree(p
->description
);
2691 p
->description
= pa_xstrdup(rvalue
);
2693 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2700 static int mapping_parse_priority(
2701 const char *filename
,
2703 const char *section
,
2709 pa_alsa_profile_set
*ps
= userdata
;
2716 if (pa_atou(rvalue
, &prio
) < 0) {
2717 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2721 if ((m
= mapping_get(ps
, section
)))
2723 else if ((p
= profile_get(ps
, section
)))
2726 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2733 static int profile_parse_mappings(
2734 const char *filename
,
2736 const char *section
,
2742 pa_alsa_profile_set
*ps
= userdata
;
2747 if (!(p
= profile_get(ps
, section
))) {
2748 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2752 if (pa_streq(lvalue
, "input-mappings")) {
2753 pa_xstrfreev(p
->input_mapping_names
);
2754 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2756 pa_xstrfreev(p
->output_mapping_names
);
2757 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2763 static int profile_parse_skip_probe(
2764 const char *filename
,
2766 const char *section
,
2772 pa_alsa_profile_set
*ps
= userdata
;
2778 if (!(p
= profile_get(ps
, section
))) {
2779 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2783 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
2784 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
2793 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
2795 static const struct description_map well_known_descriptions
[] = {
2796 { "analog-mono", N_("Analog Mono") },
2797 { "analog-stereo", N_("Analog Stereo") },
2798 { "analog-surround-21", N_("Analog Surround 2.1") },
2799 { "analog-surround-30", N_("Analog Surround 3.0") },
2800 { "analog-surround-31", N_("Analog Surround 3.1") },
2801 { "analog-surround-40", N_("Analog Surround 4.0") },
2802 { "analog-surround-41", N_("Analog Surround 4.1") },
2803 { "analog-surround-50", N_("Analog Surround 5.0") },
2804 { "analog-surround-51", N_("Analog Surround 5.1") },
2805 { "analog-surround-61", N_("Analog Surround 6.0") },
2806 { "analog-surround-61", N_("Analog Surround 6.1") },
2807 { "analog-surround-70", N_("Analog Surround 7.0") },
2808 { "analog-surround-71", N_("Analog Surround 7.1") },
2809 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2810 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2811 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2812 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2813 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2818 if (!pa_channel_map_valid(&m
->channel_map
)) {
2819 pa_log("Mapping %s is missing channel map.", m
->name
);
2823 if (!m
->device_strings
) {
2824 pa_log("Mapping %s is missing device strings.", m
->name
);
2828 if ((m
->input_path_names
&& m
->input_element
) ||
2829 (m
->output_path_names
&& m
->output_element
)) {
2830 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m
->name
);
2834 if (!m
->description
)
2835 m
->description
= pa_xstrdup(lookup_description(m
->name
,
2836 well_known_descriptions
,
2837 PA_ELEMENTSOF(well_known_descriptions
)));
2839 if (!m
->description
)
2840 m
->description
= pa_xstrdup(m
->name
);
2843 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
2845 else if (m
->channel_map
.channels
== bonus
->channels
)
2852 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
2853 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
2857 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2859 pa_strnull(m
->description
),
2861 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
2862 pa_yes_no(m
->supported
),
2866 static void profile_set_add_auto_pair(
2867 pa_alsa_profile_set
*ps
,
2868 pa_alsa_mapping
*m
, /* output */
2869 pa_alsa_mapping
*n
/* input */) {
2877 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
2880 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2884 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
2886 name
= pa_sprintf_malloc("output:%s", m
->name
);
2888 name
= pa_sprintf_malloc("input:%s", n
->name
);
2890 if (pa_hashmap_get(ps
->profiles
, name
)) {
2895 p
= pa_xnew0(pa_alsa_profile
, 1);
2896 p
->profile_set
= ps
;
2900 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2901 pa_idxset_put(p
->output_mappings
, m
, NULL
);
2902 p
->priority
+= m
->priority
* 100;
2906 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2907 pa_idxset_put(p
->input_mappings
, n
, NULL
);
2908 p
->priority
+= n
->priority
;
2911 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2914 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
2915 pa_alsa_mapping
*m
, *n
;
2916 void *m_state
, *n_state
;
2920 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
2921 profile_set_add_auto_pair(ps
, m
, NULL
);
2923 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
2924 profile_set_add_auto_pair(ps
, m
, n
);
2927 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
2928 profile_set_add_auto_pair(ps
, NULL
, n
);
2931 static int profile_verify(pa_alsa_profile
*p
) {
2933 static const struct description_map well_known_descriptions
[] = {
2934 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2935 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2936 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2937 { "off", N_("Off") }
2942 /* Replace the output mapping names by the actual mappings */
2943 if (p
->output_mapping_names
) {
2946 pa_assert(!p
->output_mappings
);
2947 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2949 for (name
= p
->output_mapping_names
; *name
; name
++) {
2952 pa_bool_t duplicate
= FALSE
;
2954 for (in
= name
+ 1; *in
; in
++)
2955 if (pa_streq(*name
, *in
)) {
2963 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
2964 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
2968 pa_idxset_put(p
->output_mappings
, m
, NULL
);
2974 pa_xstrfreev(p
->output_mapping_names
);
2975 p
->output_mapping_names
= NULL
;
2978 /* Replace the input mapping names by the actual mappings */
2979 if (p
->input_mapping_names
) {
2982 pa_assert(!p
->input_mappings
);
2983 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2985 for (name
= p
->input_mapping_names
; *name
; name
++) {
2988 pa_bool_t duplicate
= FALSE
;
2990 for (in
= name
+ 1; *in
; in
++)
2991 if (pa_streq(*name
, *in
)) {
2999 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3000 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3004 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3010 pa_xstrfreev(p
->input_mapping_names
);
3011 p
->input_mapping_names
= NULL
;
3014 if (!p
->input_mappings
&& !p
->output_mappings
) {
3015 pa_log("Profile '%s' lacks mappings.", p
->name
);
3019 if (!p
->description
)
3020 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3021 well_known_descriptions
,
3022 PA_ELEMENTSOF(well_known_descriptions
)));
3024 if (!p
->description
) {
3029 sb
= pa_strbuf_new();
3031 if (p
->output_mappings
)
3032 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3033 if (!pa_strbuf_isempty(sb
))
3034 pa_strbuf_puts(sb
, " + ");
3036 pa_strbuf_printf(sb
, "%s Output", m
->description
);
3039 if (p
->input_mappings
)
3040 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3041 if (!pa_strbuf_isempty(sb
))
3042 pa_strbuf_puts(sb
, " + ");
3044 pa_strbuf_printf(sb
, "%s Input", m
->description
);
3047 p
->description
= pa_strbuf_tostring_free(sb
);
3053 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3058 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3060 pa_strnull(p
->description
),
3062 pa_yes_no(p
->supported
),
3063 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3064 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3066 if (p
->input_mappings
)
3067 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3068 pa_log_debug("Input %s", m
->name
);
3070 if (p
->output_mappings
)
3071 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3072 pa_log_debug("Output %s", m
->name
);
3075 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3076 pa_alsa_profile_set
*ps
;
3083 static pa_config_item items
[] = {
3085 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3088 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3089 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3090 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3091 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3092 { "element-input", mapping_parse_element
, NULL
, NULL
},
3093 { "element-output", mapping_parse_element
, NULL
, NULL
},
3094 { "direction", mapping_parse_direction
, NULL
, NULL
},
3096 /* Shared by [Mapping ...] and [Profile ...] */
3097 { "description", mapping_parse_description
, NULL
, NULL
},
3098 { "priority", mapping_parse_priority
, NULL
, NULL
},
3101 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3102 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3103 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3104 { NULL
, NULL
, NULL
, NULL
}
3107 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3108 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3109 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3111 items
[0].data
= &ps
->auto_profiles
;
3114 fname
= "default.conf";
3116 fn
= pa_maybe_prefix_path(fname
,
3117 #if defined(__linux__) && !defined(__OPTIMIZE__)
3118 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3120 PA_ALSA_PROFILE_SETS_DIR
);
3122 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3128 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3129 if (mapping_verify(m
, bonus
) < 0)
3132 if (ps
->auto_profiles
)
3133 profile_set_add_auto(ps
);
3135 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3136 if (profile_verify(p
) < 0)
3142 pa_alsa_profile_set_free(ps
);
3146 void pa_alsa_profile_set_probe(
3147 pa_alsa_profile_set
*ps
,
3149 const pa_sample_spec
*ss
,
3150 unsigned default_n_fragments
,
3151 unsigned default_fragment_size_msec
) {
3154 pa_alsa_profile
*p
, *last
= NULL
;
3164 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3165 pa_sample_spec try_ss
;
3166 pa_channel_map try_map
;
3167 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3170 /* Is this already marked that it is supported? (i.e. from the config file) */
3174 pa_log_debug("Looking at profile %s", p
->name
);
3176 /* Close PCMs from the last iteration we don't need anymore */
3177 if (last
&& last
->output_mappings
)
3178 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3183 if (last
->supported
)
3186 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3187 snd_pcm_close(m
->output_pcm
);
3188 m
->output_pcm
= NULL
;
3192 if (last
&& last
->input_mappings
)
3193 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3198 if (last
->supported
)
3201 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3202 snd_pcm_close(m
->input_pcm
);
3203 m
->input_pcm
= NULL
;
3207 p
->supported
= TRUE
;
3209 /* Check if we can open all new ones */
3210 if (p
->output_mappings
)
3211 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3216 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3217 try_map
= m
->channel_map
;
3219 try_ss
.channels
= try_map
.channels
;
3222 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3223 pa_frame_size(&try_ss
);
3224 try_buffer_size
= default_n_fragments
* try_period_size
;
3226 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3231 SND_PCM_STREAM_PLAYBACK
,
3232 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3234 p
->supported
= FALSE
;
3239 if (p
->input_mappings
&& p
->supported
)
3240 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3245 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3246 try_map
= m
->channel_map
;
3248 try_ss
.channels
= try_map
.channels
;
3251 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3252 pa_frame_size(&try_ss
);
3253 try_buffer_size
= default_n_fragments
* try_period_size
;
3255 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3260 SND_PCM_STREAM_CAPTURE
,
3261 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3263 p
->supported
= FALSE
;
3271 pa_log_debug("Profile %s supported.", p
->name
);
3278 if (last
->output_mappings
)
3279 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3280 if (m
->output_pcm
) {
3282 if (last
->supported
)
3285 snd_pcm_close(m
->output_pcm
);
3286 m
->output_pcm
= NULL
;
3289 if (last
->input_mappings
)
3290 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3293 if (last
->supported
)
3296 snd_pcm_close(m
->input_pcm
);
3297 m
->input_pcm
= NULL
;
3301 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3302 if (!p
->supported
) {
3303 pa_hashmap_remove(ps
->profiles
, p
->name
);
3307 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3308 if (m
->supported
<= 0) {
3309 pa_hashmap_remove(ps
->mappings
, m
->name
);
3316 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3323 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3326 pa_yes_no(ps
->auto_profiles
),
3327 pa_yes_no(ps
->probed
),
3328 pa_hashmap_size(ps
->mappings
),
3329 pa_hashmap_size(ps
->profiles
));
3331 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3332 pa_alsa_mapping_dump(m
);
3334 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3335 pa_alsa_profile_dump(p
);
3338 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3345 /* if there is no path, we don't want a port list */
3349 if (!ps
->paths
->next
){
3352 /* If there is only one path, but no or only one setting, then
3353 * we want a port list either */
3354 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3357 /* Ok, there is only one path, however with multiple settings,
3358 * so let's create a port for each setting */
3359 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3361 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3362 pa_device_port
*port
;
3363 pa_alsa_port_data
*data
;
3365 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3366 port
->priority
= s
->priority
;
3368 data
= PA_DEVICE_PORT_DATA(port
);
3369 data
->path
= ps
->paths
;
3372 pa_hashmap_put(*p
, port
->name
, port
);
3377 /* We have multiple paths, so let's create a port for each
3378 * one, and each of each settings */
3379 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3381 PA_LLIST_FOREACH(path
, ps
->paths
) {
3383 if (!path
->settings
|| !path
->settings
->next
) {
3384 pa_device_port
*port
;
3385 pa_alsa_port_data
*data
;
3387 /* If there is no or just one setting we only need a
3390 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3391 port
->priority
= path
->priority
* 100;
3394 data
= PA_DEVICE_PORT_DATA(port
);
3396 data
->setting
= path
->settings
;
3398 pa_hashmap_put(*p
, port
->name
, port
);
3402 PA_LLIST_FOREACH(s
, path
->settings
) {
3403 pa_device_port
*port
;
3404 pa_alsa_port_data
*data
;
3407 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3409 if (s
->description
[0])
3410 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3412 d
= pa_xstrdup(path
->description
);
3414 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3415 port
->priority
= path
->priority
* 100 + s
->priority
;
3420 data
= PA_DEVICE_PORT_DATA(port
);
3424 pa_hashmap_put(*p
, port
->name
, port
);
3430 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));