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
;
1099 SELEM_INIT(sid
, e
->alsa_name
);
1100 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1101 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1105 switch (e
->volume_use
) {
1106 case PA_ALSA_VOLUME_OFF
:
1107 volume
= e
->min_volume
;
1110 case PA_ALSA_VOLUME_ZERO
:
1114 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1118 case PA_ALSA_VOLUME_CONSTANT
:
1119 volume
= e
->constant_volume
;
1123 pa_assert_not_reached();
1127 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1128 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1130 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1132 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1133 pa_assert(!e
->db_fix
);
1135 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1136 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1138 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1142 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1147 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1154 pa_log_debug("Activating path %s", p
->name
);
1155 pa_alsa_path_dump(p
);
1157 PA_LLIST_FOREACH(e
, p
->elements
) {
1159 switch (e
->switch_use
) {
1160 case PA_ALSA_SWITCH_OFF
:
1161 r
= element_set_switch(e
, m
, FALSE
);
1164 case PA_ALSA_SWITCH_ON
:
1165 r
= element_set_switch(e
, m
, TRUE
);
1168 case PA_ALSA_SWITCH_MUTE
:
1169 case PA_ALSA_SWITCH_IGNORE
:
1170 case PA_ALSA_SWITCH_SELECT
:
1178 switch (e
->volume_use
) {
1179 case PA_ALSA_VOLUME_OFF
:
1180 case PA_ALSA_VOLUME_ZERO
:
1181 case PA_ALSA_VOLUME_CONSTANT
:
1182 r
= element_set_constant_volume(e
, m
);
1185 case PA_ALSA_VOLUME_MERGE
:
1186 case PA_ALSA_VOLUME_IGNORE
:
1198 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1199 pa_bool_t has_switch
;
1200 pa_bool_t has_enumeration
;
1201 pa_bool_t has_volume
;
1206 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1208 snd_mixer_selem_has_playback_switch(me
) ||
1209 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1212 snd_mixer_selem_has_capture_switch(me
) ||
1213 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1216 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1218 snd_mixer_selem_has_playback_volume(me
) ||
1219 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1222 snd_mixer_selem_has_capture_volume(me
) ||
1223 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1226 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1228 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1229 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1230 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1233 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1236 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1237 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1238 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1241 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1244 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1245 switch (e
->required_any
) {
1246 case PA_ALSA_REQUIRED_VOLUME
:
1247 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1249 case PA_ALSA_REQUIRED_SWITCH
:
1250 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1252 case PA_ALSA_REQUIRED_ENUMERATION
:
1253 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1255 case PA_ALSA_REQUIRED_ANY
:
1256 e
->path
->req_any_present
|=
1257 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1258 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1259 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1262 pa_assert_not_reached();
1266 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1268 PA_LLIST_FOREACH(o
, e
->options
) {
1269 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1271 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1273 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1281 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1282 snd_mixer_selem_id_t
*sid
;
1283 snd_mixer_elem_t
*me
;
1289 SELEM_INIT(sid
, e
->alsa_name
);
1291 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1293 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1296 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1297 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1298 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1303 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1304 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1306 if (!snd_mixer_selem_has_playback_switch(me
)) {
1307 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1308 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1310 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1315 if (!snd_mixer_selem_has_capture_switch(me
)) {
1316 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1317 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1319 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1323 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1324 e
->direction_try_other
= FALSE
;
1327 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1329 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1331 if (!snd_mixer_selem_has_playback_volume(me
)) {
1332 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1333 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1335 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1340 if (!snd_mixer_selem_has_capture_volume(me
)) {
1341 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1342 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1344 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1348 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1349 long min_dB
= 0, max_dB
= 0;
1352 e
->direction_try_other
= FALSE
;
1354 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1355 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1357 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1360 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1364 if (e
->min_volume
>= e
->max_volume
) {
1365 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
);
1366 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1368 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1369 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1370 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1371 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1372 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1376 pa_channel_position_t p
;
1379 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1380 (e
->max_volume
< e
->db_fix
->max_step
))) {
1381 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1382 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1383 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1384 e
->min_volume
, e
->max_volume
);
1386 decibel_fix_free(e
->db_fix
);
1392 e
->min_volume
= e
->db_fix
->min_step
;
1393 e
->max_volume
= e
->db_fix
->max_step
;
1394 min_dB
= e
->db_fix
->db_values
[0];
1395 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1396 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1397 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1399 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1401 /* Check that the kernel driver returns consistent limits with
1402 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1403 if (e
->has_dB
&& !e
->db_fix
) {
1404 long min_dB_checked
= 0;
1405 long max_dB_checked
= 0;
1407 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1408 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1410 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1413 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1417 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1418 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1420 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1423 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1427 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1428 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1429 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1430 "%0.2f dB at level %li.",
1432 min_dB
/ 100.0, max_dB
/ 100.0,
1433 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1439 #ifdef HAVE_VALGRIND_MEMCHECK_H
1440 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1441 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1444 e
->min_dB
= ((double) min_dB
) / 100.0;
1445 e
->max_dB
= ((double) max_dB
) / 100.0;
1447 if (min_dB
>= max_dB
) {
1448 pa_assert(!e
->db_fix
);
1449 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
);
1454 if (e
->volume_limit
>= 0) {
1455 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1456 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1457 "%li-%li. The volume limit is ignored.",
1458 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1461 e
->max_volume
= e
->volume_limit
;
1465 e
->db_fix
->max_step
= e
->max_volume
;
1466 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1469 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1470 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1472 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1475 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1478 e
->max_dB
= ((double) max_dB
) / 100.0;
1484 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1485 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1487 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1492 if (!e
->override_map
) {
1493 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1494 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1497 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1500 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1503 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1506 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1508 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1511 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1512 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1514 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1517 if (e
->n_channels
<= 0) {
1518 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1522 if (e
->n_channels
> 2) {
1523 /* FIXME: In some places code like this is used:
1525 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1527 * The definition of e->masks is
1529 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1531 * Since the array size is fixed at 2, we obviously
1532 * don't support elements with more than two
1534 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1538 if (!e
->override_map
) {
1539 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1540 pa_bool_t has_channel
;
1542 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1545 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1546 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1548 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1550 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1555 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1556 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1559 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1567 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1570 PA_LLIST_FOREACH(o
, e
->options
)
1571 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1572 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1576 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1577 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1581 PA_LLIST_FOREACH(o
, e
->options
) {
1584 for (i
= 0; i
< n
; i
++) {
1587 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1590 if (!pa_streq(buf
, o
->alsa_name
))
1598 if (check_required(e
, me
) < 0)
1604 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1611 if (!pa_startswith(section
, "Element "))
1617 /* This is not an element section, but an enum section? */
1618 if (strchr(section
, ':'))
1621 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1622 return p
->last_element
;
1624 PA_LLIST_FOREACH(e
, p
->elements
)
1625 if (pa_streq(e
->alsa_name
, section
))
1628 e
= pa_xnew0(pa_alsa_element
, 1);
1630 e
->alsa_name
= pa_xstrdup(section
);
1631 e
->direction
= p
->direction
;
1632 e
->volume_limit
= -1;
1634 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1637 p
->last_element
= e
;
1641 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1647 if (!pa_startswith(section
, "Option "))
1652 /* This is not an enum section, but an element section? */
1653 if (!(on
= strchr(section
, ':')))
1656 en
= pa_xstrndup(section
, on
- section
);
1659 if (p
->last_option
&&
1660 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1661 pa_streq(p
->last_option
->alsa_name
, on
)) {
1663 return p
->last_option
;
1666 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1669 PA_LLIST_FOREACH(o
, e
->options
)
1670 if (pa_streq(o
->alsa_name
, on
))
1673 o
= pa_xnew0(pa_alsa_option
, 1);
1675 o
->alsa_name
= pa_xstrdup(on
);
1678 if (p
->last_option
&& p
->last_option
->element
== e
)
1679 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1681 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1688 static int element_parse_switch(
1689 const char *filename
,
1691 const char *section
,
1697 pa_alsa_path
*p
= userdata
;
1702 if (!(e
= element_get(p
, section
, TRUE
))) {
1703 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1707 if (pa_streq(rvalue
, "ignore"))
1708 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1709 else if (pa_streq(rvalue
, "mute"))
1710 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1711 else if (pa_streq(rvalue
, "off"))
1712 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1713 else if (pa_streq(rvalue
, "on"))
1714 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1715 else if (pa_streq(rvalue
, "select"))
1716 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1718 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1725 static int element_parse_volume(
1726 const char *filename
,
1728 const char *section
,
1734 pa_alsa_path
*p
= userdata
;
1739 if (!(e
= element_get(p
, section
, TRUE
))) {
1740 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1744 if (pa_streq(rvalue
, "ignore"))
1745 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1746 else if (pa_streq(rvalue
, "merge"))
1747 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1748 else if (pa_streq(rvalue
, "off"))
1749 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1750 else if (pa_streq(rvalue
, "zero"))
1751 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1755 if (pa_atou(rvalue
, &constant
) >= 0) {
1756 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1757 e
->constant_volume
= constant
;
1759 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1767 static int element_parse_enumeration(
1768 const char *filename
,
1770 const char *section
,
1776 pa_alsa_path
*p
= userdata
;
1781 if (!(e
= element_get(p
, section
, TRUE
))) {
1782 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1786 if (pa_streq(rvalue
, "ignore"))
1787 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1788 else if (pa_streq(rvalue
, "select"))
1789 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1791 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1798 static int option_parse_priority(
1799 const char *filename
,
1801 const char *section
,
1807 pa_alsa_path
*p
= userdata
;
1813 if (!(o
= option_get(p
, section
))) {
1814 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1818 if (pa_atou(rvalue
, &prio
) < 0) {
1819 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1827 static int option_parse_name(
1828 const char *filename
,
1830 const char *section
,
1836 pa_alsa_path
*p
= userdata
;
1841 if (!(o
= option_get(p
, section
))) {
1842 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1847 o
->name
= pa_xstrdup(rvalue
);
1852 static int element_parse_required(
1853 const char *filename
,
1855 const char *section
,
1861 pa_alsa_path
*p
= userdata
;
1864 pa_alsa_required_t req
;
1868 e
= element_get(p
, section
, TRUE
);
1869 o
= option_get(p
, section
);
1871 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1875 if (pa_streq(rvalue
, "ignore"))
1876 req
= PA_ALSA_REQUIRED_IGNORE
;
1877 else if (pa_streq(rvalue
, "switch") && e
)
1878 req
= PA_ALSA_REQUIRED_SWITCH
;
1879 else if (pa_streq(rvalue
, "volume") && e
)
1880 req
= PA_ALSA_REQUIRED_VOLUME
;
1881 else if (pa_streq(rvalue
, "enumeration"))
1882 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1883 else if (pa_streq(rvalue
, "any"))
1884 req
= PA_ALSA_REQUIRED_ANY
;
1886 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1890 if (pa_streq(lvalue
, "required-absent")) {
1892 e
->required_absent
= req
;
1894 o
->required_absent
= req
;
1896 else if (pa_streq(lvalue
, "required-any")) {
1898 e
->required_any
= req
;
1899 e
->path
->has_req_any
= TRUE
;
1902 o
->required_any
= req
;
1903 o
->element
->path
->has_req_any
= TRUE
;
1916 static int element_parse_direction(
1917 const char *filename
,
1919 const char *section
,
1925 pa_alsa_path
*p
= userdata
;
1930 if (!(e
= element_get(p
, section
, TRUE
))) {
1931 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1935 if (pa_streq(rvalue
, "playback"))
1936 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1937 else if (pa_streq(rvalue
, "capture"))
1938 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1940 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1947 static int element_parse_direction_try_other(
1948 const char *filename
,
1950 const char *section
,
1956 pa_alsa_path
*p
= userdata
;
1960 if (!(e
= element_get(p
, section
, TRUE
))) {
1961 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1965 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1966 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1970 e
->direction_try_other
= !!yes
;
1974 static int element_parse_volume_limit(
1975 const char *filename
,
1977 const char *section
,
1983 pa_alsa_path
*p
= userdata
;
1987 if (!(e
= element_get(p
, section
, TRUE
))) {
1988 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
1992 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
1993 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
1997 e
->volume_limit
= volume_limit
;
2001 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2002 pa_channel_position_mask_t v
;
2004 if (pa_streq(m
, "all-left"))
2005 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2006 else if (pa_streq(m
, "all-right"))
2007 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2008 else if (pa_streq(m
, "all-center"))
2009 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2010 else if (pa_streq(m
, "all-front"))
2011 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2012 else if (pa_streq(m
, "all-rear"))
2013 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2014 else if (pa_streq(m
, "all-side"))
2015 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2016 else if (pa_streq(m
, "all-top"))
2017 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2018 else if (pa_streq(m
, "all-no-lfe"))
2019 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2020 else if (pa_streq(m
, "all"))
2021 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2023 pa_channel_position_t p
;
2025 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2028 v
= PA_CHANNEL_POSITION_MASK(p
);
2034 static int element_parse_override_map(
2035 const char *filename
,
2037 const char *section
,
2043 pa_alsa_path
*p
= userdata
;
2045 const char *state
= NULL
;
2049 if (!(e
= element_get(p
, section
, TRUE
))) {
2050 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2054 while ((n
= pa_split(rvalue
, ",", &state
))) {
2055 pa_channel_position_mask_t m
;
2060 if ((m
= parse_mask(n
)) == 0) {
2061 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2067 if (pa_streq(lvalue
, "override-map.1"))
2068 e
->masks
[i
++][0] = m
;
2070 e
->masks
[i
++][1] = m
;
2072 /* Later on we might add override-map.3 and so on here ... */
2077 e
->override_map
= TRUE
;
2082 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2083 snd_mixer_selem_id_t
*sid
;
2084 snd_mixer_elem_t
*me
;
2090 SELEM_INIT(sid
, e
->alsa_name
);
2091 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2092 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2096 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2098 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2099 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2101 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2104 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2107 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2109 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2110 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2116 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2123 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2124 element_set_option(o
->element
, m
, o
->alsa_idx
);
2129 static int option_verify(pa_alsa_option
*o
) {
2130 static const struct description_map well_known_descriptions
[] = {
2131 { "input", N_("Input") },
2132 { "input-docking", N_("Docking Station Input") },
2133 { "input-docking-microphone", N_("Docking Station Microphone") },
2134 { "input-docking-linein", N_("Docking Station Line-In") },
2135 { "input-linein", N_("Line-In") },
2136 { "input-microphone", N_("Microphone") },
2137 { "input-microphone-front", N_("Front Microphone") },
2138 { "input-microphone-rear", N_("Rear Microphone") },
2139 { "input-microphone-external", N_("External Microphone") },
2140 { "input-microphone-internal", N_("Internal Microphone") },
2141 { "input-radio", N_("Radio") },
2142 { "input-video", N_("Video") },
2143 { "input-agc-on", N_("Automatic Gain Control") },
2144 { "input-agc-off", N_("No Automatic Gain Control") },
2145 { "input-boost-on", N_("Boost") },
2146 { "input-boost-off", N_("No Boost") },
2147 { "output-amplifier-on", N_("Amplifier") },
2148 { "output-amplifier-off", N_("No Amplifier") },
2149 { "output-bass-boost-on", N_("Bass Boost") },
2150 { "output-bass-boost-off", N_("No Bass Boost") },
2151 { "output-speaker", N_("Speaker") },
2152 { "output-headphones", N_("Headphones") }
2158 pa_log("No name set for option %s", o
->alsa_name
);
2162 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2163 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2164 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2168 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2169 !pa_streq(o
->alsa_name
, "on") &&
2170 !pa_streq(o
->alsa_name
, "off")) {
2171 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2175 if (!o
->description
)
2176 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2177 well_known_descriptions
,
2178 PA_ELEMENTSOF(well_known_descriptions
)));
2179 if (!o
->description
)
2180 o
->description
= pa_xstrdup(o
->name
);
2185 static int element_verify(pa_alsa_element
*e
) {
2190 // 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);
2191 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2192 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2193 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2194 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2195 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2199 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2200 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2204 PA_LLIST_FOREACH(o
, e
->options
)
2205 if (option_verify(o
) < 0)
2211 static int path_verify(pa_alsa_path
*p
) {
2212 static const struct description_map well_known_descriptions
[] = {
2213 { "analog-input", N_("Analog Input") },
2214 { "analog-input-microphone", N_("Analog Microphone") },
2215 { "analog-input-microphone-front", N_("Front Microphone") },
2216 { "analog-input-microphone-rear", N_("Rear Microphone") },
2217 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2218 { "analog-input-microphone-internal", N_("Internal Microphone") },
2219 { "analog-input-linein", N_("Analog Line-In") },
2220 { "analog-input-radio", N_("Analog Radio") },
2221 { "analog-input-video", N_("Analog Video") },
2222 { "analog-output", N_("Analog Output") },
2223 { "analog-output-headphones", N_("Analog Headphones") },
2224 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2225 { "analog-output-mono", N_("Analog Mono Output") },
2226 { "analog-output-speaker", N_("Analog Speakers") },
2227 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2228 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2235 PA_LLIST_FOREACH(e
, p
->elements
)
2236 if (element_verify(e
) < 0)
2239 if (!p
->description
)
2240 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2241 well_known_descriptions
,
2242 PA_ELEMENTSOF(well_known_descriptions
)));
2244 if (!p
->description
)
2245 p
->description
= pa_xstrdup(p
->name
);
2250 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2256 pa_config_item items
[] = {
2258 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2259 { "description", pa_config_parse_string
, NULL
, "General" },
2260 { "name", pa_config_parse_string
, NULL
, "General" },
2263 { "priority", option_parse_priority
, NULL
, NULL
},
2264 { "name", option_parse_name
, NULL
, NULL
},
2267 { "switch", element_parse_switch
, NULL
, NULL
},
2268 { "volume", element_parse_volume
, NULL
, NULL
},
2269 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2270 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2271 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2272 /* ... later on we might add override-map.3 and so on here ... */
2273 { "required", element_parse_required
, NULL
, NULL
},
2274 { "required-any", element_parse_required
, NULL
, NULL
},
2275 { "required-absent", element_parse_required
, NULL
, NULL
},
2276 { "direction", element_parse_direction
, NULL
, NULL
},
2277 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2278 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2279 { NULL
, NULL
, NULL
, NULL
}
2284 p
= pa_xnew0(pa_alsa_path
, 1);
2285 n
= pa_path_get_filename(fname
);
2286 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2287 p
->direction
= direction
;
2289 items
[0].data
= &p
->priority
;
2290 items
[1].data
= &p
->description
;
2291 items
[2].data
= &p
->name
;
2293 fn
= pa_maybe_prefix_path(fname
,
2294 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2297 r
= pa_config_parse(fn
, NULL
, items
, p
);
2303 if (path_verify(p
) < 0)
2309 pa_alsa_path_free(p
);
2313 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2319 p
= pa_xnew0(pa_alsa_path
, 1);
2320 p
->name
= pa_xstrdup(element
);
2321 p
->direction
= direction
;
2323 e
= pa_xnew0(pa_alsa_element
, 1);
2325 e
->alsa_name
= pa_xstrdup(element
);
2326 e
->direction
= direction
;
2327 e
->volume_limit
= -1;
2329 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2330 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2332 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2333 p
->last_element
= e
;
2337 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2338 pa_alsa_option
*o
, *n
;
2342 for (o
= e
->options
; o
; o
= n
) {
2345 if (o
->alsa_idx
< 0) {
2346 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2352 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2353 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2354 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2357 static void path_drop_unsupported(pa_alsa_path
*p
) {
2358 pa_alsa_element
*e
, *n
;
2362 for (e
= p
->elements
; e
; e
= n
) {
2365 if (!element_drop_unsupported(e
)) {
2366 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2372 static void path_make_options_unique(pa_alsa_path
*p
) {
2374 pa_alsa_option
*o
, *u
;
2376 PA_LLIST_FOREACH(e
, p
->elements
) {
2377 PA_LLIST_FOREACH(o
, e
->options
) {
2381 for (u
= o
->next
; u
; u
= u
->next
)
2382 if (pa_streq(u
->name
, o
->name
))
2388 m
= pa_xstrdup(o
->name
);
2390 /* OK, this name is not unique, hence let's rename */
2391 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2394 if (!pa_streq(u
->name
, m
))
2397 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2401 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2402 pa_xfree(u
->description
);
2403 u
->description
= nd
;
2413 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2416 for (; e
; e
= e
->next
)
2417 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2418 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2424 for (o
= e
->options
; o
; o
= o
->next
) {
2428 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2429 s
->options
= pa_idxset_copy(template->options
);
2430 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2432 (template->description
[0] && o
->description
[0])
2433 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2434 : (template->description
[0]
2435 ? pa_xstrdup(template->description
)
2436 : pa_xstrdup(o
->description
));
2438 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2440 s
= pa_xnew0(pa_alsa_setting
, 1);
2441 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2442 s
->name
= pa_xstrdup(o
->name
);
2443 s
->description
= pa_xstrdup(o
->description
);
2444 s
->priority
= o
->priority
;
2447 pa_idxset_put(s
->options
, o
, NULL
);
2449 if (element_create_settings(e
->next
, s
))
2450 /* This is not a leaf, so let's get rid of it */
2453 /* This is a leaf, so let's add it */
2454 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2456 e
->path
->last_setting
= s
;
2463 static void path_create_settings(pa_alsa_path
*p
) {
2466 element_create_settings(p
->elements
, NULL
);
2469 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2471 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2472 pa_channel_position_t t
;
2473 pa_channel_position_mask_t path_volume_channels
= 0;
2484 pa_log_debug("Probing path '%s'", p
->name
);
2486 PA_LLIST_FOREACH(e
, p
->elements
) {
2487 if (element_probe(e
, m
) < 0) {
2488 p
->supported
= FALSE
;
2489 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2492 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
);
2497 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2499 if (!p
->has_volume
) {
2500 p
->min_volume
= e
->min_volume
;
2501 p
->max_volume
= e
->max_volume
;
2505 if (!p
->has_volume
) {
2506 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2507 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2508 min_dB
[t
] = e
->min_dB
;
2509 max_dB
[t
] = e
->max_dB
;
2510 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2517 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2518 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2519 min_dB
[t
] += e
->min_dB
;
2520 max_dB
[t
] += e
->max_dB
;
2521 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2524 /* Hmm, there's another element before us
2525 * which cannot do dB volumes, so we we need
2526 * to 'neutralize' this slider */
2527 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2528 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2531 } else if (p
->has_volume
) {
2532 /* We can't use this volume, so let's ignore it */
2533 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2534 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2536 p
->has_volume
= TRUE
;
2539 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2543 if (p
->has_req_any
&& !p
->req_any_present
) {
2544 p
->supported
= FALSE
;
2545 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2549 path_drop_unsupported(p
);
2550 path_make_options_unique(p
);
2551 path_create_settings(p
);
2553 p
->supported
= TRUE
;
2556 p
->min_dB
= INFINITY
;
2557 p
->max_dB
= -INFINITY
;
2559 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2560 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2561 if (p
->min_dB
> min_dB
[t
])
2562 p
->min_dB
= min_dB
[t
];
2564 if (p
->max_dB
< max_dB
[t
])
2565 p
->max_dB
= max_dB
[t
];
2572 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2575 pa_log_debug("Setting %s (%s) priority=%u",
2577 pa_strnull(s
->description
),
2581 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2584 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2586 pa_strnull(o
->name
),
2587 pa_strnull(o
->description
),
2592 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2596 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",
2606 (long long unsigned) e
->merged_mask
,
2608 pa_yes_no(e
->override_map
));
2610 PA_LLIST_FOREACH(o
, e
->options
)
2611 pa_alsa_option_dump(o
);
2614 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2619 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2620 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2622 pa_strnull(p
->description
),
2625 pa_yes_no(p
->probed
),
2626 pa_yes_no(p
->supported
),
2627 pa_yes_no(p
->has_mute
),
2628 pa_yes_no(p
->has_volume
),
2629 pa_yes_no(p
->has_dB
),
2630 p
->min_volume
, p
->max_volume
,
2631 p
->min_dB
, p
->max_dB
);
2633 PA_LLIST_FOREACH(e
, p
->elements
)
2634 pa_alsa_element_dump(e
);
2636 PA_LLIST_FOREACH(s
, p
->settings
)
2637 pa_alsa_setting_dump(s
);
2640 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2641 snd_mixer_selem_id_t
*sid
;
2642 snd_mixer_elem_t
*me
;
2648 SELEM_INIT(sid
, e
->alsa_name
);
2649 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2650 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2654 snd_mixer_elem_set_callback(me
, cb
);
2655 snd_mixer_elem_set_callback_private(me
, userdata
);
2658 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2665 PA_LLIST_FOREACH(e
, p
->elements
)
2666 element_set_callback(e
, m
, cb
, userdata
);
2669 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2676 PA_LLIST_FOREACH(p
, ps
->paths
)
2677 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2680 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2681 pa_alsa_path_set
*ps
;
2682 char **pn
= NULL
, **en
= NULL
, **ie
;
2683 pa_alsa_decibel_fix
*db_fix
;
2687 pa_assert(m
->profile_set
);
2688 pa_assert(m
->profile_set
->decibel_fixes
);
2689 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2691 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2694 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2695 ps
->direction
= direction
;
2697 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2698 pn
= m
->output_path_names
;
2699 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2700 pn
= m
->input_path_names
;
2705 for (in
= pn
; *in
; in
++) {
2707 pa_bool_t duplicate
= FALSE
;
2710 for (kn
= pn
; kn
< in
; kn
++)
2711 if (pa_streq(*kn
, *in
)) {
2719 fn
= pa_sprintf_malloc("%s.conf", *in
);
2721 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2723 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2733 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2734 en
= m
->output_element
;
2735 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2736 en
= m
->input_element
;
2739 pa_alsa_path_set_free(ps
);
2743 for (ie
= en
; *ie
; ie
++) {
2747 p
= pa_alsa_path_synthesize(*ie
, direction
);
2750 /* Mark all other passed elements for require-absent */
2751 for (je
= en
; *je
; je
++) {
2757 e
= pa_xnew0(pa_alsa_element
, 1);
2759 e
->alsa_name
= pa_xstrdup(*je
);
2760 e
->direction
= direction
;
2761 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2762 e
->volume_limit
= -1;
2764 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2765 p
->last_element
= e
;
2768 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2773 /* Assign decibel fixes to elements. */
2774 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2777 PA_LLIST_FOREACH(p
, ps
->paths
) {
2780 PA_LLIST_FOREACH(e
, p
->elements
) {
2781 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2782 /* The profile set that contains the dB fix may be freed
2783 * before the element, so we have to copy the dB fix
2785 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2786 e
->db_fix
->profile_set
= NULL
;
2787 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2788 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2797 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2801 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2804 pa_yes_no(ps
->probed
));
2806 PA_LLIST_FOREACH(p
, ps
->paths
)
2807 pa_alsa_path_dump(p
);
2810 static void path_set_unify(pa_alsa_path_set
*ps
) {
2812 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2815 /* We have issues dealing with paths that vary too wildly. That
2816 * means for now we have to have all paths support volume/mute/dB
2819 PA_LLIST_FOREACH(p
, ps
->paths
) {
2820 pa_assert(p
->probed
);
2824 else if (!p
->has_dB
)
2831 if (!has_volume
|| !has_dB
|| !has_mute
) {
2834 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2836 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2839 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2841 PA_LLIST_FOREACH(p
, ps
->paths
) {
2843 p
->has_volume
= FALSE
;
2848 p
->has_mute
= FALSE
;
2853 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2854 pa_alsa_path
*p
, *q
;
2856 PA_LLIST_FOREACH(p
, ps
->paths
) {
2860 for (q
= p
->next
; q
; q
= q
->next
)
2861 if (pa_streq(q
->name
, p
->name
))
2867 m
= pa_xstrdup(p
->name
);
2869 /* OK, this name is not unique, hence let's rename */
2870 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2873 if (!pa_streq(q
->name
, m
))
2876 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2880 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2881 pa_xfree(q
->description
);
2882 q
->description
= nd
;
2891 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2892 pa_alsa_path
*p
, *n
;
2899 for (p
= ps
->paths
; p
; p
= n
) {
2902 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2903 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2904 pa_alsa_path_free(p
);
2909 path_set_make_paths_unique(ps
);
2913 static void mapping_free(pa_alsa_mapping
*m
) {
2917 pa_xfree(m
->description
);
2919 pa_xstrfreev(m
->device_strings
);
2920 pa_xstrfreev(m
->input_path_names
);
2921 pa_xstrfreev(m
->output_path_names
);
2922 pa_xstrfreev(m
->input_element
);
2923 pa_xstrfreev(m
->output_element
);
2925 pa_assert(!m
->input_pcm
);
2926 pa_assert(!m
->output_pcm
);
2931 static void profile_free(pa_alsa_profile
*p
) {
2935 pa_xfree(p
->description
);
2937 pa_xstrfreev(p
->input_mapping_names
);
2938 pa_xstrfreev(p
->output_mapping_names
);
2940 if (p
->input_mappings
)
2941 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2943 if (p
->output_mappings
)
2944 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2949 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2955 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2958 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2964 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2967 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2970 if (ps
->decibel_fixes
) {
2971 pa_alsa_decibel_fix
*db_fix
;
2973 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2974 decibel_fix_free(db_fix
);
2976 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2982 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2985 if (!pa_startswith(name
, "Mapping "))
2990 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2993 m
= pa_xnew0(pa_alsa_mapping
, 1);
2994 m
->profile_set
= ps
;
2995 m
->name
= pa_xstrdup(name
);
2996 pa_channel_map_init(&m
->channel_map
);
2998 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3003 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3006 if (!pa_startswith(name
, "Profile "))
3011 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3014 p
= pa_xnew0(pa_alsa_profile
, 1);
3015 p
->profile_set
= ps
;
3016 p
->name
= pa_xstrdup(name
);
3018 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3023 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3024 pa_alsa_decibel_fix
*db_fix
;
3026 if (!pa_startswith(name
, "DecibelFix "))
3031 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3034 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3035 db_fix
->profile_set
= ps
;
3036 db_fix
->name
= pa_xstrdup(name
);
3038 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3043 static int mapping_parse_device_strings(
3044 const char *filename
,
3046 const char *section
,
3052 pa_alsa_profile_set
*ps
= userdata
;
3057 if (!(m
= mapping_get(ps
, section
))) {
3058 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3062 pa_xstrfreev(m
->device_strings
);
3063 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3064 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3071 static int mapping_parse_channel_map(
3072 const char *filename
,
3074 const char *section
,
3080 pa_alsa_profile_set
*ps
= userdata
;
3085 if (!(m
= mapping_get(ps
, section
))) {
3086 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3090 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3091 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3098 static int mapping_parse_paths(
3099 const char *filename
,
3101 const char *section
,
3107 pa_alsa_profile_set
*ps
= userdata
;
3112 if (!(m
= mapping_get(ps
, section
))) {
3113 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3117 if (pa_streq(lvalue
, "paths-input")) {
3118 pa_xstrfreev(m
->input_path_names
);
3119 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3121 pa_xstrfreev(m
->output_path_names
);
3122 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3128 static int mapping_parse_element(
3129 const char *filename
,
3131 const char *section
,
3137 pa_alsa_profile_set
*ps
= userdata
;
3142 if (!(m
= mapping_get(ps
, section
))) {
3143 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3147 if (pa_streq(lvalue
, "element-input")) {
3148 pa_xstrfreev(m
->input_element
);
3149 m
->input_element
= pa_split_spaces_strv(rvalue
);
3151 pa_xstrfreev(m
->output_element
);
3152 m
->output_element
= pa_split_spaces_strv(rvalue
);
3158 static int mapping_parse_direction(
3159 const char *filename
,
3161 const char *section
,
3167 pa_alsa_profile_set
*ps
= userdata
;
3172 if (!(m
= mapping_get(ps
, section
))) {
3173 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3177 if (pa_streq(rvalue
, "input"))
3178 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3179 else if (pa_streq(rvalue
, "output"))
3180 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3181 else if (pa_streq(rvalue
, "any"))
3182 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3184 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3191 static int mapping_parse_description(
3192 const char *filename
,
3194 const char *section
,
3200 pa_alsa_profile_set
*ps
= userdata
;
3206 if ((m
= mapping_get(ps
, section
))) {
3207 pa_xfree(m
->description
);
3208 m
->description
= pa_xstrdup(rvalue
);
3209 } else if ((p
= profile_get(ps
, section
))) {
3210 pa_xfree(p
->description
);
3211 p
->description
= pa_xstrdup(rvalue
);
3213 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3220 static int mapping_parse_priority(
3221 const char *filename
,
3223 const char *section
,
3229 pa_alsa_profile_set
*ps
= userdata
;
3236 if (pa_atou(rvalue
, &prio
) < 0) {
3237 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3241 if ((m
= mapping_get(ps
, section
)))
3243 else if ((p
= profile_get(ps
, section
)))
3246 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3253 static int profile_parse_mappings(
3254 const char *filename
,
3256 const char *section
,
3262 pa_alsa_profile_set
*ps
= userdata
;
3267 if (!(p
= profile_get(ps
, section
))) {
3268 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3272 if (pa_streq(lvalue
, "input-mappings")) {
3273 pa_xstrfreev(p
->input_mapping_names
);
3274 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3276 pa_xstrfreev(p
->output_mapping_names
);
3277 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3283 static int profile_parse_skip_probe(
3284 const char *filename
,
3286 const char *section
,
3292 pa_alsa_profile_set
*ps
= userdata
;
3298 if (!(p
= profile_get(ps
, section
))) {
3299 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3303 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3304 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3313 static int decibel_fix_parse_db_values(
3314 const char *filename
,
3316 const char *section
,
3322 pa_alsa_profile_set
*ps
= userdata
;
3323 pa_alsa_decibel_fix
*db_fix
;
3327 unsigned n
= 8; /* Current size of the db_values table. */
3328 unsigned min_step
= 0;
3329 unsigned max_step
= 0;
3330 unsigned i
= 0; /* Index to the items table. */
3331 unsigned prev_step
= 0;
3334 pa_assert(filename
);
3340 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3341 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3345 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3346 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3350 db_values
= pa_xnew(long, n
);
3352 while ((item
= items
[i
++])) {
3353 char *s
= item
; /* Step value string. */
3354 char *d
= item
; /* dB value string. */
3358 /* Move d forward until it points to a colon or to the end of the item. */
3359 for (; *d
&& *d
!= ':'; ++d
);
3362 /* item started with colon. */
3363 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3367 if (!*d
|| !*(d
+ 1)) {
3368 /* No colon found, or it was the last character in item. */
3369 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3373 /* pa_atou() needs a null-terminating string. Let's replace the colon
3374 * with a zero byte. */
3377 if (pa_atou(s
, &step
) < 0) {
3378 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3382 if (pa_atod(d
, &db
) < 0) {
3383 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3387 if (step
<= prev_step
&& i
!= 1) {
3388 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3392 if (db
< prev_db
&& i
!= 1) {
3393 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3399 db_values
[0] = (long) (db
* 100.0);
3403 /* Interpolate linearly. */
3404 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3406 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3408 /* Reallocate the db_values table if it's about to overflow. */
3409 if (prev_step
+ 1 - min_step
== n
) {
3411 db_values
= pa_xrenew(long, db_values
, n
);
3414 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3421 db_fix
->min_step
= min_step
;
3422 db_fix
->max_step
= max_step
;
3423 pa_xfree(db_fix
->db_values
);
3424 db_fix
->db_values
= db_values
;
3426 pa_xstrfreev(items
);
3431 pa_xstrfreev(items
);
3432 pa_xfree(db_values
);
3437 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3439 static const struct description_map well_known_descriptions
[] = {
3440 { "analog-mono", N_("Analog Mono") },
3441 { "analog-stereo", N_("Analog Stereo") },
3442 { "analog-surround-21", N_("Analog Surround 2.1") },
3443 { "analog-surround-30", N_("Analog Surround 3.0") },
3444 { "analog-surround-31", N_("Analog Surround 3.1") },
3445 { "analog-surround-40", N_("Analog Surround 4.0") },
3446 { "analog-surround-41", N_("Analog Surround 4.1") },
3447 { "analog-surround-50", N_("Analog Surround 5.0") },
3448 { "analog-surround-51", N_("Analog Surround 5.1") },
3449 { "analog-surround-61", N_("Analog Surround 6.0") },
3450 { "analog-surround-61", N_("Analog Surround 6.1") },
3451 { "analog-surround-70", N_("Analog Surround 7.0") },
3452 { "analog-surround-71", N_("Analog Surround 7.1") },
3453 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3454 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3455 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3456 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3457 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3462 if (!pa_channel_map_valid(&m
->channel_map
)) {
3463 pa_log("Mapping %s is missing channel map.", m
->name
);
3467 if (!m
->device_strings
) {
3468 pa_log("Mapping %s is missing device strings.", m
->name
);
3472 if ((m
->input_path_names
&& m
->input_element
) ||
3473 (m
->output_path_names
&& m
->output_element
)) {
3474 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3478 if (!m
->description
)
3479 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3480 well_known_descriptions
,
3481 PA_ELEMENTSOF(well_known_descriptions
)));
3483 if (!m
->description
)
3484 m
->description
= pa_xstrdup(m
->name
);
3487 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3489 else if (m
->channel_map
.channels
== bonus
->channels
)
3496 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3497 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3501 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3503 pa_strnull(m
->description
),
3505 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3506 pa_yes_no(m
->supported
),
3510 static void profile_set_add_auto_pair(
3511 pa_alsa_profile_set
*ps
,
3512 pa_alsa_mapping
*m
, /* output */
3513 pa_alsa_mapping
*n
/* input */) {
3521 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3524 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3528 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3530 name
= pa_sprintf_malloc("output:%s", m
->name
);
3532 name
= pa_sprintf_malloc("input:%s", n
->name
);
3534 if (pa_hashmap_get(ps
->profiles
, name
)) {
3539 p
= pa_xnew0(pa_alsa_profile
, 1);
3540 p
->profile_set
= ps
;
3544 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3545 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3546 p
->priority
+= m
->priority
* 100;
3550 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3551 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3552 p
->priority
+= n
->priority
;
3555 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3558 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3559 pa_alsa_mapping
*m
, *n
;
3560 void *m_state
, *n_state
;
3564 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3565 profile_set_add_auto_pair(ps
, m
, NULL
);
3567 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3568 profile_set_add_auto_pair(ps
, m
, n
);
3571 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3572 profile_set_add_auto_pair(ps
, NULL
, n
);
3575 static int profile_verify(pa_alsa_profile
*p
) {
3577 static const struct description_map well_known_descriptions
[] = {
3578 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3579 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3580 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3581 { "off", N_("Off") }
3586 /* Replace the output mapping names by the actual mappings */
3587 if (p
->output_mapping_names
) {
3590 pa_assert(!p
->output_mappings
);
3591 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3593 for (name
= p
->output_mapping_names
; *name
; name
++) {
3596 pa_bool_t duplicate
= FALSE
;
3598 for (in
= name
+ 1; *in
; in
++)
3599 if (pa_streq(*name
, *in
)) {
3607 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3608 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3612 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3618 pa_xstrfreev(p
->output_mapping_names
);
3619 p
->output_mapping_names
= NULL
;
3622 /* Replace the input mapping names by the actual mappings */
3623 if (p
->input_mapping_names
) {
3626 pa_assert(!p
->input_mappings
);
3627 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3629 for (name
= p
->input_mapping_names
; *name
; name
++) {
3632 pa_bool_t duplicate
= FALSE
;
3634 for (in
= name
+ 1; *in
; in
++)
3635 if (pa_streq(*name
, *in
)) {
3643 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3644 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3648 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3654 pa_xstrfreev(p
->input_mapping_names
);
3655 p
->input_mapping_names
= NULL
;
3658 if (!p
->input_mappings
&& !p
->output_mappings
) {
3659 pa_log("Profile '%s' lacks mappings.", p
->name
);
3663 if (!p
->description
)
3664 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3665 well_known_descriptions
,
3666 PA_ELEMENTSOF(well_known_descriptions
)));
3668 if (!p
->description
) {
3673 sb
= pa_strbuf_new();
3675 if (p
->output_mappings
)
3676 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3677 if (!pa_strbuf_isempty(sb
))
3678 pa_strbuf_puts(sb
, " + ");
3680 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3683 if (p
->input_mappings
)
3684 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3685 if (!pa_strbuf_isempty(sb
))
3686 pa_strbuf_puts(sb
, " + ");
3688 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3691 p
->description
= pa_strbuf_tostring_free(sb
);
3697 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3702 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3704 pa_strnull(p
->description
),
3706 pa_yes_no(p
->supported
),
3707 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3708 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3710 if (p
->input_mappings
)
3711 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3712 pa_log_debug("Input %s", m
->name
);
3714 if (p
->output_mappings
)
3715 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3716 pa_log_debug("Output %s", m
->name
);
3719 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3722 /* Check that the dB mapping has been configured. Since "db-values" is
3723 * currently the only option in the DecibelFix section, and decibel fix
3724 * objects don't get created if a DecibelFix section is empty, this is
3725 * actually a redundant check. Having this may prevent future bugs,
3727 if (!db_fix
->db_values
) {
3728 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3735 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3736 char *db_values
= NULL
;
3740 if (db_fix
->db_values
) {
3742 unsigned long i
, nsteps
;
3744 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3745 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3747 buf
= pa_strbuf_new();
3748 for (i
= 0; i
< nsteps
; ++i
)
3749 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3751 db_values
= pa_strbuf_tostring_free(buf
);
3754 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3755 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3757 pa_xfree(db_values
);
3760 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3761 pa_alsa_profile_set
*ps
;
3764 pa_alsa_decibel_fix
*db_fix
;
3769 static pa_config_item items
[] = {
3771 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3774 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3775 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3776 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3777 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3778 { "element-input", mapping_parse_element
, NULL
, NULL
},
3779 { "element-output", mapping_parse_element
, NULL
, NULL
},
3780 { "direction", mapping_parse_direction
, NULL
, NULL
},
3782 /* Shared by [Mapping ...] and [Profile ...] */
3783 { "description", mapping_parse_description
, NULL
, NULL
},
3784 { "priority", mapping_parse_priority
, NULL
, NULL
},
3787 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3788 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3789 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3791 /* [DecibelFix ...] */
3792 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3793 { NULL
, NULL
, NULL
, NULL
}
3796 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3797 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3798 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3799 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3801 items
[0].data
= &ps
->auto_profiles
;
3804 fname
= "default.conf";
3806 fn
= pa_maybe_prefix_path(fname
,
3807 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3808 PA_ALSA_PROFILE_SETS_DIR
);
3810 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3816 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3817 if (mapping_verify(m
, bonus
) < 0)
3820 if (ps
->auto_profiles
)
3821 profile_set_add_auto(ps
);
3823 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3824 if (profile_verify(p
) < 0)
3827 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3828 if (decibel_fix_verify(db_fix
) < 0)
3834 pa_alsa_profile_set_free(ps
);
3838 void pa_alsa_profile_set_probe(
3839 pa_alsa_profile_set
*ps
,
3841 const pa_sample_spec
*ss
,
3842 unsigned default_n_fragments
,
3843 unsigned default_fragment_size_msec
) {
3846 pa_alsa_profile
*p
, *last
= NULL
;
3856 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3857 pa_sample_spec try_ss
;
3858 pa_channel_map try_map
;
3859 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3862 /* Is this already marked that it is supported? (i.e. from the config file) */
3866 pa_log_debug("Looking at profile %s", p
->name
);
3868 /* Close PCMs from the last iteration we don't need anymore */
3869 if (last
&& last
->output_mappings
)
3870 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3875 if (last
->supported
)
3878 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3879 snd_pcm_close(m
->output_pcm
);
3880 m
->output_pcm
= NULL
;
3884 if (last
&& last
->input_mappings
)
3885 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3890 if (last
->supported
)
3893 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3894 snd_pcm_close(m
->input_pcm
);
3895 m
->input_pcm
= NULL
;
3899 p
->supported
= TRUE
;
3901 /* Check if we can open all new ones */
3902 if (p
->output_mappings
)
3903 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3908 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3909 try_map
= m
->channel_map
;
3911 try_ss
.channels
= try_map
.channels
;
3914 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3915 pa_frame_size(&try_ss
);
3916 try_buffer_size
= default_n_fragments
* try_period_size
;
3918 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3923 SND_PCM_STREAM_PLAYBACK
,
3924 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3926 p
->supported
= FALSE
;
3931 if (p
->input_mappings
&& p
->supported
)
3932 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3937 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3938 try_map
= m
->channel_map
;
3940 try_ss
.channels
= try_map
.channels
;
3943 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3944 pa_frame_size(&try_ss
);
3945 try_buffer_size
= default_n_fragments
* try_period_size
;
3947 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3952 SND_PCM_STREAM_CAPTURE
,
3953 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3955 p
->supported
= FALSE
;
3963 pa_log_debug("Profile %s supported.", p
->name
);
3970 if (last
->output_mappings
)
3971 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3972 if (m
->output_pcm
) {
3974 if (last
->supported
)
3977 snd_pcm_close(m
->output_pcm
);
3978 m
->output_pcm
= NULL
;
3981 if (last
->input_mappings
)
3982 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3985 if (last
->supported
)
3988 snd_pcm_close(m
->input_pcm
);
3989 m
->input_pcm
= NULL
;
3993 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3994 if (!p
->supported
) {
3995 pa_hashmap_remove(ps
->profiles
, p
->name
);
3999 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4000 if (m
->supported
<= 0) {
4001 pa_hashmap_remove(ps
->mappings
, m
->name
);
4008 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4011 pa_alsa_decibel_fix
*db_fix
;
4016 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4019 pa_yes_no(ps
->auto_profiles
),
4020 pa_yes_no(ps
->probed
),
4021 pa_hashmap_size(ps
->mappings
),
4022 pa_hashmap_size(ps
->profiles
),
4023 pa_hashmap_size(ps
->decibel_fixes
));
4025 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4026 pa_alsa_mapping_dump(m
);
4028 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4029 pa_alsa_profile_dump(p
);
4031 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4032 pa_alsa_decibel_fix_dump(db_fix
);
4035 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4042 /* if there is no path, we don't want a port list */
4046 if (!ps
->paths
->next
){
4049 /* If there is only one path, but no or only one setting, then
4050 * we want a port list either */
4051 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4054 /* Ok, there is only one path, however with multiple settings,
4055 * so let's create a port for each setting */
4056 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4058 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4059 pa_device_port
*port
;
4060 pa_alsa_port_data
*data
;
4062 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4063 port
->priority
= s
->priority
;
4065 data
= PA_DEVICE_PORT_DATA(port
);
4066 data
->path
= ps
->paths
;
4069 pa_hashmap_put(*p
, port
->name
, port
);
4074 /* We have multiple paths, so let's create a port for each
4075 * one, and each of each settings */
4076 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4078 PA_LLIST_FOREACH(path
, ps
->paths
) {
4080 if (!path
->settings
|| !path
->settings
->next
) {
4081 pa_device_port
*port
;
4082 pa_alsa_port_data
*data
;
4084 /* If there is no or just one setting we only need a
4087 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4088 port
->priority
= path
->priority
* 100;
4091 data
= PA_DEVICE_PORT_DATA(port
);
4093 data
->setting
= path
->settings
;
4095 pa_hashmap_put(*p
, port
->name
, port
);
4099 PA_LLIST_FOREACH(s
, path
->settings
) {
4100 pa_device_port
*port
;
4101 pa_alsa_port_data
*data
;
4104 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4106 if (s
->description
[0])
4107 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4109 d
= pa_xstrdup(path
->description
);
4111 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4112 port
->priority
= path
->priority
* 100 + s
->priority
;
4117 data
= PA_DEVICE_PORT_DATA(port
);
4121 pa_hashmap_put(*p
, port
->name
, port
);
4127 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));