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 struct pa_alsa_mixer_pdata
{
248 pa_rtpoll_item
*poll_item
;
253 struct pa_alsa_mixer_pdata
*pa_alsa_mixer_pdata_new(void) {
254 struct pa_alsa_mixer_pdata
*pd
;
256 pd
= pa_xnew0(struct pa_alsa_mixer_pdata
, 1);
261 void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata
*pd
) {
265 pa_rtpoll_item_free(pd
->poll_item
);
271 static int rtpoll_work_cb(pa_rtpoll_item
*i
) {
272 struct pa_alsa_mixer_pdata
*pd
;
275 unsigned short revents
= 0;
278 pd
= pa_rtpoll_item_get_userdata(i
);
280 pa_assert_fp(i
== pd
->poll_item
);
282 p
= pa_rtpoll_item_get_pollfd(i
, &n_fds
);
284 if ((err
= snd_mixer_poll_descriptors_revents(pd
->mixer
, p
, n_fds
, &revents
)) < 0) {
285 pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err
));
286 pa_rtpoll_item_free(i
);
291 snd_mixer_handle_events(pd
->mixer
);
292 pa_rtpoll_item_free(i
);
293 pa_alsa_set_mixer_rtpoll(pd
, pd
->mixer
, pd
->rtpoll
);
299 int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata
*pd
, snd_mixer_t
*mixer
, pa_rtpoll
*rtp
) {
308 if ((n
= snd_mixer_poll_descriptors_count(mixer
)) < 0) {
309 pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n
));
313 i
= pa_rtpoll_item_new(rtp
, PA_RTPOLL_LATE
, (unsigned) n
);
315 p
= pa_rtpoll_item_get_pollfd(i
, NULL
);
317 memset(p
, 0, sizeof(struct pollfd
) * n
);
319 if ((err
= snd_mixer_poll_descriptors(mixer
, p
, (unsigned) n
)) < 0) {
320 pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err
));
321 pa_rtpoll_item_free(i
);
329 pa_rtpoll_item_set_userdata(i
, pd
);
330 pa_rtpoll_item_set_work_callback(i
, rtpoll_work_cb
);
335 static int prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
341 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
342 pa_log_info("Unable to attach to mixer %s: %s", dev
, pa_alsa_strerror(err
));
346 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
347 pa_log_warn("Unable to register mixer: %s", pa_alsa_strerror(err
));
351 if ((err
= snd_mixer_load(mixer
)) < 0) {
352 pa_log_warn("Unable to load mixer: %s", pa_alsa_strerror(err
));
356 pa_log_info("Successfully attached to mixer '%s'", dev
);
360 snd_mixer_t
*pa_alsa_open_mixer_for_pcm(snd_pcm_t
*pcm
, char **ctl_device
) {
364 snd_pcm_info_t
* info
;
365 snd_pcm_info_alloca(&info
);
369 if ((err
= snd_mixer_open(&m
, 0)) < 0) {
370 pa_log("Error opening mixer: %s", pa_alsa_strerror(err
));
374 /* First, try by name */
375 if ((dev
= snd_pcm_name(pcm
)))
376 if (prepare_mixer(m
, dev
) >= 0) {
378 *ctl_device
= pa_xstrdup(dev
);
383 /* Then, try by card index */
384 if (snd_pcm_info(pcm
, info
) >= 0) {
388 if ((card_idx
= snd_pcm_info_get_card(info
)) >= 0) {
390 md
= pa_sprintf_malloc("hw:%i", card_idx
);
392 if (!dev
|| !pa_streq(dev
, md
))
393 if (prepare_mixer(m
, md
) >= 0) {
411 static const snd_mixer_selem_channel_id_t alsa_channel_ids
[PA_CHANNEL_POSITION_MAX
] = {
412 [PA_CHANNEL_POSITION_MONO
] = SND_MIXER_SCHN_MONO
, /* The ALSA name is just an alias! */
414 [PA_CHANNEL_POSITION_FRONT_CENTER
] = SND_MIXER_SCHN_FRONT_CENTER
,
415 [PA_CHANNEL_POSITION_FRONT_LEFT
] = SND_MIXER_SCHN_FRONT_LEFT
,
416 [PA_CHANNEL_POSITION_FRONT_RIGHT
] = SND_MIXER_SCHN_FRONT_RIGHT
,
418 [PA_CHANNEL_POSITION_REAR_CENTER
] = SND_MIXER_SCHN_REAR_CENTER
,
419 [PA_CHANNEL_POSITION_REAR_LEFT
] = SND_MIXER_SCHN_REAR_LEFT
,
420 [PA_CHANNEL_POSITION_REAR_RIGHT
] = SND_MIXER_SCHN_REAR_RIGHT
,
422 [PA_CHANNEL_POSITION_LFE
] = SND_MIXER_SCHN_WOOFER
,
424 [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
425 [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
427 [PA_CHANNEL_POSITION_SIDE_LEFT
] = SND_MIXER_SCHN_SIDE_LEFT
,
428 [PA_CHANNEL_POSITION_SIDE_RIGHT
] = SND_MIXER_SCHN_SIDE_RIGHT
,
430 [PA_CHANNEL_POSITION_AUX0
] = SND_MIXER_SCHN_UNKNOWN
,
431 [PA_CHANNEL_POSITION_AUX1
] = SND_MIXER_SCHN_UNKNOWN
,
432 [PA_CHANNEL_POSITION_AUX2
] = SND_MIXER_SCHN_UNKNOWN
,
433 [PA_CHANNEL_POSITION_AUX3
] = SND_MIXER_SCHN_UNKNOWN
,
434 [PA_CHANNEL_POSITION_AUX4
] = SND_MIXER_SCHN_UNKNOWN
,
435 [PA_CHANNEL_POSITION_AUX5
] = SND_MIXER_SCHN_UNKNOWN
,
436 [PA_CHANNEL_POSITION_AUX6
] = SND_MIXER_SCHN_UNKNOWN
,
437 [PA_CHANNEL_POSITION_AUX7
] = SND_MIXER_SCHN_UNKNOWN
,
438 [PA_CHANNEL_POSITION_AUX8
] = SND_MIXER_SCHN_UNKNOWN
,
439 [PA_CHANNEL_POSITION_AUX9
] = SND_MIXER_SCHN_UNKNOWN
,
440 [PA_CHANNEL_POSITION_AUX10
] = SND_MIXER_SCHN_UNKNOWN
,
441 [PA_CHANNEL_POSITION_AUX11
] = SND_MIXER_SCHN_UNKNOWN
,
442 [PA_CHANNEL_POSITION_AUX12
] = SND_MIXER_SCHN_UNKNOWN
,
443 [PA_CHANNEL_POSITION_AUX13
] = SND_MIXER_SCHN_UNKNOWN
,
444 [PA_CHANNEL_POSITION_AUX14
] = SND_MIXER_SCHN_UNKNOWN
,
445 [PA_CHANNEL_POSITION_AUX15
] = SND_MIXER_SCHN_UNKNOWN
,
446 [PA_CHANNEL_POSITION_AUX16
] = SND_MIXER_SCHN_UNKNOWN
,
447 [PA_CHANNEL_POSITION_AUX17
] = SND_MIXER_SCHN_UNKNOWN
,
448 [PA_CHANNEL_POSITION_AUX18
] = SND_MIXER_SCHN_UNKNOWN
,
449 [PA_CHANNEL_POSITION_AUX19
] = SND_MIXER_SCHN_UNKNOWN
,
450 [PA_CHANNEL_POSITION_AUX20
] = SND_MIXER_SCHN_UNKNOWN
,
451 [PA_CHANNEL_POSITION_AUX21
] = SND_MIXER_SCHN_UNKNOWN
,
452 [PA_CHANNEL_POSITION_AUX22
] = SND_MIXER_SCHN_UNKNOWN
,
453 [PA_CHANNEL_POSITION_AUX23
] = SND_MIXER_SCHN_UNKNOWN
,
454 [PA_CHANNEL_POSITION_AUX24
] = SND_MIXER_SCHN_UNKNOWN
,
455 [PA_CHANNEL_POSITION_AUX25
] = SND_MIXER_SCHN_UNKNOWN
,
456 [PA_CHANNEL_POSITION_AUX26
] = SND_MIXER_SCHN_UNKNOWN
,
457 [PA_CHANNEL_POSITION_AUX27
] = SND_MIXER_SCHN_UNKNOWN
,
458 [PA_CHANNEL_POSITION_AUX28
] = SND_MIXER_SCHN_UNKNOWN
,
459 [PA_CHANNEL_POSITION_AUX29
] = SND_MIXER_SCHN_UNKNOWN
,
460 [PA_CHANNEL_POSITION_AUX30
] = SND_MIXER_SCHN_UNKNOWN
,
461 [PA_CHANNEL_POSITION_AUX31
] = SND_MIXER_SCHN_UNKNOWN
,
463 [PA_CHANNEL_POSITION_TOP_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
465 [PA_CHANNEL_POSITION_TOP_FRONT_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
466 [PA_CHANNEL_POSITION_TOP_FRONT_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
467 [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
,
469 [PA_CHANNEL_POSITION_TOP_REAR_CENTER
] = SND_MIXER_SCHN_UNKNOWN
,
470 [PA_CHANNEL_POSITION_TOP_REAR_LEFT
] = SND_MIXER_SCHN_UNKNOWN
,
471 [PA_CHANNEL_POSITION_TOP_REAR_RIGHT
] = SND_MIXER_SCHN_UNKNOWN
474 static void setting_free(pa_alsa_setting
*s
) {
478 pa_idxset_free(s
->options
, NULL
, NULL
);
481 pa_xfree(s
->description
);
485 static void option_free(pa_alsa_option
*o
) {
488 pa_xfree(o
->alsa_name
);
490 pa_xfree(o
->description
);
494 static void decibel_fix_free(pa_alsa_decibel_fix
*db_fix
) {
497 pa_xfree(db_fix
->name
);
498 pa_xfree(db_fix
->db_values
);
503 static void element_free(pa_alsa_element
*e
) {
507 while ((o
= e
->options
)) {
508 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
513 decibel_fix_free(e
->db_fix
);
515 pa_xfree(e
->alsa_name
);
519 void pa_alsa_path_free(pa_alsa_path
*p
) {
525 while ((e
= p
->elements
)) {
526 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
530 while ((s
= p
->settings
)) {
531 PA_LLIST_REMOVE(pa_alsa_setting
, p
->settings
, s
);
536 pa_xfree(p
->description
);
540 void pa_alsa_path_set_free(pa_alsa_path_set
*ps
) {
544 while ((p
= ps
->paths
)) {
545 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
546 pa_alsa_path_free(p
);
552 static long to_alsa_dB(pa_volume_t v
) {
553 return (long) (pa_sw_volume_to_dB(v
) * 100.0);
556 static pa_volume_t
from_alsa_dB(long v
) {
557 return pa_sw_volume_from_dB((double) v
/ 100.0);
560 static long to_alsa_volume(pa_volume_t v
, long min
, long max
) {
563 w
= (long) round(((double) v
* (double) (max
- min
)) / PA_VOLUME_NORM
) + min
;
564 return PA_CLAMP_UNLIKELY(w
, min
, max
);
567 static pa_volume_t
from_alsa_volume(long v
, long min
, long max
) {
568 return (pa_volume_t
) round(((double) (v
- min
) * PA_VOLUME_NORM
) / (double) (max
- min
));
571 #define SELEM_INIT(sid, name) \
573 snd_mixer_selem_id_alloca(&(sid)); \
574 snd_mixer_selem_id_set_name((sid), (name)); \
575 snd_mixer_selem_id_set_index((sid), 0); \
578 static int element_get_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
579 snd_mixer_selem_id_t
*sid
;
580 snd_mixer_elem_t
*me
;
581 snd_mixer_selem_channel_id_t c
;
582 pa_channel_position_mask_t mask
= 0;
590 SELEM_INIT(sid
, e
->alsa_name
);
591 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
592 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
596 pa_cvolume_mute(v
, cm
->channels
);
598 /* We take the highest volume of all channels that match */
600 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
607 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
608 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
610 if ((r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
)) >= 0) {
611 /* If the channel volume is outside the limits set
612 * by the dB fix, we clamp the hw volume to be
613 * within the limits. */
614 if (value
< e
->db_fix
->min_step
) {
615 value
= e
->db_fix
->min_step
;
616 snd_mixer_selem_set_playback_volume(me
, c
, value
);
617 pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
618 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
619 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
620 } else if (value
> e
->db_fix
->max_step
) {
621 value
= e
->db_fix
->max_step
;
622 snd_mixer_selem_set_playback_volume(me
, c
, value
);
623 pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
624 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
625 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
628 /* Volume step -> dB value conversion. */
629 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
632 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
636 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
638 if ((r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
)) >= 0) {
639 /* If the channel volume is outside the limits set
640 * by the dB fix, we clamp the hw volume to be
641 * within the limits. */
642 if (value
< e
->db_fix
->min_step
) {
643 value
= e
->db_fix
->min_step
;
644 snd_mixer_selem_set_capture_volume(me
, c
, value
);
645 pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
646 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
647 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
648 } else if (value
> e
->db_fix
->max_step
) {
649 value
= e
->db_fix
->max_step
;
650 snd_mixer_selem_set_capture_volume(me
, c
, value
);
651 pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
652 "Volume reset to %0.2f dB.", e
->alsa_name
, c
,
653 e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
] / 100.0);
656 /* Volume step -> dB value conversion. */
657 value
= e
->db_fix
->db_values
[value
- e
->db_fix
->min_step
];
660 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
668 #ifdef HAVE_VALGRIND_MEMCHECK_H
669 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
672 f
= from_alsa_dB(value
);
677 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
678 if (snd_mixer_selem_has_playback_channel(me
, c
))
679 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
683 if (snd_mixer_selem_has_capture_channel(me
, c
))
684 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
692 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
695 for (k
= 0; k
< cm
->channels
; k
++)
696 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
697 if (v
->values
[k
] < f
)
700 mask
|= e
->masks
[c
][e
->n_channels
-1];
703 for (k
= 0; k
< cm
->channels
; k
++)
704 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
705 v
->values
[k
] = PA_VOLUME_NORM
;
710 int pa_alsa_path_get_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
) {
721 pa_cvolume_reset(v
, cm
->channels
);
723 PA_LLIST_FOREACH(e
, p
->elements
) {
726 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
729 pa_assert(!p
->has_dB
|| e
->has_dB
);
731 if (element_get_volume(e
, m
, cm
, &ev
) < 0)
734 /* If we have no dB information all we can do is take the first element and leave */
740 pa_sw_cvolume_multiply(v
, v
, &ev
);
746 static int element_get_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t
*b
) {
747 snd_mixer_selem_id_t
*sid
;
748 snd_mixer_elem_t
*me
;
749 snd_mixer_selem_channel_id_t c
;
755 SELEM_INIT(sid
, e
->alsa_name
);
756 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
757 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
761 /* We return muted if at least one channel is muted */
763 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
767 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
768 if (snd_mixer_selem_has_playback_channel(me
, c
))
769 r
= snd_mixer_selem_get_playback_switch(me
, c
, &value
);
773 if (snd_mixer_selem_has_capture_channel(me
, c
))
774 r
= snd_mixer_selem_get_capture_switch(me
, c
, &value
);
792 int pa_alsa_path_get_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t
*muted
) {
802 PA_LLIST_FOREACH(e
, p
->elements
) {
805 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
808 if (element_get_switch(e
, m
, &b
) < 0)
821 /* Finds the closest item in db_fix->db_values and returns the corresponding
822 * step. *db_value is replaced with the value from the db_values table.
823 * Rounding is done based on the rounding parameter: -1 means rounding down and
824 * +1 means rounding up. */
825 static long decibel_fix_get_step(pa_alsa_decibel_fix
*db_fix
, long *db_value
, int rounding
) {
831 pa_assert(rounding
!= 0);
833 max_i
= db_fix
->max_step
- db_fix
->min_step
;
836 for (i
= 0; i
< max_i
; i
++) {
837 if (db_fix
->db_values
[i
] >= *db_value
)
841 for (i
= 0; i
< max_i
; i
++) {
842 if (db_fix
->db_values
[i
+ 1] > *db_value
)
847 *db_value
= db_fix
->db_values
[i
];
849 return i
+ db_fix
->min_step
;
852 static int element_set_volume(pa_alsa_element
*e
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t write_to_hw
) {
854 snd_mixer_selem_id_t
*sid
;
856 snd_mixer_elem_t
*me
;
857 snd_mixer_selem_channel_id_t c
;
858 pa_channel_position_mask_t mask
= 0;
865 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
867 SELEM_INIT(sid
, e
->alsa_name
);
868 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
869 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
873 pa_cvolume_mute(&rv
, cm
->channels
);
875 for (c
= 0; c
<= SND_MIXER_SCHN_LAST
; c
++) {
877 pa_volume_t f
= PA_VOLUME_MUTED
;
878 pa_bool_t found
= FALSE
;
880 for (k
= 0; k
< cm
->channels
; k
++)
881 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
])) {
883 if (v
->values
[k
] > f
)
888 /* Hmm, so this channel does not exist in the volume
889 * struct, so let's bind it to the overall max of the
891 f
= pa_cvolume_max(v
);
895 long value
= to_alsa_dB(f
);
896 int rounding
= value
> 0 ? -1 : +1;
898 if (e
->volume_limit
>= 0 && value
> (e
->max_dB
* 100))
899 value
= e
->max_dB
* 100;
901 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
902 /* If we call set_playback_volume() without checking first
903 * if the channel is available, ALSA behaves very
904 * strangely and doesn't fail the call */
905 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
908 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
910 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
916 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
917 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
920 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
921 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
927 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
930 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
932 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
938 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
939 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
942 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
943 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
953 #ifdef HAVE_VALGRIND_MEMCHECK_H
954 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
957 f
= from_alsa_dB(value
);
962 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
964 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
965 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
966 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
967 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
971 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
972 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
973 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
981 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
984 for (k
= 0; k
< cm
->channels
; k
++)
985 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
986 if (rv
.values
[k
] < f
)
989 mask
|= e
->masks
[c
][e
->n_channels
-1];
992 for (k
= 0; k
< cm
->channels
; k
++)
993 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
994 rv
.values
[k
] = PA_VOLUME_NORM
;
1000 int pa_alsa_path_set_volume(pa_alsa_path
*p
, snd_mixer_t
*m
, const pa_channel_map
*cm
, pa_cvolume
*v
, pa_bool_t write_to_hw
) {
1009 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1014 rv
= *v
; /* Remaining adjustment */
1015 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1017 PA_LLIST_FOREACH(e
, p
->elements
) {
1020 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1023 pa_assert(!p
->has_dB
|| e
->has_dB
);
1026 if (element_set_volume(e
, m
, cm
, &ev
, write_to_hw
) < 0)
1034 pa_sw_cvolume_multiply(v
, v
, &ev
);
1035 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1041 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1042 snd_mixer_elem_t
*me
;
1043 snd_mixer_selem_id_t
*sid
;
1049 SELEM_INIT(sid
, e
->alsa_name
);
1050 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1051 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1055 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1056 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1058 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1061 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1066 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1075 PA_LLIST_FOREACH(e
, p
->elements
) {
1077 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1080 if (element_set_switch(e
, m
, !muted
) < 0)
1087 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1088 * function sets all channels of the volume element to e->min_volume, 0 dB or
1089 * e->constant_volume. */
1090 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1091 snd_mixer_elem_t
*me
= NULL
;
1092 snd_mixer_selem_id_t
*sid
= NULL
;
1095 pa_bool_t volume_set
= FALSE
;
1100 SELEM_INIT(sid
, e
->alsa_name
);
1101 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1102 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1106 switch (e
->volume_use
) {
1107 case PA_ALSA_VOLUME_OFF
:
1108 volume
= e
->min_volume
;
1112 case PA_ALSA_VOLUME_ZERO
:
1116 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1121 case PA_ALSA_VOLUME_CONSTANT
:
1122 volume
= e
->constant_volume
;
1127 pa_assert_not_reached();
1131 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1132 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1134 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1136 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1137 pa_assert(!e
->db_fix
);
1139 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1140 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1142 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1146 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1151 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1158 pa_log_debug("Activating path %s", p
->name
);
1159 pa_alsa_path_dump(p
);
1161 PA_LLIST_FOREACH(e
, p
->elements
) {
1163 switch (e
->switch_use
) {
1164 case PA_ALSA_SWITCH_OFF
:
1165 r
= element_set_switch(e
, m
, FALSE
);
1168 case PA_ALSA_SWITCH_ON
:
1169 r
= element_set_switch(e
, m
, TRUE
);
1172 case PA_ALSA_SWITCH_MUTE
:
1173 case PA_ALSA_SWITCH_IGNORE
:
1174 case PA_ALSA_SWITCH_SELECT
:
1182 switch (e
->volume_use
) {
1183 case PA_ALSA_VOLUME_OFF
:
1184 case PA_ALSA_VOLUME_ZERO
:
1185 case PA_ALSA_VOLUME_CONSTANT
:
1186 r
= element_set_constant_volume(e
, m
);
1189 case PA_ALSA_VOLUME_MERGE
:
1190 case PA_ALSA_VOLUME_IGNORE
:
1202 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1203 pa_bool_t has_switch
;
1204 pa_bool_t has_enumeration
;
1205 pa_bool_t has_volume
;
1210 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1212 snd_mixer_selem_has_playback_switch(me
) ||
1213 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1216 snd_mixer_selem_has_capture_switch(me
) ||
1217 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1220 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1222 snd_mixer_selem_has_playback_volume(me
) ||
1223 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1226 snd_mixer_selem_has_capture_volume(me
) ||
1227 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1230 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1232 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1233 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1234 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1237 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1240 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1241 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1242 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1245 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1248 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1249 switch (e
->required_any
) {
1250 case PA_ALSA_REQUIRED_VOLUME
:
1251 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1253 case PA_ALSA_REQUIRED_SWITCH
:
1254 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1256 case PA_ALSA_REQUIRED_ENUMERATION
:
1257 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1259 case PA_ALSA_REQUIRED_ANY
:
1260 e
->path
->req_any_present
|=
1261 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1262 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1263 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1266 pa_assert_not_reached();
1270 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1272 PA_LLIST_FOREACH(o
, e
->options
) {
1273 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1275 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1277 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1285 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1286 snd_mixer_selem_id_t
*sid
;
1287 snd_mixer_elem_t
*me
;
1293 SELEM_INIT(sid
, e
->alsa_name
);
1295 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1297 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1300 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1301 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1302 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1307 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1308 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1310 if (!snd_mixer_selem_has_playback_switch(me
)) {
1311 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1312 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1314 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1319 if (!snd_mixer_selem_has_capture_switch(me
)) {
1320 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1321 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1323 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1327 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1328 e
->direction_try_other
= FALSE
;
1331 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1333 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1335 if (!snd_mixer_selem_has_playback_volume(me
)) {
1336 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1337 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1339 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1344 if (!snd_mixer_selem_has_capture_volume(me
)) {
1345 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1346 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1348 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1352 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1353 long min_dB
= 0, max_dB
= 0;
1356 e
->direction_try_other
= FALSE
;
1358 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1359 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1361 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1364 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1368 if (e
->min_volume
>= e
->max_volume
) {
1369 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
);
1370 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1372 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1373 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1374 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1375 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1376 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1380 pa_channel_position_t p
;
1383 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1384 (e
->max_volume
< e
->db_fix
->max_step
))) {
1385 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1386 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1387 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1388 e
->min_volume
, e
->max_volume
);
1390 decibel_fix_free(e
->db_fix
);
1396 e
->min_volume
= e
->db_fix
->min_step
;
1397 e
->max_volume
= e
->db_fix
->max_step
;
1398 min_dB
= e
->db_fix
->db_values
[0];
1399 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1400 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1401 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1403 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1405 /* Check that the kernel driver returns consistent limits with
1406 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1407 if (e
->has_dB
&& !e
->db_fix
) {
1408 long min_dB_checked
= 0;
1409 long max_dB_checked
= 0;
1411 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1412 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1414 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1417 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1421 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1422 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1424 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1427 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1431 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1432 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1433 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1434 "%0.2f dB at level %li.",
1436 min_dB
/ 100.0, max_dB
/ 100.0,
1437 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1443 #ifdef HAVE_VALGRIND_MEMCHECK_H
1444 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1445 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1448 e
->min_dB
= ((double) min_dB
) / 100.0;
1449 e
->max_dB
= ((double) max_dB
) / 100.0;
1451 if (min_dB
>= max_dB
) {
1452 pa_assert(!e
->db_fix
);
1453 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
);
1458 if (e
->volume_limit
>= 0) {
1459 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1460 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1461 "%li-%li. The volume limit is ignored.",
1462 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1465 e
->max_volume
= e
->volume_limit
;
1469 e
->db_fix
->max_step
= e
->max_volume
;
1470 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1473 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1474 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1476 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1479 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1482 e
->max_dB
= ((double) max_dB
) / 100.0;
1488 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1489 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1491 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1496 if (!e
->override_map
) {
1497 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1498 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1501 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1504 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1507 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1510 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1512 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1515 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1516 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1518 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1521 if (e
->n_channels
<= 0) {
1522 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1526 if (e
->n_channels
> 2) {
1527 /* FIXME: In some places code like this is used:
1529 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1531 * The definition of e->masks is
1533 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1535 * Since the array size is fixed at 2, we obviously
1536 * don't support elements with more than two
1538 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1542 if (!e
->override_map
) {
1543 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1544 pa_bool_t has_channel
;
1546 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1549 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1550 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1552 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1554 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1559 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1560 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1563 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1571 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1574 PA_LLIST_FOREACH(o
, e
->options
)
1575 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1576 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1580 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1581 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1585 PA_LLIST_FOREACH(o
, e
->options
) {
1588 for (i
= 0; i
< n
; i
++) {
1591 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1594 if (!pa_streq(buf
, o
->alsa_name
))
1602 if (check_required(e
, me
) < 0)
1608 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1615 if (!pa_startswith(section
, "Element "))
1621 /* This is not an element section, but an enum section? */
1622 if (strchr(section
, ':'))
1625 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1626 return p
->last_element
;
1628 PA_LLIST_FOREACH(e
, p
->elements
)
1629 if (pa_streq(e
->alsa_name
, section
))
1632 e
= pa_xnew0(pa_alsa_element
, 1);
1634 e
->alsa_name
= pa_xstrdup(section
);
1635 e
->direction
= p
->direction
;
1636 e
->volume_limit
= -1;
1638 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1641 p
->last_element
= e
;
1645 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1651 if (!pa_startswith(section
, "Option "))
1656 /* This is not an enum section, but an element section? */
1657 if (!(on
= strchr(section
, ':')))
1660 en
= pa_xstrndup(section
, on
- section
);
1663 if (p
->last_option
&&
1664 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1665 pa_streq(p
->last_option
->alsa_name
, on
)) {
1667 return p
->last_option
;
1670 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1673 PA_LLIST_FOREACH(o
, e
->options
)
1674 if (pa_streq(o
->alsa_name
, on
))
1677 o
= pa_xnew0(pa_alsa_option
, 1);
1679 o
->alsa_name
= pa_xstrdup(on
);
1682 if (p
->last_option
&& p
->last_option
->element
== e
)
1683 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1685 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1692 static int element_parse_switch(
1693 const char *filename
,
1695 const char *section
,
1701 pa_alsa_path
*p
= userdata
;
1706 if (!(e
= element_get(p
, section
, TRUE
))) {
1707 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1711 if (pa_streq(rvalue
, "ignore"))
1712 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1713 else if (pa_streq(rvalue
, "mute"))
1714 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1715 else if (pa_streq(rvalue
, "off"))
1716 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1717 else if (pa_streq(rvalue
, "on"))
1718 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1719 else if (pa_streq(rvalue
, "select"))
1720 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1722 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1729 static int element_parse_volume(
1730 const char *filename
,
1732 const char *section
,
1738 pa_alsa_path
*p
= userdata
;
1743 if (!(e
= element_get(p
, section
, TRUE
))) {
1744 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1748 if (pa_streq(rvalue
, "ignore"))
1749 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1750 else if (pa_streq(rvalue
, "merge"))
1751 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1752 else if (pa_streq(rvalue
, "off"))
1753 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1754 else if (pa_streq(rvalue
, "zero"))
1755 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1759 if (pa_atou(rvalue
, &constant
) >= 0) {
1760 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1761 e
->constant_volume
= constant
;
1763 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1771 static int element_parse_enumeration(
1772 const char *filename
,
1774 const char *section
,
1780 pa_alsa_path
*p
= userdata
;
1785 if (!(e
= element_get(p
, section
, TRUE
))) {
1786 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1790 if (pa_streq(rvalue
, "ignore"))
1791 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1792 else if (pa_streq(rvalue
, "select"))
1793 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1795 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1802 static int option_parse_priority(
1803 const char *filename
,
1805 const char *section
,
1811 pa_alsa_path
*p
= userdata
;
1817 if (!(o
= option_get(p
, section
))) {
1818 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1822 if (pa_atou(rvalue
, &prio
) < 0) {
1823 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1831 static int option_parse_name(
1832 const char *filename
,
1834 const char *section
,
1840 pa_alsa_path
*p
= userdata
;
1845 if (!(o
= option_get(p
, section
))) {
1846 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1851 o
->name
= pa_xstrdup(rvalue
);
1856 static int element_parse_required(
1857 const char *filename
,
1859 const char *section
,
1865 pa_alsa_path
*p
= userdata
;
1868 pa_alsa_required_t req
;
1872 e
= element_get(p
, section
, TRUE
);
1873 o
= option_get(p
, section
);
1875 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1879 if (pa_streq(rvalue
, "ignore"))
1880 req
= PA_ALSA_REQUIRED_IGNORE
;
1881 else if (pa_streq(rvalue
, "switch") && e
)
1882 req
= PA_ALSA_REQUIRED_SWITCH
;
1883 else if (pa_streq(rvalue
, "volume") && e
)
1884 req
= PA_ALSA_REQUIRED_VOLUME
;
1885 else if (pa_streq(rvalue
, "enumeration"))
1886 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1887 else if (pa_streq(rvalue
, "any"))
1888 req
= PA_ALSA_REQUIRED_ANY
;
1890 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1894 if (pa_streq(lvalue
, "required-absent")) {
1896 e
->required_absent
= req
;
1898 o
->required_absent
= req
;
1900 else if (pa_streq(lvalue
, "required-any")) {
1902 e
->required_any
= req
;
1903 e
->path
->has_req_any
= TRUE
;
1906 o
->required_any
= req
;
1907 o
->element
->path
->has_req_any
= TRUE
;
1920 static int element_parse_direction(
1921 const char *filename
,
1923 const char *section
,
1929 pa_alsa_path
*p
= userdata
;
1934 if (!(e
= element_get(p
, section
, TRUE
))) {
1935 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1939 if (pa_streq(rvalue
, "playback"))
1940 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1941 else if (pa_streq(rvalue
, "capture"))
1942 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1944 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1951 static int element_parse_direction_try_other(
1952 const char *filename
,
1954 const char *section
,
1960 pa_alsa_path
*p
= userdata
;
1964 if (!(e
= element_get(p
, section
, TRUE
))) {
1965 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1969 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1970 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1974 e
->direction_try_other
= !!yes
;
1978 static int element_parse_volume_limit(
1979 const char *filename
,
1981 const char *section
,
1987 pa_alsa_path
*p
= userdata
;
1991 if (!(e
= element_get(p
, section
, TRUE
))) {
1992 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
1996 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
1997 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2001 e
->volume_limit
= volume_limit
;
2005 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2006 pa_channel_position_mask_t v
;
2008 if (pa_streq(m
, "all-left"))
2009 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2010 else if (pa_streq(m
, "all-right"))
2011 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2012 else if (pa_streq(m
, "all-center"))
2013 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2014 else if (pa_streq(m
, "all-front"))
2015 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2016 else if (pa_streq(m
, "all-rear"))
2017 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2018 else if (pa_streq(m
, "all-side"))
2019 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2020 else if (pa_streq(m
, "all-top"))
2021 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2022 else if (pa_streq(m
, "all-no-lfe"))
2023 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2024 else if (pa_streq(m
, "all"))
2025 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2027 pa_channel_position_t p
;
2029 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2032 v
= PA_CHANNEL_POSITION_MASK(p
);
2038 static int element_parse_override_map(
2039 const char *filename
,
2041 const char *section
,
2047 pa_alsa_path
*p
= userdata
;
2049 const char *state
= NULL
;
2053 if (!(e
= element_get(p
, section
, TRUE
))) {
2054 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2058 while ((n
= pa_split(rvalue
, ",", &state
))) {
2059 pa_channel_position_mask_t m
;
2064 if ((m
= parse_mask(n
)) == 0) {
2065 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2071 if (pa_streq(lvalue
, "override-map.1"))
2072 e
->masks
[i
++][0] = m
;
2074 e
->masks
[i
++][1] = m
;
2076 /* Later on we might add override-map.3 and so on here ... */
2081 e
->override_map
= TRUE
;
2086 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2087 snd_mixer_selem_id_t
*sid
;
2088 snd_mixer_elem_t
*me
;
2094 SELEM_INIT(sid
, e
->alsa_name
);
2095 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2096 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2100 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2102 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2103 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2105 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2108 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2111 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2113 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2114 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2120 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2127 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2128 element_set_option(o
->element
, m
, o
->alsa_idx
);
2133 static int option_verify(pa_alsa_option
*o
) {
2134 static const struct description_map well_known_descriptions
[] = {
2135 { "input", N_("Input") },
2136 { "input-docking", N_("Docking Station Input") },
2137 { "input-docking-microphone", N_("Docking Station Microphone") },
2138 { "input-docking-linein", N_("Docking Station Line-In") },
2139 { "input-linein", N_("Line-In") },
2140 { "input-microphone", N_("Microphone") },
2141 { "input-microphone-front", N_("Front Microphone") },
2142 { "input-microphone-rear", N_("Rear Microphone") },
2143 { "input-microphone-external", N_("External Microphone") },
2144 { "input-microphone-internal", N_("Internal Microphone") },
2145 { "input-radio", N_("Radio") },
2146 { "input-video", N_("Video") },
2147 { "input-agc-on", N_("Automatic Gain Control") },
2148 { "input-agc-off", N_("No Automatic Gain Control") },
2149 { "input-boost-on", N_("Boost") },
2150 { "input-boost-off", N_("No Boost") },
2151 { "output-amplifier-on", N_("Amplifier") },
2152 { "output-amplifier-off", N_("No Amplifier") },
2153 { "output-bass-boost-on", N_("Bass Boost") },
2154 { "output-bass-boost-off", N_("No Bass Boost") },
2155 { "output-speaker", N_("Speaker") },
2156 { "output-headphones", N_("Headphones") }
2162 pa_log("No name set for option %s", o
->alsa_name
);
2166 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2167 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2168 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2172 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2173 !pa_streq(o
->alsa_name
, "on") &&
2174 !pa_streq(o
->alsa_name
, "off")) {
2175 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2179 if (!o
->description
)
2180 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2181 well_known_descriptions
,
2182 PA_ELEMENTSOF(well_known_descriptions
)));
2183 if (!o
->description
)
2184 o
->description
= pa_xstrdup(o
->name
);
2189 static int element_verify(pa_alsa_element
*e
) {
2194 // pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
2195 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2196 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2197 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2198 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2199 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2203 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2204 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2208 PA_LLIST_FOREACH(o
, e
->options
)
2209 if (option_verify(o
) < 0)
2215 static int path_verify(pa_alsa_path
*p
) {
2216 static const struct description_map well_known_descriptions
[] = {
2217 { "analog-input", N_("Analog Input") },
2218 { "analog-input-microphone", N_("Analog Microphone") },
2219 { "analog-input-microphone-front", N_("Front Microphone") },
2220 { "analog-input-microphone-rear", N_("Rear Microphone") },
2221 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2222 { "analog-input-microphone-internal", N_("Internal Microphone") },
2223 { "analog-input-linein", N_("Analog Line-In") },
2224 { "analog-input-radio", N_("Analog Radio") },
2225 { "analog-input-video", N_("Analog Video") },
2226 { "analog-output", N_("Analog Output") },
2227 { "analog-output-headphones", N_("Analog Headphones") },
2228 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2229 { "analog-output-mono", N_("Analog Mono Output") },
2230 { "analog-output-speaker", N_("Analog Speakers") },
2231 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2232 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2239 PA_LLIST_FOREACH(e
, p
->elements
)
2240 if (element_verify(e
) < 0)
2243 if (!p
->description
)
2244 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2245 well_known_descriptions
,
2246 PA_ELEMENTSOF(well_known_descriptions
)));
2248 if (!p
->description
)
2249 p
->description
= pa_xstrdup(p
->name
);
2254 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2260 pa_config_item items
[] = {
2262 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2263 { "description", pa_config_parse_string
, NULL
, "General" },
2264 { "name", pa_config_parse_string
, NULL
, "General" },
2267 { "priority", option_parse_priority
, NULL
, NULL
},
2268 { "name", option_parse_name
, NULL
, NULL
},
2271 { "switch", element_parse_switch
, NULL
, NULL
},
2272 { "volume", element_parse_volume
, NULL
, NULL
},
2273 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2274 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2275 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2276 /* ... later on we might add override-map.3 and so on here ... */
2277 { "required", element_parse_required
, NULL
, NULL
},
2278 { "required-any", element_parse_required
, NULL
, NULL
},
2279 { "required-absent", element_parse_required
, NULL
, NULL
},
2280 { "direction", element_parse_direction
, NULL
, NULL
},
2281 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2282 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2283 { NULL
, NULL
, NULL
, NULL
}
2288 p
= pa_xnew0(pa_alsa_path
, 1);
2289 n
= pa_path_get_filename(fname
);
2290 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2291 p
->direction
= direction
;
2293 items
[0].data
= &p
->priority
;
2294 items
[1].data
= &p
->description
;
2295 items
[2].data
= &p
->name
;
2297 fn
= pa_maybe_prefix_path(fname
,
2298 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2301 r
= pa_config_parse(fn
, NULL
, items
, p
);
2307 if (path_verify(p
) < 0)
2313 pa_alsa_path_free(p
);
2317 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2323 p
= pa_xnew0(pa_alsa_path
, 1);
2324 p
->name
= pa_xstrdup(element
);
2325 p
->direction
= direction
;
2327 e
= pa_xnew0(pa_alsa_element
, 1);
2329 e
->alsa_name
= pa_xstrdup(element
);
2330 e
->direction
= direction
;
2331 e
->volume_limit
= -1;
2333 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2334 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2336 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2337 p
->last_element
= e
;
2341 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2342 pa_alsa_option
*o
, *n
;
2346 for (o
= e
->options
; o
; o
= n
) {
2349 if (o
->alsa_idx
< 0) {
2350 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2356 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2357 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2358 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2361 static void path_drop_unsupported(pa_alsa_path
*p
) {
2362 pa_alsa_element
*e
, *n
;
2366 for (e
= p
->elements
; e
; e
= n
) {
2369 if (!element_drop_unsupported(e
)) {
2370 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2376 static void path_make_options_unique(pa_alsa_path
*p
) {
2378 pa_alsa_option
*o
, *u
;
2380 PA_LLIST_FOREACH(e
, p
->elements
) {
2381 PA_LLIST_FOREACH(o
, e
->options
) {
2385 for (u
= o
->next
; u
; u
= u
->next
)
2386 if (pa_streq(u
->name
, o
->name
))
2392 m
= pa_xstrdup(o
->name
);
2394 /* OK, this name is not unique, hence let's rename */
2395 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2398 if (!pa_streq(u
->name
, m
))
2401 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2405 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2406 pa_xfree(u
->description
);
2407 u
->description
= nd
;
2417 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2420 for (; e
; e
= e
->next
)
2421 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2422 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2428 for (o
= e
->options
; o
; o
= o
->next
) {
2432 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2433 s
->options
= pa_idxset_copy(template->options
);
2434 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2436 (template->description
[0] && o
->description
[0])
2437 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2438 : (template->description
[0]
2439 ? pa_xstrdup(template->description
)
2440 : pa_xstrdup(o
->description
));
2442 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2444 s
= pa_xnew0(pa_alsa_setting
, 1);
2445 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2446 s
->name
= pa_xstrdup(o
->name
);
2447 s
->description
= pa_xstrdup(o
->description
);
2448 s
->priority
= o
->priority
;
2451 pa_idxset_put(s
->options
, o
, NULL
);
2453 if (element_create_settings(e
->next
, s
))
2454 /* This is not a leaf, so let's get rid of it */
2457 /* This is a leaf, so let's add it */
2458 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2460 e
->path
->last_setting
= s
;
2467 static void path_create_settings(pa_alsa_path
*p
) {
2470 element_create_settings(p
->elements
, NULL
);
2473 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2475 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2476 pa_channel_position_t t
;
2477 pa_channel_position_mask_t path_volume_channels
= 0;
2488 pa_log_debug("Probing path '%s'", p
->name
);
2490 PA_LLIST_FOREACH(e
, p
->elements
) {
2491 if (element_probe(e
, m
) < 0) {
2492 p
->supported
= FALSE
;
2493 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2496 pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e
->alsa_name
, e
->volume_use
, e
->switch_use
, e
->enumeration_use
);
2501 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2503 if (!p
->has_volume
) {
2504 p
->min_volume
= e
->min_volume
;
2505 p
->max_volume
= e
->max_volume
;
2509 if (!p
->has_volume
) {
2510 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2511 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2512 min_dB
[t
] = e
->min_dB
;
2513 max_dB
[t
] = e
->max_dB
;
2514 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2521 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2522 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2523 min_dB
[t
] += e
->min_dB
;
2524 max_dB
[t
] += e
->max_dB
;
2525 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2528 /* Hmm, there's another element before us
2529 * which cannot do dB volumes, so we we need
2530 * to 'neutralize' this slider */
2531 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2532 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2535 } else if (p
->has_volume
) {
2536 /* We can't use this volume, so let's ignore it */
2537 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2538 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2540 p
->has_volume
= TRUE
;
2543 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2547 if (p
->has_req_any
&& !p
->req_any_present
) {
2548 p
->supported
= FALSE
;
2549 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2553 path_drop_unsupported(p
);
2554 path_make_options_unique(p
);
2555 path_create_settings(p
);
2557 p
->supported
= TRUE
;
2560 p
->min_dB
= INFINITY
;
2561 p
->max_dB
= -INFINITY
;
2563 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2564 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2565 if (p
->min_dB
> min_dB
[t
])
2566 p
->min_dB
= min_dB
[t
];
2568 if (p
->max_dB
< max_dB
[t
])
2569 p
->max_dB
= max_dB
[t
];
2576 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2579 pa_log_debug("Setting %s (%s) priority=%u",
2581 pa_strnull(s
->description
),
2585 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2588 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2590 pa_strnull(o
->name
),
2591 pa_strnull(o
->description
),
2596 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2600 pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
2610 (long long unsigned) e
->merged_mask
,
2612 pa_yes_no(e
->override_map
));
2614 PA_LLIST_FOREACH(o
, e
->options
)
2615 pa_alsa_option_dump(o
);
2618 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2623 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2624 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2626 pa_strnull(p
->description
),
2629 pa_yes_no(p
->probed
),
2630 pa_yes_no(p
->supported
),
2631 pa_yes_no(p
->has_mute
),
2632 pa_yes_no(p
->has_volume
),
2633 pa_yes_no(p
->has_dB
),
2634 p
->min_volume
, p
->max_volume
,
2635 p
->min_dB
, p
->max_dB
);
2637 PA_LLIST_FOREACH(e
, p
->elements
)
2638 pa_alsa_element_dump(e
);
2640 PA_LLIST_FOREACH(s
, p
->settings
)
2641 pa_alsa_setting_dump(s
);
2644 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2645 snd_mixer_selem_id_t
*sid
;
2646 snd_mixer_elem_t
*me
;
2652 SELEM_INIT(sid
, e
->alsa_name
);
2653 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2654 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2658 snd_mixer_elem_set_callback(me
, cb
);
2659 snd_mixer_elem_set_callback_private(me
, userdata
);
2662 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2669 PA_LLIST_FOREACH(e
, p
->elements
)
2670 element_set_callback(e
, m
, cb
, userdata
);
2673 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2680 PA_LLIST_FOREACH(p
, ps
->paths
)
2681 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2684 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2685 pa_alsa_path_set
*ps
;
2686 char **pn
= NULL
, **en
= NULL
, **ie
;
2687 pa_alsa_decibel_fix
*db_fix
;
2691 pa_assert(m
->profile_set
);
2692 pa_assert(m
->profile_set
->decibel_fixes
);
2693 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2695 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2698 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2699 ps
->direction
= direction
;
2701 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2702 pn
= m
->output_path_names
;
2703 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2704 pn
= m
->input_path_names
;
2709 for (in
= pn
; *in
; in
++) {
2711 pa_bool_t duplicate
= FALSE
;
2714 for (kn
= pn
; kn
< in
; kn
++)
2715 if (pa_streq(*kn
, *in
)) {
2723 fn
= pa_sprintf_malloc("%s.conf", *in
);
2725 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2727 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2737 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2738 en
= m
->output_element
;
2739 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2740 en
= m
->input_element
;
2743 pa_alsa_path_set_free(ps
);
2747 for (ie
= en
; *ie
; ie
++) {
2751 p
= pa_alsa_path_synthesize(*ie
, direction
);
2754 /* Mark all other passed elements for require-absent */
2755 for (je
= en
; *je
; je
++) {
2761 e
= pa_xnew0(pa_alsa_element
, 1);
2763 e
->alsa_name
= pa_xstrdup(*je
);
2764 e
->direction
= direction
;
2765 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2766 e
->volume_limit
= -1;
2768 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2769 p
->last_element
= e
;
2772 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2777 /* Assign decibel fixes to elements. */
2778 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2781 PA_LLIST_FOREACH(p
, ps
->paths
) {
2784 PA_LLIST_FOREACH(e
, p
->elements
) {
2785 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2786 /* The profile set that contains the dB fix may be freed
2787 * before the element, so we have to copy the dB fix
2789 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2790 e
->db_fix
->profile_set
= NULL
;
2791 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2792 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2801 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2805 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2808 pa_yes_no(ps
->probed
));
2810 PA_LLIST_FOREACH(p
, ps
->paths
)
2811 pa_alsa_path_dump(p
);
2814 static void path_set_unify(pa_alsa_path_set
*ps
) {
2816 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2819 /* We have issues dealing with paths that vary too wildly. That
2820 * means for now we have to have all paths support volume/mute/dB
2823 PA_LLIST_FOREACH(p
, ps
->paths
) {
2824 pa_assert(p
->probed
);
2828 else if (!p
->has_dB
)
2835 if (!has_volume
|| !has_dB
|| !has_mute
) {
2838 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2840 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2843 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2845 PA_LLIST_FOREACH(p
, ps
->paths
) {
2847 p
->has_volume
= FALSE
;
2852 p
->has_mute
= FALSE
;
2857 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2858 pa_alsa_path
*p
, *q
;
2860 PA_LLIST_FOREACH(p
, ps
->paths
) {
2864 for (q
= p
->next
; q
; q
= q
->next
)
2865 if (pa_streq(q
->name
, p
->name
))
2871 m
= pa_xstrdup(p
->name
);
2873 /* OK, this name is not unique, hence let's rename */
2874 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2877 if (!pa_streq(q
->name
, m
))
2880 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2884 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2885 pa_xfree(q
->description
);
2886 q
->description
= nd
;
2895 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2896 pa_alsa_path
*p
, *n
;
2903 for (p
= ps
->paths
; p
; p
= n
) {
2906 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2907 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2908 pa_alsa_path_free(p
);
2913 path_set_make_paths_unique(ps
);
2917 static void mapping_free(pa_alsa_mapping
*m
) {
2921 pa_xfree(m
->description
);
2923 pa_xstrfreev(m
->device_strings
);
2924 pa_xstrfreev(m
->input_path_names
);
2925 pa_xstrfreev(m
->output_path_names
);
2926 pa_xstrfreev(m
->input_element
);
2927 pa_xstrfreev(m
->output_element
);
2929 pa_assert(!m
->input_pcm
);
2930 pa_assert(!m
->output_pcm
);
2935 static void profile_free(pa_alsa_profile
*p
) {
2939 pa_xfree(p
->description
);
2941 pa_xstrfreev(p
->input_mapping_names
);
2942 pa_xstrfreev(p
->output_mapping_names
);
2944 if (p
->input_mappings
)
2945 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2947 if (p
->output_mappings
)
2948 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2953 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2959 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2962 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2968 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2971 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2974 if (ps
->decibel_fixes
) {
2975 pa_alsa_decibel_fix
*db_fix
;
2977 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2978 decibel_fix_free(db_fix
);
2980 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2986 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2989 if (!pa_startswith(name
, "Mapping "))
2994 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2997 m
= pa_xnew0(pa_alsa_mapping
, 1);
2998 m
->profile_set
= ps
;
2999 m
->name
= pa_xstrdup(name
);
3000 pa_channel_map_init(&m
->channel_map
);
3002 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3007 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3010 if (!pa_startswith(name
, "Profile "))
3015 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3018 p
= pa_xnew0(pa_alsa_profile
, 1);
3019 p
->profile_set
= ps
;
3020 p
->name
= pa_xstrdup(name
);
3022 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3027 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3028 pa_alsa_decibel_fix
*db_fix
;
3030 if (!pa_startswith(name
, "DecibelFix "))
3035 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3038 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3039 db_fix
->profile_set
= ps
;
3040 db_fix
->name
= pa_xstrdup(name
);
3042 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3047 static int mapping_parse_device_strings(
3048 const char *filename
,
3050 const char *section
,
3056 pa_alsa_profile_set
*ps
= userdata
;
3061 if (!(m
= mapping_get(ps
, section
))) {
3062 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3066 pa_xstrfreev(m
->device_strings
);
3067 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3068 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3075 static int mapping_parse_channel_map(
3076 const char *filename
,
3078 const char *section
,
3084 pa_alsa_profile_set
*ps
= userdata
;
3089 if (!(m
= mapping_get(ps
, section
))) {
3090 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3094 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3095 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3102 static int mapping_parse_paths(
3103 const char *filename
,
3105 const char *section
,
3111 pa_alsa_profile_set
*ps
= userdata
;
3116 if (!(m
= mapping_get(ps
, section
))) {
3117 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3121 if (pa_streq(lvalue
, "paths-input")) {
3122 pa_xstrfreev(m
->input_path_names
);
3123 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3125 pa_xstrfreev(m
->output_path_names
);
3126 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3132 static int mapping_parse_element(
3133 const char *filename
,
3135 const char *section
,
3141 pa_alsa_profile_set
*ps
= userdata
;
3146 if (!(m
= mapping_get(ps
, section
))) {
3147 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3151 if (pa_streq(lvalue
, "element-input")) {
3152 pa_xstrfreev(m
->input_element
);
3153 m
->input_element
= pa_split_spaces_strv(rvalue
);
3155 pa_xstrfreev(m
->output_element
);
3156 m
->output_element
= pa_split_spaces_strv(rvalue
);
3162 static int mapping_parse_direction(
3163 const char *filename
,
3165 const char *section
,
3171 pa_alsa_profile_set
*ps
= userdata
;
3176 if (!(m
= mapping_get(ps
, section
))) {
3177 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3181 if (pa_streq(rvalue
, "input"))
3182 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3183 else if (pa_streq(rvalue
, "output"))
3184 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3185 else if (pa_streq(rvalue
, "any"))
3186 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3188 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3195 static int mapping_parse_description(
3196 const char *filename
,
3198 const char *section
,
3204 pa_alsa_profile_set
*ps
= userdata
;
3210 if ((m
= mapping_get(ps
, section
))) {
3211 pa_xfree(m
->description
);
3212 m
->description
= pa_xstrdup(rvalue
);
3213 } else if ((p
= profile_get(ps
, section
))) {
3214 pa_xfree(p
->description
);
3215 p
->description
= pa_xstrdup(rvalue
);
3217 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3224 static int mapping_parse_priority(
3225 const char *filename
,
3227 const char *section
,
3233 pa_alsa_profile_set
*ps
= userdata
;
3240 if (pa_atou(rvalue
, &prio
) < 0) {
3241 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3245 if ((m
= mapping_get(ps
, section
)))
3247 else if ((p
= profile_get(ps
, section
)))
3250 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3257 static int profile_parse_mappings(
3258 const char *filename
,
3260 const char *section
,
3266 pa_alsa_profile_set
*ps
= userdata
;
3271 if (!(p
= profile_get(ps
, section
))) {
3272 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3276 if (pa_streq(lvalue
, "input-mappings")) {
3277 pa_xstrfreev(p
->input_mapping_names
);
3278 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3280 pa_xstrfreev(p
->output_mapping_names
);
3281 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3287 static int profile_parse_skip_probe(
3288 const char *filename
,
3290 const char *section
,
3296 pa_alsa_profile_set
*ps
= userdata
;
3302 if (!(p
= profile_get(ps
, section
))) {
3303 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3307 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3308 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3317 static int decibel_fix_parse_db_values(
3318 const char *filename
,
3320 const char *section
,
3326 pa_alsa_profile_set
*ps
= userdata
;
3327 pa_alsa_decibel_fix
*db_fix
;
3331 unsigned n
= 8; /* Current size of the db_values table. */
3332 unsigned min_step
= 0;
3333 unsigned max_step
= 0;
3334 unsigned i
= 0; /* Index to the items table. */
3335 unsigned prev_step
= 0;
3338 pa_assert(filename
);
3344 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3345 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3349 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3350 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3354 db_values
= pa_xnew(long, n
);
3356 while ((item
= items
[i
++])) {
3357 char *s
= item
; /* Step value string. */
3358 char *d
= item
; /* dB value string. */
3362 /* Move d forward until it points to a colon or to the end of the item. */
3363 for (; *d
&& *d
!= ':'; ++d
);
3366 /* item started with colon. */
3367 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3371 if (!*d
|| !*(d
+ 1)) {
3372 /* No colon found, or it was the last character in item. */
3373 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3377 /* pa_atou() needs a null-terminating string. Let's replace the colon
3378 * with a zero byte. */
3381 if (pa_atou(s
, &step
) < 0) {
3382 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3386 if (pa_atod(d
, &db
) < 0) {
3387 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3391 if (step
<= prev_step
&& i
!= 1) {
3392 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3396 if (db
< prev_db
&& i
!= 1) {
3397 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3403 db_values
[0] = (long) (db
* 100.0);
3407 /* Interpolate linearly. */
3408 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3410 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3412 /* Reallocate the db_values table if it's about to overflow. */
3413 if (prev_step
+ 1 - min_step
== n
) {
3415 db_values
= pa_xrenew(long, db_values
, n
);
3418 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3425 db_fix
->min_step
= min_step
;
3426 db_fix
->max_step
= max_step
;
3427 pa_xfree(db_fix
->db_values
);
3428 db_fix
->db_values
= db_values
;
3430 pa_xstrfreev(items
);
3435 pa_xstrfreev(items
);
3436 pa_xfree(db_values
);
3441 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3443 static const struct description_map well_known_descriptions
[] = {
3444 { "analog-mono", N_("Analog Mono") },
3445 { "analog-stereo", N_("Analog Stereo") },
3446 { "analog-surround-21", N_("Analog Surround 2.1") },
3447 { "analog-surround-30", N_("Analog Surround 3.0") },
3448 { "analog-surround-31", N_("Analog Surround 3.1") },
3449 { "analog-surround-40", N_("Analog Surround 4.0") },
3450 { "analog-surround-41", N_("Analog Surround 4.1") },
3451 { "analog-surround-50", N_("Analog Surround 5.0") },
3452 { "analog-surround-51", N_("Analog Surround 5.1") },
3453 { "analog-surround-61", N_("Analog Surround 6.0") },
3454 { "analog-surround-61", N_("Analog Surround 6.1") },
3455 { "analog-surround-70", N_("Analog Surround 7.0") },
3456 { "analog-surround-71", N_("Analog Surround 7.1") },
3457 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3458 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3459 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3460 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3461 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3466 if (!pa_channel_map_valid(&m
->channel_map
)) {
3467 pa_log("Mapping %s is missing channel map.", m
->name
);
3471 if (!m
->device_strings
) {
3472 pa_log("Mapping %s is missing device strings.", m
->name
);
3476 if ((m
->input_path_names
&& m
->input_element
) ||
3477 (m
->output_path_names
&& m
->output_element
)) {
3478 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3482 if (!m
->description
)
3483 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3484 well_known_descriptions
,
3485 PA_ELEMENTSOF(well_known_descriptions
)));
3487 if (!m
->description
)
3488 m
->description
= pa_xstrdup(m
->name
);
3491 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3493 else if (m
->channel_map
.channels
== bonus
->channels
)
3500 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3501 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3505 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3507 pa_strnull(m
->description
),
3509 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3510 pa_yes_no(m
->supported
),
3514 static void profile_set_add_auto_pair(
3515 pa_alsa_profile_set
*ps
,
3516 pa_alsa_mapping
*m
, /* output */
3517 pa_alsa_mapping
*n
/* input */) {
3525 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3528 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3532 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3534 name
= pa_sprintf_malloc("output:%s", m
->name
);
3536 name
= pa_sprintf_malloc("input:%s", n
->name
);
3538 if (pa_hashmap_get(ps
->profiles
, name
)) {
3543 p
= pa_xnew0(pa_alsa_profile
, 1);
3544 p
->profile_set
= ps
;
3548 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3549 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3550 p
->priority
+= m
->priority
* 100;
3554 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3555 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3556 p
->priority
+= n
->priority
;
3559 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3562 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3563 pa_alsa_mapping
*m
, *n
;
3564 void *m_state
, *n_state
;
3568 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3569 profile_set_add_auto_pair(ps
, m
, NULL
);
3571 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3572 profile_set_add_auto_pair(ps
, m
, n
);
3575 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3576 profile_set_add_auto_pair(ps
, NULL
, n
);
3579 static int profile_verify(pa_alsa_profile
*p
) {
3581 static const struct description_map well_known_descriptions
[] = {
3582 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3583 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3584 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3585 { "off", N_("Off") }
3590 /* Replace the output mapping names by the actual mappings */
3591 if (p
->output_mapping_names
) {
3594 pa_assert(!p
->output_mappings
);
3595 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3597 for (name
= p
->output_mapping_names
; *name
; name
++) {
3600 pa_bool_t duplicate
= FALSE
;
3602 for (in
= name
+ 1; *in
; in
++)
3603 if (pa_streq(*name
, *in
)) {
3611 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3612 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3616 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3622 pa_xstrfreev(p
->output_mapping_names
);
3623 p
->output_mapping_names
= NULL
;
3626 /* Replace the input mapping names by the actual mappings */
3627 if (p
->input_mapping_names
) {
3630 pa_assert(!p
->input_mappings
);
3631 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3633 for (name
= p
->input_mapping_names
; *name
; name
++) {
3636 pa_bool_t duplicate
= FALSE
;
3638 for (in
= name
+ 1; *in
; in
++)
3639 if (pa_streq(*name
, *in
)) {
3647 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3648 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3652 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3658 pa_xstrfreev(p
->input_mapping_names
);
3659 p
->input_mapping_names
= NULL
;
3662 if (!p
->input_mappings
&& !p
->output_mappings
) {
3663 pa_log("Profile '%s' lacks mappings.", p
->name
);
3667 if (!p
->description
)
3668 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3669 well_known_descriptions
,
3670 PA_ELEMENTSOF(well_known_descriptions
)));
3672 if (!p
->description
) {
3677 sb
= pa_strbuf_new();
3679 if (p
->output_mappings
)
3680 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3681 if (!pa_strbuf_isempty(sb
))
3682 pa_strbuf_puts(sb
, " + ");
3684 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3687 if (p
->input_mappings
)
3688 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3689 if (!pa_strbuf_isempty(sb
))
3690 pa_strbuf_puts(sb
, " + ");
3692 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3695 p
->description
= pa_strbuf_tostring_free(sb
);
3701 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3706 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3708 pa_strnull(p
->description
),
3710 pa_yes_no(p
->supported
),
3711 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3712 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3714 if (p
->input_mappings
)
3715 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3716 pa_log_debug("Input %s", m
->name
);
3718 if (p
->output_mappings
)
3719 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3720 pa_log_debug("Output %s", m
->name
);
3723 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3726 /* Check that the dB mapping has been configured. Since "db-values" is
3727 * currently the only option in the DecibelFix section, and decibel fix
3728 * objects don't get created if a DecibelFix section is empty, this is
3729 * actually a redundant check. Having this may prevent future bugs,
3731 if (!db_fix
->db_values
) {
3732 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3739 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3740 char *db_values
= NULL
;
3744 if (db_fix
->db_values
) {
3746 unsigned long i
, nsteps
;
3748 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3749 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3751 buf
= pa_strbuf_new();
3752 for (i
= 0; i
< nsteps
; ++i
)
3753 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3755 db_values
= pa_strbuf_tostring_free(buf
);
3758 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3759 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3761 pa_xfree(db_values
);
3764 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3765 pa_alsa_profile_set
*ps
;
3768 pa_alsa_decibel_fix
*db_fix
;
3773 static pa_config_item items
[] = {
3775 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3778 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3779 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3780 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3781 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3782 { "element-input", mapping_parse_element
, NULL
, NULL
},
3783 { "element-output", mapping_parse_element
, NULL
, NULL
},
3784 { "direction", mapping_parse_direction
, NULL
, NULL
},
3786 /* Shared by [Mapping ...] and [Profile ...] */
3787 { "description", mapping_parse_description
, NULL
, NULL
},
3788 { "priority", mapping_parse_priority
, NULL
, NULL
},
3791 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3792 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3793 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3795 /* [DecibelFix ...] */
3796 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3797 { NULL
, NULL
, NULL
, NULL
}
3800 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3801 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3802 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3803 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3805 items
[0].data
= &ps
->auto_profiles
;
3808 fname
= "default.conf";
3810 fn
= pa_maybe_prefix_path(fname
,
3811 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3812 PA_ALSA_PROFILE_SETS_DIR
);
3814 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3820 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3821 if (mapping_verify(m
, bonus
) < 0)
3824 if (ps
->auto_profiles
)
3825 profile_set_add_auto(ps
);
3827 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3828 if (profile_verify(p
) < 0)
3831 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3832 if (decibel_fix_verify(db_fix
) < 0)
3838 pa_alsa_profile_set_free(ps
);
3842 void pa_alsa_profile_set_probe(
3843 pa_alsa_profile_set
*ps
,
3845 const pa_sample_spec
*ss
,
3846 unsigned default_n_fragments
,
3847 unsigned default_fragment_size_msec
) {
3850 pa_alsa_profile
*p
, *last
= NULL
;
3860 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3861 pa_sample_spec try_ss
;
3862 pa_channel_map try_map
;
3863 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3866 /* Is this already marked that it is supported? (i.e. from the config file) */
3870 pa_log_debug("Looking at profile %s", p
->name
);
3872 /* Close PCMs from the last iteration we don't need anymore */
3873 if (last
&& last
->output_mappings
)
3874 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3879 if (last
->supported
)
3882 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3883 snd_pcm_close(m
->output_pcm
);
3884 m
->output_pcm
= NULL
;
3888 if (last
&& last
->input_mappings
)
3889 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3894 if (last
->supported
)
3897 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3898 snd_pcm_close(m
->input_pcm
);
3899 m
->input_pcm
= NULL
;
3903 p
->supported
= TRUE
;
3905 /* Check if we can open all new ones */
3906 if (p
->output_mappings
)
3907 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3912 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3913 try_map
= m
->channel_map
;
3915 try_ss
.channels
= try_map
.channels
;
3918 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3919 pa_frame_size(&try_ss
);
3920 try_buffer_size
= default_n_fragments
* try_period_size
;
3922 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3927 SND_PCM_STREAM_PLAYBACK
,
3928 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3930 p
->supported
= FALSE
;
3935 if (p
->input_mappings
&& p
->supported
)
3936 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3941 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3942 try_map
= m
->channel_map
;
3944 try_ss
.channels
= try_map
.channels
;
3947 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3948 pa_frame_size(&try_ss
);
3949 try_buffer_size
= default_n_fragments
* try_period_size
;
3951 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3956 SND_PCM_STREAM_CAPTURE
,
3957 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3959 p
->supported
= FALSE
;
3967 pa_log_debug("Profile %s supported.", p
->name
);
3974 if (last
->output_mappings
)
3975 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3976 if (m
->output_pcm
) {
3978 if (last
->supported
)
3981 snd_pcm_close(m
->output_pcm
);
3982 m
->output_pcm
= NULL
;
3985 if (last
->input_mappings
)
3986 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3989 if (last
->supported
)
3992 snd_pcm_close(m
->input_pcm
);
3993 m
->input_pcm
= NULL
;
3997 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3998 if (!p
->supported
) {
3999 pa_hashmap_remove(ps
->profiles
, p
->name
);
4003 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4004 if (m
->supported
<= 0) {
4005 pa_hashmap_remove(ps
->mappings
, m
->name
);
4012 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4015 pa_alsa_decibel_fix
*db_fix
;
4020 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4023 pa_yes_no(ps
->auto_profiles
),
4024 pa_yes_no(ps
->probed
),
4025 pa_hashmap_size(ps
->mappings
),
4026 pa_hashmap_size(ps
->profiles
),
4027 pa_hashmap_size(ps
->decibel_fixes
));
4029 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4030 pa_alsa_mapping_dump(m
);
4032 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4033 pa_alsa_profile_dump(p
);
4035 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4036 pa_alsa_decibel_fix_dump(db_fix
);
4039 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4046 /* if there is no path, we don't want a port list */
4050 if (!ps
->paths
->next
){
4053 /* If there is only one path, but no or only one setting, then
4054 * we want a port list either */
4055 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4058 /* Ok, there is only one path, however with multiple settings,
4059 * so let's create a port for each setting */
4060 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4062 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4063 pa_device_port
*port
;
4064 pa_alsa_port_data
*data
;
4066 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4067 port
->priority
= s
->priority
;
4069 data
= PA_DEVICE_PORT_DATA(port
);
4070 data
->path
= ps
->paths
;
4073 pa_hashmap_put(*p
, port
->name
, port
);
4078 /* We have multiple paths, so let's create a port for each
4079 * one, and each of each settings */
4080 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4082 PA_LLIST_FOREACH(path
, ps
->paths
) {
4084 if (!path
->settings
|| !path
->settings
->next
) {
4085 pa_device_port
*port
;
4086 pa_alsa_port_data
*data
;
4088 /* If there is no or just one setting we only need a
4091 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4092 port
->priority
= path
->priority
* 100;
4095 data
= PA_DEVICE_PORT_DATA(port
);
4097 data
->setting
= path
->settings
;
4099 pa_hashmap_put(*p
, port
->name
, port
);
4103 PA_LLIST_FOREACH(s
, path
->settings
) {
4104 pa_device_port
*port
;
4105 pa_alsa_port_data
*data
;
4108 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4110 if (s
->description
[0])
4111 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4113 d
= pa_xstrdup(path
->description
);
4115 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4116 port
->priority
= path
->priority
* 100 + s
->priority
;
4121 data
= PA_DEVICE_PORT_DATA(port
);
4125 pa_hashmap_put(*p
, port
->name
, port
);
4131 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));