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;
482 pa_volume_t max_channel_volume
= PA_VOLUME_MUTED
;
490 SELEM_INIT(sid
, e
->alsa_name
);
491 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
492 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
496 pa_cvolume_mute(v
, cm
->channels
);
498 /* We take the highest volume of all channels that match */
500 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
507 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
508 if (snd_mixer_selem_has_playback_channel(me
, c
))
509 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
513 if (snd_mixer_selem_has_capture_channel(me
, c
))
514 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
522 #ifdef HAVE_VALGRIND_MEMCHECK_H
523 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
526 f
= from_alsa_dB(value
);
531 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
532 if (snd_mixer_selem_has_playback_channel(me
, c
))
533 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
537 if (snd_mixer_selem_has_capture_channel(me
, c
))
538 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
546 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
549 if (f
> max_channel_volume
)
550 max_channel_volume
= f
;
552 for (k
= 0; k
< cm
->channels
; k
++)
553 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
554 if (v
->values
[k
] < f
)
557 mask
|= e
->masks
[c
][e
->n_channels
-1];
560 for (k
= 0; k
< cm
->channels
; k
++)
561 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
562 v
->values
[k
] = max_channel_volume
;
567 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
578 pa_cvolume_reset(v
, cm
->channels
);
580 PA_LLIST_FOREACH(e
, p
->elements
) {
583 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
586 pa_assert(!p
->has_dB
|| e
->has_dB
);
588 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
591 /* If we have no dB information all we can do is take the first element and leave */
597 pa_sw_cvolume_multiply(v
, v
, &ev
);
603 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
604 snd_mixer_selem_id_t
*sid
;
605 snd_mixer_elem_t
*me
;
606 snd_mixer_selem_channel_id_t c
;
612 SELEM_INIT(sid
, e
->alsa_name
);
613 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
614 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
618 /* We return muted if at least one channel is muted */
620 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
624 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
625 if (snd_mixer_selem_has_playback_channel(me
, c
))
626 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
630 if (snd_mixer_selem_has_capture_channel(me
, c
))
631 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
649 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
659 PA_LLIST_FOREACH(e
, p
->elements
) {
662 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
665 if (element_get_switch(e
, m
, &b
) < 0)
678 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
679 snd_mixer_selem_id_t
*sid
;
681 snd_mixer_elem_t
*me
;
682 snd_mixer_selem_channel_id_t c
;
683 pa_channel_position_mask_t mask
= 0;
684 pa_volume_t max_channel_volume
= PA_VOLUME_MUTED
;
691 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
693 SELEM_INIT(sid
, e
->alsa_name
);
694 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
695 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
699 pa_cvolume_mute(&rv
, cm
->channels
);
701 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
703 pa_volume_t f
= PA_VOLUME_MUTED
;
704 pa_bool_t found
= FALSE
;
706 for (k
= 0; k
< cm
->channels
; k
++)
707 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
709 if (v
->values
[k
] > f
)
714 /* Hmm, so this channel does not exist in the volume
715 * struct, so let's bind it to the overall max of the
717 f
= pa_cvolume_max(v
);
721 long value
= to_alsa_dB(f
);
723 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
724 /* If we call set_play_volume() without checking first
725 * if the channel is available, ALSA behaves ver
726 * strangely and doesn't fail the call */
727 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
728 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, +1)) >= 0)
729 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
733 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
734 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, +1)) >= 0)
735 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
743 #ifdef HAVE_VALGRIND_MEMCHECK_H
744 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
747 f
= from_alsa_dB(value
);
752 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
754 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
755 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
756 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
757 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
761 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
762 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
763 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
771 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
774 if (f
> max_channel_volume
)
775 max_channel_volume
= f
;
777 for (k
= 0; k
< cm
->channels
; k
++)
778 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
779 if (rv
.values
[k
] < f
)
782 mask
|= e
->masks
[c
][e
->n_channels
-1];
785 for (k
= 0; k
< cm
->channels
; k
++)
786 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
787 rv
.values
[k
] = max_channel_volume
;
793 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
801 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
806 rv
= *v
; /* Remaining adjustment */
807 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
809 PA_LLIST_FOREACH(e
, p
->elements
) {
812 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
815 pa_assert(!p
->has_dB
|| e
->has_dB
);
818 if (element_set_volume(e
, m
, cm
, &ev
) < 0)
826 pa_sw_cvolume_multiply(v
, v
, &ev
);
827 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
833 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
834 snd_mixer_elem_t
*me
;
835 snd_mixer_selem_id_t
*sid
;
841 SELEM_INIT(sid
, e
->alsa_name
);
842 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
843 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
847 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
848 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
850 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
853 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
858 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
867 PA_LLIST_FOREACH(e
, p
->elements
) {
869 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
872 if (element_set_switch(e
, m
, !muted
) < 0)
879 static int element_mute_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
880 snd_mixer_elem_t
*me
;
881 snd_mixer_selem_id_t
*sid
;
887 SELEM_INIT(sid
, e
->alsa_name
);
888 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
889 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
893 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
894 r
= snd_mixer_selem_set_playback_volume_all(me
, e
->min_volume
);
896 r
= snd_mixer_selem_set_capture_volume_all(me
, e
->min_volume
);
899 pa_log_warn("Faile to set volume to muted of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
904 /* The volume to 0dB */
905 static int element_zero_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
906 snd_mixer_elem_t
*me
;
907 snd_mixer_selem_id_t
*sid
;
913 SELEM_INIT(sid
, e
->alsa_name
);
914 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
915 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
919 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
920 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
922 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
925 pa_log_warn("Faile to set volume to 0dB of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
930 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
937 pa_log_debug("Activating path %s", p
->name
);
938 pa_alsa_path_dump(p
);
940 PA_LLIST_FOREACH(e
, p
->elements
) {
942 switch (e
->switch_use
) {
943 case PA_ALSA_SWITCH_OFF
:
944 r
= element_set_switch(e
, m
, FALSE
);
947 case PA_ALSA_SWITCH_ON
:
948 r
= element_set_switch(e
, m
, TRUE
);
951 case PA_ALSA_SWITCH_MUTE
:
952 case PA_ALSA_SWITCH_IGNORE
:
953 case PA_ALSA_SWITCH_SELECT
:
961 switch (e
->volume_use
) {
962 case PA_ALSA_VOLUME_OFF
:
963 r
= element_mute_volume(e
, m
);
966 case PA_ALSA_VOLUME_ZERO
:
967 r
= element_zero_volume(e
, m
);
970 case PA_ALSA_VOLUME_MERGE
:
971 case PA_ALSA_VOLUME_IGNORE
:
983 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
984 pa_bool_t has_switch
;
985 pa_bool_t has_enumeration
;
986 pa_bool_t has_volume
;
991 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
993 snd_mixer_selem_has_playback_switch(me
) ||
994 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
997 snd_mixer_selem_has_capture_switch(me
) ||
998 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1001 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1003 snd_mixer_selem_has_playback_volume(me
) ||
1004 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1007 snd_mixer_selem_has_capture_volume(me
) ||
1008 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1011 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1013 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1014 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1015 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1018 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1021 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1022 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1023 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1026 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1032 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1033 snd_mixer_selem_id_t
*sid
;
1034 snd_mixer_elem_t
*me
;
1039 SELEM_INIT(sid
, e
->alsa_name
);
1041 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1043 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1046 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1047 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1048 e
->enumeration_use
= PA_ALSA_VOLUME_IGNORE
;
1053 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1054 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1056 if (!snd_mixer_selem_has_playback_switch(me
)) {
1057 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1058 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1060 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1065 if (!snd_mixer_selem_has_capture_switch(me
)) {
1066 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1067 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1069 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1073 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1074 e
->direction_try_other
= FALSE
;
1077 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1079 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1081 if (!snd_mixer_selem_has_playback_volume(me
)) {
1082 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1083 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1085 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1090 if (!snd_mixer_selem_has_capture_volume(me
)) {
1091 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1092 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1094 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1098 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1099 long min_dB
= 0, max_dB
= 0;
1102 e
->direction_try_other
= FALSE
;
1104 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1105 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1107 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1110 #ifdef HAVE_VALGRIND_MEMCHECK_H
1111 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1112 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1115 e
->min_dB
= ((double) min_dB
) / 100.0;
1116 e
->max_dB
= ((double) max_dB
) / 100.0;
1118 if (min_dB
>= max_dB
) {
1119 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
);
1124 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1125 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1127 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1130 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1135 if (e
->min_volume
>= e
->max_volume
) {
1136 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
);
1137 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1141 pa_channel_position_t p
;
1143 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1144 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1146 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1151 if (!e
->override_map
) {
1152 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1153 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1154 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1157 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1160 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1162 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1165 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1166 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1168 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1171 if (e
->n_channels
<= 0) {
1172 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1176 if (!e
->override_map
) {
1177 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1178 pa_bool_t has_channel
;
1180 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1183 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1184 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1186 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1188 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1193 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++)
1194 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1201 if (check_required(e
, me
) < 0)
1204 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1207 PA_LLIST_FOREACH(o
, e
->options
)
1208 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1209 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1213 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1214 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1218 PA_LLIST_FOREACH(o
, e
->options
) {
1221 for (i
= 0; i
< n
; i
++) {
1224 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1227 if (!pa_streq(buf
, o
->alsa_name
))
1238 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1245 if (!pa_startswith(section
, "Element "))
1251 /* This is not an element section, but an enum section? */
1252 if (strchr(section
, ':'))
1255 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1256 return p
->last_element
;
1258 PA_LLIST_FOREACH(e
, p
->elements
)
1259 if (pa_streq(e
->alsa_name
, section
))
1262 e
= pa_xnew0(pa_alsa_element
, 1);
1264 e
->alsa_name
= pa_xstrdup(section
);
1265 e
->direction
= p
->direction
;
1267 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1270 p
->last_element
= e
;
1274 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1280 if (!pa_startswith(section
, "Option "))
1285 /* This is not an enum section, but an element section? */
1286 if (!(on
= strchr(section
, ':')))
1289 en
= pa_xstrndup(section
, on
- section
);
1292 if (p
->last_option
&&
1293 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1294 pa_streq(p
->last_option
->alsa_name
, on
)) {
1296 return p
->last_option
;
1299 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1302 PA_LLIST_FOREACH(o
, e
->options
)
1303 if (pa_streq(o
->alsa_name
, on
))
1306 o
= pa_xnew0(pa_alsa_option
, 1);
1308 o
->alsa_name
= pa_xstrdup(on
);
1311 if (p
->last_option
&& p
->last_option
->element
== e
)
1312 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1314 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1321 static int element_parse_switch(
1322 const char *filename
,
1324 const char *section
,
1330 pa_alsa_path
*p
= userdata
;
1335 if (!(e
= element_get(p
, section
, TRUE
))) {
1336 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1340 if (pa_streq(rvalue
, "ignore"))
1341 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1342 else if (pa_streq(rvalue
, "mute"))
1343 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1344 else if (pa_streq(rvalue
, "off"))
1345 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1346 else if (pa_streq(rvalue
, "on"))
1347 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1348 else if (pa_streq(rvalue
, "select"))
1349 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1351 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1358 static int element_parse_volume(
1359 const char *filename
,
1361 const char *section
,
1367 pa_alsa_path
*p
= userdata
;
1372 if (!(e
= element_get(p
, section
, TRUE
))) {
1373 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1377 if (pa_streq(rvalue
, "ignore"))
1378 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1379 else if (pa_streq(rvalue
, "merge"))
1380 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1381 else if (pa_streq(rvalue
, "off"))
1382 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1383 else if (pa_streq(rvalue
, "zero"))
1384 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1386 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1393 static int element_parse_enumeration(
1394 const char *filename
,
1396 const char *section
,
1402 pa_alsa_path
*p
= userdata
;
1407 if (!(e
= element_get(p
, section
, TRUE
))) {
1408 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1412 if (pa_streq(rvalue
, "ignore"))
1413 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1414 else if (pa_streq(rvalue
, "select"))
1415 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1417 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1424 static int option_parse_priority(
1425 const char *filename
,
1427 const char *section
,
1433 pa_alsa_path
*p
= userdata
;
1439 if (!(o
= option_get(p
, section
))) {
1440 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1444 if (pa_atou(rvalue
, &prio
) < 0) {
1445 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1453 static int option_parse_name(
1454 const char *filename
,
1456 const char *section
,
1462 pa_alsa_path
*p
= userdata
;
1467 if (!(o
= option_get(p
, section
))) {
1468 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1473 o
->name
= pa_xstrdup(rvalue
);
1478 static int element_parse_required(
1479 const char *filename
,
1481 const char *section
,
1487 pa_alsa_path
*p
= userdata
;
1489 pa_alsa_required_t req
;
1493 if (!(e
= element_get(p
, section
, TRUE
))) {
1494 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1498 if (pa_streq(rvalue
, "ignore"))
1499 req
= PA_ALSA_REQUIRED_IGNORE
;
1500 else if (pa_streq(rvalue
, "switch"))
1501 req
= PA_ALSA_REQUIRED_SWITCH
;
1502 else if (pa_streq(rvalue
, "volume"))
1503 req
= PA_ALSA_REQUIRED_VOLUME
;
1504 else if (pa_streq(rvalue
, "enumeration"))
1505 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1506 else if (pa_streq(rvalue
, "any"))
1507 req
= PA_ALSA_REQUIRED_ANY
;
1509 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1513 if (pa_streq(lvalue
, "required-absent"))
1514 e
->required_absent
= req
;
1521 static int element_parse_direction(
1522 const char *filename
,
1524 const char *section
,
1530 pa_alsa_path
*p
= userdata
;
1535 if (!(e
= element_get(p
, section
, TRUE
))) {
1536 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1540 if (pa_streq(rvalue
, "playback"))
1541 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1542 else if (pa_streq(rvalue
, "capture"))
1543 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1545 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1552 static int element_parse_direction_try_other(
1553 const char *filename
,
1555 const char *section
,
1561 pa_alsa_path
*p
= userdata
;
1565 if (!(e
= element_get(p
, section
, TRUE
))) {
1566 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1570 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1571 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1575 e
->direction_try_other
= !!yes
;
1579 static pa_channel_position_mask_t
parse_mask(const char *m
) {
1580 pa_channel_position_mask_t v
;
1582 if (pa_streq(m
, "all-left"))
1583 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
1584 else if (pa_streq(m
, "all-right"))
1585 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
1586 else if (pa_streq(m
, "all-center"))
1587 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
1588 else if (pa_streq(m
, "all-front"))
1589 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
1590 else if (pa_streq(m
, "all-rear"))
1591 v
= PA_CHANNEL_POSITION_MASK_REAR
;
1592 else if (pa_streq(m
, "all-side"))
1593 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
1594 else if (pa_streq(m
, "all-top"))
1595 v
= PA_CHANNEL_POSITION_MASK_TOP
;
1596 else if (pa_streq(m
, "all-no-lfe"))
1597 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
1598 else if (pa_streq(m
, "all"))
1599 v
= PA_CHANNEL_POSITION_MASK_ALL
;
1601 pa_channel_position_t p
;
1603 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
1606 v
= PA_CHANNEL_POSITION_MASK(p
);
1612 static int element_parse_override_map(
1613 const char *filename
,
1615 const char *section
,
1621 pa_alsa_path
*p
= userdata
;
1623 const char *state
= NULL
;
1627 if (!(e
= element_get(p
, section
, TRUE
))) {
1628 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
1632 while ((n
= pa_split(rvalue
, ",", &state
))) {
1633 pa_channel_position_mask_t m
;
1638 if ((m
= parse_mask(n
)) == 0) {
1639 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
1645 if (pa_streq(lvalue
, "override-map.1"))
1646 e
->masks
[i
++][0] = m
;
1648 e
->masks
[i
++][1] = m
;
1650 /* Later on we might add override-map.3 and so on here ... */
1655 e
->override_map
= TRUE
;
1660 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
1661 snd_mixer_selem_id_t
*sid
;
1662 snd_mixer_elem_t
*me
;
1668 SELEM_INIT(sid
, e
->alsa_name
);
1669 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1670 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1674 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1676 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1677 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
1679 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
1682 pa_log_warn("Faile to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1685 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
1687 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
1688 pa_log_warn("Faile to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1694 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
1701 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
1702 element_set_option(o
->element
, m
, o
->alsa_idx
);
1707 static int option_verify(pa_alsa_option
*o
) {
1708 static const struct description_map well_known_descriptions
[] = {
1709 { "input", N_("Input") },
1710 { "input-docking", N_("Docking Station Input") },
1711 { "input-docking-microphone", N_("Docking Station Microphone") },
1712 { "input-linein", N_("Line-In") },
1713 { "input-microphone", N_("Microphone") },
1714 { "input-microphone-external", N_("External Microphone") },
1715 { "input-microphone-internal", N_("Internal Microphone") },
1716 { "input-radio", N_("Radio") },
1717 { "input-video", N_("Video") },
1718 { "input-agc-on", N_("Automatic Gain Control") },
1719 { "input-agc-off", "" },
1720 { "input-boost-on", N_("Boost") },
1721 { "input-boost-off", "" },
1722 { "output-amplifier-on", N_("Amplifier") },
1723 { "output-amplifier-off", "" }
1729 pa_log("No name set for option %s", o
->alsa_name
);
1733 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
1734 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
1735 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
1739 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
1740 !pa_streq(o
->alsa_name
, "on") &&
1741 !pa_streq(o
->alsa_name
, "off")) {
1742 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
1746 if (!o
->description
)
1747 o
->description
= pa_xstrdup(lookup_description(o
->name
,
1748 well_known_descriptions
,
1749 PA_ELEMENTSOF(well_known_descriptions
)));
1750 if (!o
->description
)
1751 o
->description
= pa_xstrdup(o
->name
);
1756 static int element_verify(pa_alsa_element
*e
) {
1761 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
1762 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
1763 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
1767 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1768 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
1772 PA_LLIST_FOREACH(o
, e
->options
)
1773 if (option_verify(o
) < 0)
1779 static int path_verify(pa_alsa_path
*p
) {
1780 static const struct description_map well_known_descriptions
[] = {
1781 { "analog-input", N_("Analog Input") },
1782 { "analog-input-microphone", N_("Analog Microphone") },
1783 { "analog-input-linein", N_("Analog Line-In") },
1784 { "analog-input-radio", N_("Analog Radio") },
1785 { "analog-input-video", N_("Analog Video") },
1786 { "analog-output", N_("Analog Output") },
1787 { "analog-output-headphones", N_("Analog Headphones") },
1788 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
1789 { "analog-output-mono", N_("Analog Mono Output") }
1796 PA_LLIST_FOREACH(e
, p
->elements
)
1797 if (element_verify(e
) < 0)
1800 if (!p
->description
)
1801 p
->description
= pa_xstrdup(lookup_description(p
->name
,
1802 well_known_descriptions
,
1803 PA_ELEMENTSOF(well_known_descriptions
)));
1805 if (!p
->description
)
1806 p
->description
= pa_xstrdup(p
->name
);
1811 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
1817 pa_config_item items
[] = {
1819 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
1820 { "description", pa_config_parse_string
, NULL
, "General" },
1821 { "name", pa_config_parse_string
, NULL
, "General" },
1824 { "priority", option_parse_priority
, NULL
, NULL
},
1825 { "name", option_parse_name
, NULL
, NULL
},
1828 { "switch", element_parse_switch
, NULL
, NULL
},
1829 { "volume", element_parse_volume
, NULL
, NULL
},
1830 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
1831 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
1832 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
1833 /* ... later on we might add override-map.3 and so on here ... */
1834 { "required", element_parse_required
, NULL
, NULL
},
1835 { "required-absent", element_parse_required
, NULL
, NULL
},
1836 { "direction", element_parse_direction
, NULL
, NULL
},
1837 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
1838 { NULL
, NULL
, NULL
, NULL
}
1843 p
= pa_xnew0(pa_alsa_path
, 1);
1844 n
= pa_path_get_filename(fname
);
1845 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
1846 p
->direction
= direction
;
1848 items
[0].data
= &p
->priority
;
1849 items
[1].data
= &p
->description
;
1850 items
[2].data
= &p
->name
;
1852 fn
= pa_maybe_prefix_path(fname
, PA_ALSA_PATHS_DIR
);
1853 r
= pa_config_parse(fn
, NULL
, items
, p
);
1859 if (path_verify(p
) < 0)
1865 pa_alsa_path_free(p
);
1869 pa_alsa_path
* pa_alsa_path_synthesize(const char*element
, pa_alsa_direction_t direction
) {
1875 p
= pa_xnew0(pa_alsa_path
, 1);
1876 p
->name
= pa_xstrdup(element
);
1877 p
->direction
= direction
;
1879 e
= pa_xnew0(pa_alsa_element
, 1);
1881 e
->alsa_name
= pa_xstrdup(element
);
1882 e
->direction
= direction
;
1884 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1885 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1887 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
1891 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
1892 pa_alsa_option
*o
, *n
;
1896 for (o
= e
->options
; o
; o
= n
) {
1899 if (o
->alsa_idx
< 0) {
1900 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
1906 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
1907 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
1908 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
1911 static void path_drop_unsupported(pa_alsa_path
*p
) {
1912 pa_alsa_element
*e
, *n
;
1916 for (e
= p
->elements
; e
; e
= n
) {
1919 if (!element_drop_unsupported(e
)) {
1920 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
1926 static void path_make_options_unique(pa_alsa_path
*p
) {
1928 pa_alsa_option
*o
, *u
;
1930 PA_LLIST_FOREACH(e
, p
->elements
) {
1931 PA_LLIST_FOREACH(o
, e
->options
) {
1935 for (u
= o
->next
; u
; u
= u
->next
)
1936 if (pa_streq(u
->name
, o
->name
))
1942 m
= pa_xstrdup(o
->name
);
1944 /* OK, this name is not unique, hence let's rename */
1945 for (i
= 1, u
= o
; u
; u
= u
->next
) {
1948 if (!pa_streq(u
->name
, m
))
1951 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
1955 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
1956 pa_xfree(u
->description
);
1957 u
->description
= nd
;
1967 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
1970 for (; e
; e
= e
->next
)
1971 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
1972 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
1978 for (o
= e
->options
; o
; o
= o
->next
) {
1982 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
1983 s
->options
= pa_idxset_copy(template->options
);
1984 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
1986 (template->description
[0] && o
->description
[0])
1987 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
1988 : (template->description
[0]
1989 ? pa_xstrdup(template->description
)
1990 : pa_xstrdup(o
->description
));
1992 s
->priority
= PA_MAX(template->priority
, o
->priority
);
1994 s
= pa_xnew0(pa_alsa_setting
, 1);
1995 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1996 s
->name
= pa_xstrdup(o
->name
);
1997 s
->description
= pa_xstrdup(o
->description
);
1998 s
->priority
= o
->priority
;
2001 pa_idxset_put(s
->options
, o
, NULL
);
2003 if (element_create_settings(e
->next
, s
))
2004 /* This is not a leaf, so let's get rid of it */
2007 /* This is a leaf, so let's add it */
2008 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2010 e
->path
->last_setting
= s
;
2017 static void path_create_settings(pa_alsa_path
*p
) {
2020 element_create_settings(p
->elements
, NULL
);
2023 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2025 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2026 pa_channel_position_t t
;
2037 pa_log_debug("Probing path '%s'", p
->name
);
2039 PA_LLIST_FOREACH(e
, p
->elements
) {
2040 if (element_probe(e
, m
) < 0) {
2041 p
->supported
= FALSE
;
2042 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2049 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2051 if (!p
->has_volume
) {
2052 p
->min_volume
= e
->min_volume
;
2053 p
->max_volume
= e
->max_volume
;
2057 if (!p
->has_volume
) {
2058 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2059 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2060 min_dB
[t
] = e
->min_dB
;
2061 max_dB
[t
] = e
->max_dB
;
2068 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2069 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2070 min_dB
[t
] += e
->min_dB
;
2071 max_dB
[t
] += e
->max_dB
;
2074 /* Hmm, there's another element before us
2075 * which cannot do dB volumes, so we we need
2076 * to 'neutralize' this slider */
2077 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2079 } else if (p
->has_volume
)
2080 /* We can't use this volume, so let's ignore it */
2081 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2083 p
->has_volume
= TRUE
;
2086 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2090 path_drop_unsupported(p
);
2091 path_make_options_unique(p
);
2092 path_create_settings(p
);
2094 p
->supported
= TRUE
;
2097 p
->min_dB
= INFINITY
;
2098 p
->max_dB
= -INFINITY
;
2100 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2101 if (p
->min_dB
> min_dB
[t
])
2102 p
->min_dB
= min_dB
[t
];
2104 if (p
->max_dB
< max_dB
[t
])
2105 p
->max_dB
= max_dB
[t
];
2111 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2114 pa_log_debug("Setting %s (%s) priority=%u",
2116 pa_strnull(s
->description
),
2120 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2123 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2125 pa_strnull(o
->name
),
2126 pa_strnull(o
->description
),
2131 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2135 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",
2143 (long long unsigned) e
->merged_mask
,
2145 pa_yes_no(e
->override_map
));
2147 PA_LLIST_FOREACH(o
, e
->options
)
2148 pa_alsa_option_dump(o
);
2151 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2156 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2157 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2159 pa_strnull(p
->description
),
2162 pa_yes_no(p
->probed
),
2163 pa_yes_no(p
->supported
),
2164 pa_yes_no(p
->has_mute
),
2165 pa_yes_no(p
->has_volume
),
2166 pa_yes_no(p
->has_dB
),
2167 p
->min_volume
, p
->max_volume
,
2168 p
->min_dB
, p
->max_dB
);
2170 PA_LLIST_FOREACH(e
, p
->elements
)
2171 pa_alsa_element_dump(e
);
2173 PA_LLIST_FOREACH(s
, p
->settings
)
2174 pa_alsa_setting_dump(s
);
2177 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2178 snd_mixer_selem_id_t
*sid
;
2179 snd_mixer_elem_t
*me
;
2185 SELEM_INIT(sid
, e
->alsa_name
);
2186 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2187 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2191 snd_mixer_elem_set_callback(me
, cb
);
2192 snd_mixer_elem_set_callback_private(me
, userdata
);
2195 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2202 PA_LLIST_FOREACH(e
, p
->elements
)
2203 element_set_callback(e
, m
, cb
, userdata
);
2206 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2213 PA_LLIST_FOREACH(p
, ps
->paths
)
2214 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2217 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2218 pa_alsa_path_set
*ps
;
2219 char **pn
= NULL
, **en
= NULL
, **ie
;
2222 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2224 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2227 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2228 ps
->direction
= direction
;
2230 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2231 pn
= m
->output_path_names
;
2232 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2233 pn
= m
->input_path_names
;
2238 for (in
= pn
; *in
; in
++) {
2240 pa_bool_t duplicate
= FALSE
;
2243 for (kn
= pn
; kn
!= in
; kn
++)
2244 if (pa_streq(*kn
, *in
)) {
2252 fn
= pa_sprintf_malloc("%s.conf", *in
);
2254 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2256 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2266 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2267 en
= m
->output_element
;
2268 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2269 en
= m
->input_element
;
2272 pa_alsa_path_set_free(ps
);
2276 for (ie
= en
; *ie
; ie
++) {
2280 p
= pa_alsa_path_synthesize(*ie
, direction
);
2283 /* Mark all other passed elements for require-absent */
2284 for (je
= en
; *je
; je
++) {
2286 e
= pa_xnew0(pa_alsa_element
, 1);
2288 e
->alsa_name
= pa_xstrdup(*je
);
2289 e
->direction
= direction
;
2290 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2292 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2293 p
->last_element
= e
;
2296 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2303 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2307 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2310 pa_yes_no(ps
->probed
));
2312 PA_LLIST_FOREACH(p
, ps
->paths
)
2313 pa_alsa_path_dump(p
);
2316 static void path_set_unify(pa_alsa_path_set
*ps
) {
2318 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2321 /* We have issues dealing with paths that vary too wildly. That
2322 * means for now we have to have all paths support volume/mute/dB
2325 PA_LLIST_FOREACH(p
, ps
->paths
) {
2326 pa_assert(p
->probed
);
2330 else if (!p
->has_dB
)
2337 if (!has_volume
|| !has_dB
|| !has_mute
) {
2340 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2342 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2345 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2347 PA_LLIST_FOREACH(p
, ps
->paths
) {
2349 p
->has_volume
= FALSE
;
2354 p
->has_mute
= FALSE
;
2359 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2360 pa_alsa_path
*p
, *q
;
2362 PA_LLIST_FOREACH(p
, ps
->paths
) {
2366 for (q
= p
->next
; q
; q
= q
->next
)
2367 if (pa_streq(q
->name
, p
->name
))
2373 m
= pa_xstrdup(p
->name
);
2375 /* OK, this name is not unique, hence let's rename */
2376 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2379 if (!pa_streq(q
->name
, m
))
2382 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2386 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2387 pa_xfree(q
->description
);
2388 q
->description
= nd
;
2397 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2398 pa_alsa_path
*p
, *n
;
2405 for (p
= ps
->paths
; p
; p
= n
) {
2408 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2409 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2410 pa_alsa_path_free(p
);
2415 path_set_make_paths_unique(ps
);
2419 static void mapping_free(pa_alsa_mapping
*m
) {
2423 pa_xfree(m
->description
);
2425 pa_xstrfreev(m
->device_strings
);
2426 pa_xstrfreev(m
->input_path_names
);
2427 pa_xstrfreev(m
->output_path_names
);
2428 pa_xstrfreev(m
->input_element
);
2429 pa_xstrfreev(m
->output_element
);
2431 pa_assert(!m
->input_pcm
);
2432 pa_assert(!m
->output_pcm
);
2437 static void profile_free(pa_alsa_profile
*p
) {
2441 pa_xfree(p
->description
);
2443 pa_xstrfreev(p
->input_mapping_names
);
2444 pa_xstrfreev(p
->output_mapping_names
);
2446 if (p
->input_mappings
)
2447 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2449 if (p
->output_mappings
)
2450 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2455 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2461 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2464 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2470 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2473 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2479 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2482 if (!pa_startswith(name
, "Mapping "))
2487 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2490 m
= pa_xnew0(pa_alsa_mapping
, 1);
2491 m
->profile_set
= ps
;
2492 m
->name
= pa_xstrdup(name
);
2493 pa_channel_map_init(&m
->channel_map
);
2495 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
2500 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
2503 if (!pa_startswith(name
, "Profile "))
2508 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
2511 p
= pa_xnew0(pa_alsa_profile
, 1);
2512 p
->profile_set
= ps
;
2513 p
->name
= pa_xstrdup(name
);
2515 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2520 static int mapping_parse_device_strings(
2521 const char *filename
,
2523 const char *section
,
2529 pa_alsa_profile_set
*ps
= userdata
;
2534 if (!(m
= mapping_get(ps
, section
))) {
2535 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2539 pa_xstrfreev(m
->device_strings
);
2540 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
2541 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
2548 static int mapping_parse_channel_map(
2549 const char *filename
,
2551 const char *section
,
2557 pa_alsa_profile_set
*ps
= userdata
;
2562 if (!(m
= mapping_get(ps
, section
))) {
2563 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2567 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
2568 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
2575 static int mapping_parse_paths(
2576 const char *filename
,
2578 const char *section
,
2584 pa_alsa_profile_set
*ps
= userdata
;
2589 if (!(m
= mapping_get(ps
, section
))) {
2590 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2594 if (pa_streq(lvalue
, "paths-input")) {
2595 pa_xstrfreev(m
->input_path_names
);
2596 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
2598 pa_xstrfreev(m
->output_path_names
);
2599 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
2605 static int mapping_parse_element(
2606 const char *filename
,
2608 const char *section
,
2614 pa_alsa_profile_set
*ps
= userdata
;
2619 if (!(m
= mapping_get(ps
, section
))) {
2620 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2624 if (pa_streq(lvalue
, "element-input")) {
2625 pa_xstrfreev(m
->input_element
);
2626 m
->input_element
= pa_split_spaces_strv(rvalue
);
2628 pa_xstrfreev(m
->output_element
);
2629 m
->output_element
= pa_split_spaces_strv(rvalue
);
2635 static int mapping_parse_direction(
2636 const char *filename
,
2638 const char *section
,
2644 pa_alsa_profile_set
*ps
= userdata
;
2649 if (!(m
= mapping_get(ps
, section
))) {
2650 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2654 if (pa_streq(rvalue
, "input"))
2655 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
2656 else if (pa_streq(rvalue
, "output"))
2657 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
2658 else if (pa_streq(rvalue
, "any"))
2659 m
->direction
= PA_ALSA_DIRECTION_ANY
;
2661 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
2668 static int mapping_parse_description(
2669 const char *filename
,
2671 const char *section
,
2677 pa_alsa_profile_set
*ps
= userdata
;
2683 if ((m
= mapping_get(ps
, section
))) {
2684 pa_xstrdup(m
->description
);
2685 m
->description
= pa_xstrdup(rvalue
);
2686 } else if ((p
= profile_get(ps
, section
))) {
2687 pa_xfree(p
->description
);
2688 p
->description
= pa_xstrdup(rvalue
);
2690 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2697 static int mapping_parse_priority(
2698 const char *filename
,
2700 const char *section
,
2706 pa_alsa_profile_set
*ps
= userdata
;
2713 if (pa_atou(rvalue
, &prio
) < 0) {
2714 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
2718 if ((m
= mapping_get(ps
, section
)))
2720 else if ((p
= profile_get(ps
, section
)))
2723 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
2730 static int profile_parse_mappings(
2731 const char *filename
,
2733 const char *section
,
2739 pa_alsa_profile_set
*ps
= userdata
;
2744 if (!(p
= profile_get(ps
, section
))) {
2745 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2749 if (pa_streq(lvalue
, "input-mappings")) {
2750 pa_xstrfreev(p
->input_mapping_names
);
2751 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
2753 pa_xstrfreev(p
->output_mapping_names
);
2754 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
2760 static int profile_parse_skip_probe(
2761 const char *filename
,
2763 const char *section
,
2769 pa_alsa_profile_set
*ps
= userdata
;
2775 if (!(p
= profile_get(ps
, section
))) {
2776 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
2780 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
2781 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
2790 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
2792 static const struct description_map well_known_descriptions
[] = {
2793 { "analog-mono", N_("Analog Mono") },
2794 { "analog-stereo", N_("Analog Stereo") },
2795 { "analog-surround-21", N_("Analog Surround 2.1") },
2796 { "analog-surround-30", N_("Analog Surround 3.0") },
2797 { "analog-surround-31", N_("Analog Surround 3.1") },
2798 { "analog-surround-40", N_("Analog Surround 4.0") },
2799 { "analog-surround-41", N_("Analog Surround 4.1") },
2800 { "analog-surround-50", N_("Analog Surround 5.0") },
2801 { "analog-surround-51", N_("Analog Surround 5.1") },
2802 { "analog-surround-61", N_("Analog Surround 6.0") },
2803 { "analog-surround-61", N_("Analog Surround 6.1") },
2804 { "analog-surround-70", N_("Analog Surround 7.0") },
2805 { "analog-surround-71", N_("Analog Surround 7.1") },
2806 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
2807 { "iec958-surround-40", N_("Digital Surround 4.0 (IEC958)") },
2808 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
2809 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
2810 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
2815 if (!pa_channel_map_valid(&m
->channel_map
)) {
2816 pa_log("Mapping %s is missing channel map.", m
->name
);
2820 if (!m
->device_strings
) {
2821 pa_log("Mapping %s is missing device strings.", m
->name
);
2825 if ((m
->input_path_names
&& m
->input_element
) ||
2826 (m
->output_path_names
&& m
->output_element
)) {
2827 pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m
->name
);
2831 if (!m
->description
)
2832 m
->description
= pa_xstrdup(lookup_description(m
->name
,
2833 well_known_descriptions
,
2834 PA_ELEMENTSOF(well_known_descriptions
)));
2836 if (!m
->description
)
2837 m
->description
= pa_xstrdup(m
->name
);
2840 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
2842 else if (m
->channel_map
.channels
== bonus
->channels
)
2849 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
2850 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
2854 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
2856 pa_strnull(m
->description
),
2858 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
2859 pa_yes_no(m
->supported
),
2863 static void profile_set_add_auto_pair(
2864 pa_alsa_profile_set
*ps
,
2865 pa_alsa_mapping
*m
, /* output */
2866 pa_alsa_mapping
*n
/* input */) {
2874 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
2877 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2881 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
2883 name
= pa_sprintf_malloc("output:%s", m
->name
);
2885 name
= pa_sprintf_malloc("input:%s", n
->name
);
2887 if ((p
= pa_hashmap_get(ps
->profiles
, name
))) {
2892 p
= pa_xnew0(pa_alsa_profile
, 1);
2893 p
->profile_set
= ps
;
2897 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2898 pa_idxset_put(p
->output_mappings
, m
, NULL
);
2899 p
->priority
+= m
->priority
* 100;
2903 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2904 pa_idxset_put(p
->input_mappings
, n
, NULL
);
2905 p
->priority
+= n
->priority
;
2908 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
2911 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
2912 pa_alsa_mapping
*m
, *n
;
2913 void *m_state
, *n_state
;
2917 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
2918 profile_set_add_auto_pair(ps
, m
, NULL
);
2920 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
2921 profile_set_add_auto_pair(ps
, m
, n
);
2924 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
2925 profile_set_add_auto_pair(ps
, NULL
, n
);
2928 static int profile_verify(pa_alsa_profile
*p
) {
2930 static const struct description_map well_known_descriptions
[] = {
2931 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
2932 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
2933 { "output:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
2934 { "off", N_("Off") }
2939 /* Replace the output mapping names by the actual mappings */
2940 if (p
->output_mapping_names
) {
2943 pa_assert(!p
->output_mappings
);
2944 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2946 for (name
= p
->output_mapping_names
; *name
; name
++) {
2949 pa_bool_t duplicate
= FALSE
;
2951 for (in
= name
+ 1; *in
; in
++)
2952 if (pa_streq(*name
, *in
)) {
2960 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
2961 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
2965 pa_idxset_put(p
->output_mappings
, m
, NULL
);
2971 pa_xstrfreev(p
->output_mapping_names
);
2972 p
->output_mapping_names
= NULL
;
2975 /* Replace the input mapping names by the actual mappings */
2976 if (p
->input_mapping_names
) {
2979 pa_assert(!p
->input_mappings
);
2980 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2982 for (name
= p
->input_mapping_names
; *name
; name
++) {
2985 pa_bool_t duplicate
= FALSE
;
2987 for (in
= name
+ 1; *in
; in
++)
2988 if (pa_streq(*name
, *in
)) {
2996 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
2997 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3001 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3007 pa_xstrfreev(p
->input_mapping_names
);
3008 p
->input_mapping_names
= NULL
;
3011 if (!p
->input_mappings
&& !p
->output_mappings
) {
3012 pa_log("Profile '%s' lacks mappings.", p
->name
);
3016 if (!p
->description
)
3017 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3018 well_known_descriptions
,
3019 PA_ELEMENTSOF(well_known_descriptions
)));
3021 if (!p
->description
) {
3026 sb
= pa_strbuf_new();
3028 if (p
->output_mappings
)
3029 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3030 if (!pa_strbuf_isempty(sb
))
3031 pa_strbuf_puts(sb
, " + ");
3033 pa_strbuf_printf(sb
, "%s Output", m
->description
);
3036 if (p
->input_mappings
)
3037 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3038 if (!pa_strbuf_isempty(sb
))
3039 pa_strbuf_puts(sb
, " + ");
3041 pa_strbuf_printf(sb
, "%s Input", m
->description
);
3044 p
->description
= pa_strbuf_tostring_free(sb
);
3050 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3055 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3057 pa_strnull(p
->description
),
3059 pa_yes_no(p
->supported
),
3060 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3061 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3063 if (p
->input_mappings
)
3064 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3065 pa_log_debug("Input %s", m
->name
);
3067 if (p
->output_mappings
)
3068 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3069 pa_log_debug("Output %s", m
->name
);
3072 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3073 pa_alsa_profile_set
*ps
;
3080 static pa_config_item items
[] = {
3082 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3085 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3086 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3087 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3088 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3089 { "element-input", mapping_parse_element
, NULL
, NULL
},
3090 { "element-output", mapping_parse_element
, NULL
, NULL
},
3091 { "direction", mapping_parse_direction
, NULL
, NULL
},
3093 /* Shared by [Mapping ...] and [Profile ...] */
3094 { "description", mapping_parse_description
, NULL
, NULL
},
3095 { "priority", mapping_parse_priority
, NULL
, NULL
},
3098 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3099 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3100 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3101 { NULL
, NULL
, NULL
, NULL
}
3104 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3105 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3106 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3108 items
[0].data
= &ps
->auto_profiles
;
3111 fname
= "default.conf";
3113 fn
= pa_maybe_prefix_path(fname
, PA_ALSA_PROFILE_SETS_DIR
);
3114 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3120 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3121 if (mapping_verify(m
, bonus
) < 0)
3124 if (ps
->auto_profiles
)
3125 profile_set_add_auto(ps
);
3127 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3128 if (profile_verify(p
) < 0)
3134 pa_alsa_profile_set_free(ps
);
3138 void pa_alsa_profile_set_probe(pa_alsa_profile_set
*ps
, const char *dev_id
, const pa_sample_spec
*ss
) {
3140 pa_alsa_profile
*p
, *last
= NULL
;
3150 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3151 pa_sample_spec try_ss
;
3152 pa_channel_map try_map
;
3155 /* Is this already marked that it is supported? (i.e. from the config file) */
3159 pa_log_debug("Looking at profile %s", p
->name
);
3161 /* Close PCMs from the last iteration we don't need anymore */
3162 if (last
&& last
->output_mappings
)
3163 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3168 if (last
->supported
)
3171 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3172 snd_pcm_close(m
->output_pcm
);
3173 m
->output_pcm
= NULL
;
3177 if (last
&& last
->input_mappings
)
3178 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3183 if (last
->supported
)
3186 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3187 snd_pcm_close(m
->input_pcm
);
3188 m
->input_pcm
= NULL
;
3192 p
->supported
= TRUE
;
3194 /* Check if we can open all new ones */
3195 if (p
->output_mappings
)
3196 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3201 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3202 try_map
= m
->channel_map
;
3204 try_ss
.channels
= try_map
.channels
;
3206 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3211 SND_PCM_STREAM_PLAYBACK
,
3212 NULL
, NULL
, 0, NULL
, NULL
,
3214 p
->supported
= FALSE
;
3219 if (p
->input_mappings
&& p
->supported
)
3220 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3225 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3226 try_map
= m
->channel_map
;
3228 try_ss
.channels
= try_map
.channels
;
3230 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3235 SND_PCM_STREAM_CAPTURE
,
3236 NULL
, NULL
, 0, NULL
, NULL
,
3238 p
->supported
= FALSE
;
3246 pa_log_debug("Profile %s supported.", p
->name
);
3253 if (last
->output_mappings
)
3254 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3255 if (m
->output_pcm
) {
3257 if (last
->supported
)
3260 snd_pcm_close(m
->output_pcm
);
3261 m
->output_pcm
= NULL
;
3264 if (last
->input_mappings
)
3265 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3268 if (last
->supported
)
3271 snd_pcm_close(m
->input_pcm
);
3272 m
->input_pcm
= NULL
;
3276 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3277 if (!p
->supported
) {
3278 pa_hashmap_remove(ps
->profiles
, p
->name
);
3282 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3283 if (m
->supported
<= 0) {
3284 pa_hashmap_remove(ps
->mappings
, m
->name
);
3291 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
3298 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u",
3301 pa_yes_no(ps
->auto_profiles
),
3302 pa_yes_no(ps
->probed
),
3303 pa_hashmap_size(ps
->mappings
),
3304 pa_hashmap_size(ps
->profiles
));
3306 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3307 pa_alsa_mapping_dump(m
);
3309 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3310 pa_alsa_profile_dump(p
);
3313 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
3320 /* if there is no path, we don't want a port list */
3324 if (!ps
->paths
->next
){
3327 /* If there is only one path, but no or only one setting, then
3328 * we want a port list either */
3329 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
3332 /* Ok, there is only one path, however with multiple settings,
3333 * so let's create a port for each setting */
3334 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3336 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
3337 pa_device_port
*port
;
3338 pa_alsa_port_data
*data
;
3340 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
3341 port
->priority
= s
->priority
;
3343 data
= PA_DEVICE_PORT_DATA(port
);
3344 data
->path
= ps
->paths
;
3347 pa_hashmap_put(*p
, port
->name
, port
);
3352 /* We have multiple paths, so let's create a port for each
3353 * one, and each of each settings */
3354 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3356 PA_LLIST_FOREACH(path
, ps
->paths
) {
3358 if (!path
->settings
|| !path
->settings
->next
) {
3359 pa_device_port
*port
;
3360 pa_alsa_port_data
*data
;
3362 /* If there is no or just one setting we only need a
3365 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
3366 port
->priority
= path
->priority
* 100;
3369 data
= PA_DEVICE_PORT_DATA(port
);
3371 data
->setting
= path
->settings
;
3373 pa_hashmap_put(*p
, port
->name
, port
);
3377 PA_LLIST_FOREACH(s
, path
->settings
) {
3378 pa_device_port
*port
;
3379 pa_alsa_port_data
*data
;
3382 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
3384 if (s
->description
[0])
3385 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
3387 d
= pa_xstrdup(path
->description
);
3389 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
3390 port
->priority
= path
->priority
* 100 + s
->priority
;
3395 data
= PA_DEVICE_PORT_DATA(port
);
3399 pa_hashmap_put(*p
, port
->name
, port
);
3405 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));