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
);
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
)) {
909 r
= snd_mixer_selem_set_playback_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
911 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
917 if ((r
= snd_mixer_selem_set_playback_dB(me
, c
, value
, rounding
)) >= 0)
918 r
= snd_mixer_selem_get_playback_dB(me
, c
, &value
);
921 if ((r
= snd_mixer_selem_ask_playback_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
922 r
= snd_mixer_selem_ask_playback_vol_dB(me
, alsa_val
, &value
);
928 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
932 r
= snd_mixer_selem_set_capture_volume(me
, c
, decibel_fix_get_step(e
->db_fix
, &value
, rounding
));
934 decibel_fix_get_step(e
->db_fix
, &value
, rounding
);
940 if ((r
= snd_mixer_selem_set_capture_dB(me
, c
, value
, rounding
)) >= 0)
941 r
= snd_mixer_selem_get_capture_dB(me
, c
, &value
);
944 if ((r
= snd_mixer_selem_ask_capture_dB_vol(me
, value
, rounding
, &alsa_val
)) >= 0)
945 r
= snd_mixer_selem_ask_capture_vol_dB(me
, alsa_val
, &value
);
955 #ifdef HAVE_VALGRIND_MEMCHECK_H
956 VALGRIND_MAKE_MEM_DEFINED(&value
, sizeof(value
));
959 f
= from_alsa_dB(value
);
964 value
= to_alsa_volume(f
, e
->min_volume
, e
->max_volume
);
966 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
967 if (snd_mixer_selem_has_playback_channel(me
, c
)) {
968 if ((r
= snd_mixer_selem_set_playback_volume(me
, c
, value
)) >= 0)
969 r
= snd_mixer_selem_get_playback_volume(me
, c
, &value
);
973 if (snd_mixer_selem_has_capture_channel(me
, c
)) {
974 if ((r
= snd_mixer_selem_set_capture_volume(me
, c
, value
)) >= 0)
975 r
= snd_mixer_selem_get_capture_volume(me
, c
, &value
);
983 f
= from_alsa_volume(value
, e
->min_volume
, e
->max_volume
);
986 for (k
= 0; k
< cm
->channels
; k
++)
987 if (e
->masks
[c
][e
->n_channels
-1] & PA_CHANNEL_POSITION_MASK(cm
->map
[k
]))
988 if (rv
.values
[k
] < f
)
991 mask
|= e
->masks
[c
][e
->n_channels
-1];
994 for (k
= 0; k
< cm
->channels
; k
++)
995 if (!(mask
& PA_CHANNEL_POSITION_MASK(cm
->map
[k
])))
996 rv
.values
[k
] = PA_VOLUME_NORM
;
1002 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
) {
1011 pa_assert(pa_cvolume_compatible_with_channel_map(v
, cm
));
1016 rv
= *v
; /* Remaining adjustment */
1017 pa_cvolume_reset(v
, cm
->channels
); /* Adjustment done */
1019 PA_LLIST_FOREACH(e
, p
->elements
) {
1022 if (e
->volume_use
!= PA_ALSA_VOLUME_MERGE
)
1025 pa_assert(!p
->has_dB
|| e
->has_dB
);
1028 if (element_set_volume(e
, m
, cm
, &ev
, write_to_hw
) < 0)
1036 pa_sw_cvolume_multiply(v
, v
, &ev
);
1037 pa_sw_cvolume_divide(&rv
, &rv
, &ev
);
1043 static int element_set_switch(pa_alsa_element
*e
, snd_mixer_t
*m
, pa_bool_t b
) {
1044 snd_mixer_elem_t
*me
;
1045 snd_mixer_selem_id_t
*sid
;
1051 SELEM_INIT(sid
, e
->alsa_name
);
1052 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1053 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1057 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1058 r
= snd_mixer_selem_set_playback_switch_all(me
, b
);
1060 r
= snd_mixer_selem_set_capture_switch_all(me
, b
);
1063 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1068 int pa_alsa_path_set_mute(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t muted
) {
1077 PA_LLIST_FOREACH(e
, p
->elements
) {
1079 if (e
->switch_use
!= PA_ALSA_SWITCH_MUTE
)
1082 if (element_set_switch(e
, m
, !muted
) < 0)
1089 /* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
1090 * function sets all channels of the volume element to e->min_volume, 0 dB or
1091 * e->constant_volume. */
1092 static int element_set_constant_volume(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1093 snd_mixer_elem_t
*me
= NULL
;
1094 snd_mixer_selem_id_t
*sid
= NULL
;
1097 pa_bool_t volume_set
= FALSE
;
1102 SELEM_INIT(sid
, e
->alsa_name
);
1103 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1104 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
1108 switch (e
->volume_use
) {
1109 case PA_ALSA_VOLUME_OFF
:
1110 volume
= e
->min_volume
;
1114 case PA_ALSA_VOLUME_ZERO
:
1118 volume
= decibel_fix_get_step(e
->db_fix
, &dB
, +1);
1123 case PA_ALSA_VOLUME_CONSTANT
:
1124 volume
= e
->constant_volume
;
1129 pa_assert_not_reached();
1133 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1134 r
= snd_mixer_selem_set_playback_volume_all(me
, volume
);
1136 r
= snd_mixer_selem_set_capture_volume_all(me
, volume
);
1138 pa_assert(e
->volume_use
== PA_ALSA_VOLUME_ZERO
);
1139 pa_assert(!e
->db_fix
);
1141 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1142 r
= snd_mixer_selem_set_playback_dB_all(me
, 0, +1);
1144 r
= snd_mixer_selem_set_capture_dB_all(me
, 0, +1);
1148 pa_log_warn("Failed to set volume of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
1153 int pa_alsa_path_select(pa_alsa_path
*p
, snd_mixer_t
*m
) {
1160 pa_log_debug("Activating path %s", p
->name
);
1161 pa_alsa_path_dump(p
);
1163 PA_LLIST_FOREACH(e
, p
->elements
) {
1165 switch (e
->switch_use
) {
1166 case PA_ALSA_SWITCH_OFF
:
1167 r
= element_set_switch(e
, m
, FALSE
);
1170 case PA_ALSA_SWITCH_ON
:
1171 r
= element_set_switch(e
, m
, TRUE
);
1174 case PA_ALSA_SWITCH_MUTE
:
1175 case PA_ALSA_SWITCH_IGNORE
:
1176 case PA_ALSA_SWITCH_SELECT
:
1184 switch (e
->volume_use
) {
1185 case PA_ALSA_VOLUME_OFF
:
1186 case PA_ALSA_VOLUME_ZERO
:
1187 case PA_ALSA_VOLUME_CONSTANT
:
1188 r
= element_set_constant_volume(e
, m
);
1191 case PA_ALSA_VOLUME_MERGE
:
1192 case PA_ALSA_VOLUME_IGNORE
:
1204 static int check_required(pa_alsa_element
*e
, snd_mixer_elem_t
*me
) {
1205 pa_bool_t has_switch
;
1206 pa_bool_t has_enumeration
;
1207 pa_bool_t has_volume
;
1212 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1214 snd_mixer_selem_has_playback_switch(me
) ||
1215 (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
));
1218 snd_mixer_selem_has_capture_switch(me
) ||
1219 (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
));
1222 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1224 snd_mixer_selem_has_playback_volume(me
) ||
1225 (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
));
1228 snd_mixer_selem_has_capture_volume(me
) ||
1229 (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
));
1232 has_enumeration
= snd_mixer_selem_is_enumerated(me
);
1234 if ((e
->required
== PA_ALSA_REQUIRED_SWITCH
&& !has_switch
) ||
1235 (e
->required
== PA_ALSA_REQUIRED_VOLUME
&& !has_volume
) ||
1236 (e
->required
== PA_ALSA_REQUIRED_ENUMERATION
&& !has_enumeration
))
1239 if (e
->required
== PA_ALSA_REQUIRED_ANY
&& !(has_switch
|| has_volume
|| has_enumeration
))
1242 if ((e
->required_absent
== PA_ALSA_REQUIRED_SWITCH
&& has_switch
) ||
1243 (e
->required_absent
== PA_ALSA_REQUIRED_VOLUME
&& has_volume
) ||
1244 (e
->required_absent
== PA_ALSA_REQUIRED_ENUMERATION
&& has_enumeration
))
1247 if (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& (has_switch
|| has_volume
|| has_enumeration
))
1250 if (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) {
1251 switch (e
->required_any
) {
1252 case PA_ALSA_REQUIRED_VOLUME
:
1253 e
->path
->req_any_present
|= (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
);
1255 case PA_ALSA_REQUIRED_SWITCH
:
1256 e
->path
->req_any_present
|= (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
);
1258 case PA_ALSA_REQUIRED_ENUMERATION
:
1259 e
->path
->req_any_present
|= (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1261 case PA_ALSA_REQUIRED_ANY
:
1262 e
->path
->req_any_present
|=
1263 (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) ||
1264 (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) ||
1265 (e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
);
1268 pa_assert_not_reached();
1272 if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1274 PA_LLIST_FOREACH(o
, e
->options
) {
1275 e
->path
->req_any_present
|= (o
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) &&
1277 if (o
->required
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
< 0)
1279 if (o
->required_absent
!= PA_ALSA_REQUIRED_IGNORE
&& o
->alsa_idx
>= 0)
1287 static int element_probe(pa_alsa_element
*e
, snd_mixer_t
*m
) {
1288 snd_mixer_selem_id_t
*sid
;
1289 snd_mixer_elem_t
*me
;
1295 SELEM_INIT(sid
, e
->alsa_name
);
1297 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
1299 if (e
->required
!= PA_ALSA_REQUIRED_IGNORE
)
1302 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1303 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1304 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1309 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
) {
1310 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1312 if (!snd_mixer_selem_has_playback_switch(me
)) {
1313 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_switch(me
))
1314 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1316 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1321 if (!snd_mixer_selem_has_capture_switch(me
)) {
1322 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_switch(me
))
1323 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1325 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1329 if (e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
)
1330 e
->direction_try_other
= FALSE
;
1333 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1335 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
1337 if (!snd_mixer_selem_has_playback_volume(me
)) {
1338 if (e
->direction_try_other
&& snd_mixer_selem_has_capture_volume(me
))
1339 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1341 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1346 if (!snd_mixer_selem_has_capture_volume(me
)) {
1347 if (e
->direction_try_other
&& snd_mixer_selem_has_playback_volume(me
))
1348 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1350 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1354 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
) {
1355 long min_dB
= 0, max_dB
= 0;
1358 e
->direction_try_other
= FALSE
;
1360 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1361 r
= snd_mixer_selem_get_playback_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1363 r
= snd_mixer_selem_get_capture_volume_range(me
, &e
->min_volume
, &e
->max_volume
);
1366 pa_log_warn("Failed to get volume range of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1370 if (e
->min_volume
>= e
->max_volume
) {
1371 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
);
1372 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1374 } else if (e
->volume_use
== PA_ALSA_VOLUME_CONSTANT
&&
1375 (e
->min_volume
> e
->constant_volume
|| e
->max_volume
< e
->constant_volume
)) {
1376 pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
1377 e
->constant_volume
, e
->alsa_name
, e
->min_volume
, e
->max_volume
);
1378 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1382 pa_channel_position_t p
;
1385 ((e
->min_volume
> e
->db_fix
->min_step
) ||
1386 (e
->max_volume
< e
->db_fix
->max_step
))) {
1387 pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
1388 "real hardware range (%li-%li). Disabling the decibel fix.", e
->alsa_name
,
1389 e
->db_fix
->min_step
, e
->db_fix
->max_step
,
1390 e
->min_volume
, e
->max_volume
);
1392 decibel_fix_free(e
->db_fix
);
1398 e
->min_volume
= e
->db_fix
->min_step
;
1399 e
->max_volume
= e
->db_fix
->max_step
;
1400 min_dB
= e
->db_fix
->db_values
[0];
1401 max_dB
= e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
];
1402 } else if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1403 e
->has_dB
= snd_mixer_selem_get_playback_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1405 e
->has_dB
= snd_mixer_selem_get_capture_dB_range(me
, &min_dB
, &max_dB
) >= 0;
1407 /* Check that the kernel driver returns consistent limits with
1408 * both _get_*_dB_range() and _ask_*_vol_dB(). */
1409 if (e
->has_dB
&& !e
->db_fix
) {
1410 long min_dB_checked
= 0;
1411 long max_dB_checked
= 0;
1413 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1414 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1416 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->min_volume
, &min_dB_checked
);
1419 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->min_volume
);
1423 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1424 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1426 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB_checked
);
1429 pa_log_warn("Failed to query the dB value for %s at volume level %li", e
->alsa_name
, e
->max_volume
);
1433 if (min_dB
!= min_dB_checked
|| max_dB
!= max_dB_checked
) {
1434 pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
1435 "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
1436 "%0.2f dB at level %li.",
1438 min_dB
/ 100.0, max_dB
/ 100.0,
1439 min_dB_checked
/ 100.0, e
->min_volume
, max_dB_checked
/ 100.0, e
->max_volume
);
1445 #ifdef HAVE_VALGRIND_MEMCHECK_H
1446 VALGRIND_MAKE_MEM_DEFINED(&min_dB
, sizeof(min_dB
));
1447 VALGRIND_MAKE_MEM_DEFINED(&max_dB
, sizeof(max_dB
));
1450 e
->min_dB
= ((double) min_dB
) / 100.0;
1451 e
->max_dB
= ((double) max_dB
) / 100.0;
1453 if (min_dB
>= max_dB
) {
1454 pa_assert(!e
->db_fix
);
1455 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
);
1460 if (e
->volume_limit
>= 0) {
1461 if (e
->volume_limit
<= e
->min_volume
|| e
->volume_limit
> e
->max_volume
)
1462 pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
1463 "%li-%li. The volume limit is ignored.",
1464 e
->alsa_name
, e
->path
->name
, e
->volume_limit
, e
->min_volume
+ 1, e
->max_volume
);
1467 e
->max_volume
= e
->volume_limit
;
1471 e
->db_fix
->max_step
= e
->max_volume
;
1472 e
->max_dB
= ((double) e
->db_fix
->db_values
[e
->db_fix
->max_step
- e
->db_fix
->min_step
]) / 100.0;
1475 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1476 r
= snd_mixer_selem_ask_playback_vol_dB(me
, e
->max_volume
, &max_dB
);
1478 r
= snd_mixer_selem_ask_capture_vol_dB(me
, e
->max_volume
, &max_dB
);
1481 pa_log_warn("Failed to get dB value of %s: %s", e
->alsa_name
, pa_alsa_strerror(r
));
1484 e
->max_dB
= ((double) max_dB
) / 100.0;
1490 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1491 is_mono
= snd_mixer_selem_is_playback_mono(me
) > 0;
1493 is_mono
= snd_mixer_selem_is_capture_mono(me
) > 0;
1498 if (!e
->override_map
) {
1499 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1500 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1503 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = 0;
1506 e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1] = PA_CHANNEL_POSITION_MASK_ALL
;
1509 e
->merged_mask
= e
->masks
[SND_MIXER_SCHN_MONO
][e
->n_channels
-1];
1512 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1514 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1517 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1518 e
->n_channels
+= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1520 e
->n_channels
+= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1523 if (e
->n_channels
<= 0) {
1524 pa_log_warn("Volume element %s with no channels?", e
->alsa_name
);
1528 if (e
->n_channels
> 2) {
1529 /* FIXME: In some places code like this is used:
1531 * e->masks[alsa_channel_ids[p]][e->n_channels-1]
1533 * The definition of e->masks is
1535 * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
1537 * Since the array size is fixed at 2, we obviously
1538 * don't support elements with more than two
1540 pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e
->alsa_name
, e
->n_channels
);
1544 if (!e
->override_map
) {
1545 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1546 pa_bool_t has_channel
;
1548 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1551 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
1552 has_channel
= snd_mixer_selem_has_playback_channel(me
, alsa_channel_ids
[p
]) > 0;
1554 has_channel
= snd_mixer_selem_has_capture_channel(me
, alsa_channel_ids
[p
]) > 0;
1556 e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1] = has_channel
? PA_CHANNEL_POSITION_MASK(p
) : 0;
1561 for (p
= PA_CHANNEL_POSITION_FRONT_LEFT
; p
< PA_CHANNEL_POSITION_MAX
; p
++) {
1562 if (alsa_channel_ids
[p
] == SND_MIXER_SCHN_UNKNOWN
)
1565 e
->merged_mask
|= e
->masks
[alsa_channel_ids
[p
]][e
->n_channels
-1];
1573 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
1576 PA_LLIST_FOREACH(o
, e
->options
)
1577 o
->alsa_idx
= pa_streq(o
->alsa_name
, "on") ? 1 : 0;
1578 } else if (e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
1582 if ((n
= snd_mixer_selem_get_enum_items(me
)) < 0) {
1583 pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n
));
1587 PA_LLIST_FOREACH(o
, e
->options
) {
1590 for (i
= 0; i
< n
; i
++) {
1593 if (snd_mixer_selem_get_enum_item_name(me
, i
, sizeof(buf
), buf
) < 0)
1596 if (!pa_streq(buf
, o
->alsa_name
))
1604 if (check_required(e
, me
) < 0)
1610 static pa_alsa_element
* element_get(pa_alsa_path
*p
, const char *section
, pa_bool_t prefixed
) {
1617 if (!pa_startswith(section
, "Element "))
1623 /* This is not an element section, but an enum section? */
1624 if (strchr(section
, ':'))
1627 if (p
->last_element
&& pa_streq(p
->last_element
->alsa_name
, section
))
1628 return p
->last_element
;
1630 PA_LLIST_FOREACH(e
, p
->elements
)
1631 if (pa_streq(e
->alsa_name
, section
))
1634 e
= pa_xnew0(pa_alsa_element
, 1);
1636 e
->alsa_name
= pa_xstrdup(section
);
1637 e
->direction
= p
->direction
;
1638 e
->volume_limit
= -1;
1640 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
1643 p
->last_element
= e
;
1647 static pa_alsa_option
* option_get(pa_alsa_path
*p
, const char *section
) {
1653 if (!pa_startswith(section
, "Option "))
1658 /* This is not an enum section, but an element section? */
1659 if (!(on
= strchr(section
, ':')))
1662 en
= pa_xstrndup(section
, on
- section
);
1665 if (p
->last_option
&&
1666 pa_streq(p
->last_option
->element
->alsa_name
, en
) &&
1667 pa_streq(p
->last_option
->alsa_name
, on
)) {
1669 return p
->last_option
;
1672 pa_assert_se(e
= element_get(p
, en
, FALSE
));
1675 PA_LLIST_FOREACH(o
, e
->options
)
1676 if (pa_streq(o
->alsa_name
, on
))
1679 o
= pa_xnew0(pa_alsa_option
, 1);
1681 o
->alsa_name
= pa_xstrdup(on
);
1684 if (p
->last_option
&& p
->last_option
->element
== e
)
1685 PA_LLIST_INSERT_AFTER(pa_alsa_option
, e
->options
, p
->last_option
, o
);
1687 PA_LLIST_PREPEND(pa_alsa_option
, e
->options
, o
);
1694 static int element_parse_switch(
1695 const char *filename
,
1697 const char *section
,
1703 pa_alsa_path
*p
= userdata
;
1708 if (!(e
= element_get(p
, section
, TRUE
))) {
1709 pa_log("[%s:%u] Switch makes no sense in '%s'", filename
, line
, section
);
1713 if (pa_streq(rvalue
, "ignore"))
1714 e
->switch_use
= PA_ALSA_SWITCH_IGNORE
;
1715 else if (pa_streq(rvalue
, "mute"))
1716 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
1717 else if (pa_streq(rvalue
, "off"))
1718 e
->switch_use
= PA_ALSA_SWITCH_OFF
;
1719 else if (pa_streq(rvalue
, "on"))
1720 e
->switch_use
= PA_ALSA_SWITCH_ON
;
1721 else if (pa_streq(rvalue
, "select"))
1722 e
->switch_use
= PA_ALSA_SWITCH_SELECT
;
1724 pa_log("[%s:%u] Switch invalid of '%s'", filename
, line
, section
);
1731 static int element_parse_volume(
1732 const char *filename
,
1734 const char *section
,
1740 pa_alsa_path
*p
= userdata
;
1745 if (!(e
= element_get(p
, section
, TRUE
))) {
1746 pa_log("[%s:%u] Volume makes no sense in '%s'", filename
, line
, section
);
1750 if (pa_streq(rvalue
, "ignore"))
1751 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
1752 else if (pa_streq(rvalue
, "merge"))
1753 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
1754 else if (pa_streq(rvalue
, "off"))
1755 e
->volume_use
= PA_ALSA_VOLUME_OFF
;
1756 else if (pa_streq(rvalue
, "zero"))
1757 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
1761 if (pa_atou(rvalue
, &constant
) >= 0) {
1762 e
->volume_use
= PA_ALSA_VOLUME_CONSTANT
;
1763 e
->constant_volume
= constant
;
1765 pa_log("[%s:%u] Volume invalid of '%s'", filename
, line
, section
);
1773 static int element_parse_enumeration(
1774 const char *filename
,
1776 const char *section
,
1782 pa_alsa_path
*p
= userdata
;
1787 if (!(e
= element_get(p
, section
, TRUE
))) {
1788 pa_log("[%s:%u] Enumeration makes no sense in '%s'", filename
, line
, section
);
1792 if (pa_streq(rvalue
, "ignore"))
1793 e
->enumeration_use
= PA_ALSA_ENUMERATION_IGNORE
;
1794 else if (pa_streq(rvalue
, "select"))
1795 e
->enumeration_use
= PA_ALSA_ENUMERATION_SELECT
;
1797 pa_log("[%s:%u] Enumeration invalid of '%s'", filename
, line
, section
);
1804 static int option_parse_priority(
1805 const char *filename
,
1807 const char *section
,
1813 pa_alsa_path
*p
= userdata
;
1819 if (!(o
= option_get(p
, section
))) {
1820 pa_log("[%s:%u] Priority makes no sense in '%s'", filename
, line
, section
);
1824 if (pa_atou(rvalue
, &prio
) < 0) {
1825 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
1833 static int option_parse_name(
1834 const char *filename
,
1836 const char *section
,
1842 pa_alsa_path
*p
= userdata
;
1847 if (!(o
= option_get(p
, section
))) {
1848 pa_log("[%s:%u] Name makes no sense in '%s'", filename
, line
, section
);
1853 o
->name
= pa_xstrdup(rvalue
);
1858 static int element_parse_required(
1859 const char *filename
,
1861 const char *section
,
1867 pa_alsa_path
*p
= userdata
;
1870 pa_alsa_required_t req
;
1874 e
= element_get(p
, section
, TRUE
);
1875 o
= option_get(p
, section
);
1877 pa_log("[%s:%u] Required makes no sense in '%s'", filename
, line
, section
);
1881 if (pa_streq(rvalue
, "ignore"))
1882 req
= PA_ALSA_REQUIRED_IGNORE
;
1883 else if (pa_streq(rvalue
, "switch") && e
)
1884 req
= PA_ALSA_REQUIRED_SWITCH
;
1885 else if (pa_streq(rvalue
, "volume") && e
)
1886 req
= PA_ALSA_REQUIRED_VOLUME
;
1887 else if (pa_streq(rvalue
, "enumeration"))
1888 req
= PA_ALSA_REQUIRED_ENUMERATION
;
1889 else if (pa_streq(rvalue
, "any"))
1890 req
= PA_ALSA_REQUIRED_ANY
;
1892 pa_log("[%s:%u] Required invalid of '%s'", filename
, line
, section
);
1896 if (pa_streq(lvalue
, "required-absent")) {
1898 e
->required_absent
= req
;
1900 o
->required_absent
= req
;
1902 else if (pa_streq(lvalue
, "required-any")) {
1904 e
->required_any
= req
;
1905 e
->path
->has_req_any
= TRUE
;
1908 o
->required_any
= req
;
1909 o
->element
->path
->has_req_any
= TRUE
;
1922 static int element_parse_direction(
1923 const char *filename
,
1925 const char *section
,
1931 pa_alsa_path
*p
= userdata
;
1936 if (!(e
= element_get(p
, section
, TRUE
))) {
1937 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1941 if (pa_streq(rvalue
, "playback"))
1942 e
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
1943 else if (pa_streq(rvalue
, "capture"))
1944 e
->direction
= PA_ALSA_DIRECTION_INPUT
;
1946 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1953 static int element_parse_direction_try_other(
1954 const char *filename
,
1956 const char *section
,
1962 pa_alsa_path
*p
= userdata
;
1966 if (!(e
= element_get(p
, section
, TRUE
))) {
1967 pa_log("[%s:%u] Direction makes no sense in '%s'", filename
, line
, section
);
1971 if ((yes
= pa_parse_boolean(rvalue
)) < 0) {
1972 pa_log("[%s:%u] Direction invalid of '%s'", filename
, line
, section
);
1976 e
->direction_try_other
= !!yes
;
1980 static int element_parse_volume_limit(
1981 const char *filename
,
1983 const char *section
,
1989 pa_alsa_path
*p
= userdata
;
1993 if (!(e
= element_get(p
, section
, TRUE
))) {
1994 pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename
, line
, section
);
1998 if (pa_atol(rvalue
, &volume_limit
) < 0 || volume_limit
< 0) {
1999 pa_log("[%s:%u] Invalid value for volume-limit", filename
, line
);
2003 e
->volume_limit
= volume_limit
;
2007 static pa_channel_position_mask_t
parse_mask(const char *m
) {
2008 pa_channel_position_mask_t v
;
2010 if (pa_streq(m
, "all-left"))
2011 v
= PA_CHANNEL_POSITION_MASK_LEFT
;
2012 else if (pa_streq(m
, "all-right"))
2013 v
= PA_CHANNEL_POSITION_MASK_RIGHT
;
2014 else if (pa_streq(m
, "all-center"))
2015 v
= PA_CHANNEL_POSITION_MASK_CENTER
;
2016 else if (pa_streq(m
, "all-front"))
2017 v
= PA_CHANNEL_POSITION_MASK_FRONT
;
2018 else if (pa_streq(m
, "all-rear"))
2019 v
= PA_CHANNEL_POSITION_MASK_REAR
;
2020 else if (pa_streq(m
, "all-side"))
2021 v
= PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER
;
2022 else if (pa_streq(m
, "all-top"))
2023 v
= PA_CHANNEL_POSITION_MASK_TOP
;
2024 else if (pa_streq(m
, "all-no-lfe"))
2025 v
= PA_CHANNEL_POSITION_MASK_ALL
^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE
);
2026 else if (pa_streq(m
, "all"))
2027 v
= PA_CHANNEL_POSITION_MASK_ALL
;
2029 pa_channel_position_t p
;
2031 if ((p
= pa_channel_position_from_string(m
)) == PA_CHANNEL_POSITION_INVALID
)
2034 v
= PA_CHANNEL_POSITION_MASK(p
);
2040 static int element_parse_override_map(
2041 const char *filename
,
2043 const char *section
,
2049 pa_alsa_path
*p
= userdata
;
2051 const char *state
= NULL
;
2055 if (!(e
= element_get(p
, section
, TRUE
))) {
2056 pa_log("[%s:%u] Override map makes no sense in '%s'", filename
, line
, section
);
2060 while ((n
= pa_split(rvalue
, ",", &state
))) {
2061 pa_channel_position_mask_t m
;
2066 if ((m
= parse_mask(n
)) == 0) {
2067 pa_log("[%s:%u] Override map '%s' invalid in '%s'", filename
, line
, n
, section
);
2073 if (pa_streq(lvalue
, "override-map.1"))
2074 e
->masks
[i
++][0] = m
;
2076 e
->masks
[i
++][1] = m
;
2078 /* Later on we might add override-map.3 and so on here ... */
2083 e
->override_map
= TRUE
;
2088 static int element_set_option(pa_alsa_element
*e
, snd_mixer_t
*m
, int alsa_idx
) {
2089 snd_mixer_selem_id_t
*sid
;
2090 snd_mixer_elem_t
*me
;
2096 SELEM_INIT(sid
, e
->alsa_name
);
2097 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2098 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2102 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
) {
2104 if (e
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
2105 r
= snd_mixer_selem_set_playback_switch_all(me
, alsa_idx
);
2107 r
= snd_mixer_selem_set_capture_switch_all(me
, alsa_idx
);
2110 pa_log_warn("Failed to set switch of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2113 pa_assert(e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
);
2115 if ((r
= snd_mixer_selem_set_enum_item(me
, 0, alsa_idx
)) < 0)
2116 pa_log_warn("Failed to set enumeration of %s: %s", e
->alsa_name
, pa_alsa_strerror(errno
));
2122 int pa_alsa_setting_select(pa_alsa_setting
*s
, snd_mixer_t
*m
) {
2129 PA_IDXSET_FOREACH(o
, s
->options
, idx
)
2130 element_set_option(o
->element
, m
, o
->alsa_idx
);
2135 static int option_verify(pa_alsa_option
*o
) {
2136 static const struct description_map well_known_descriptions
[] = {
2137 { "input", N_("Input") },
2138 { "input-docking", N_("Docking Station Input") },
2139 { "input-docking-microphone", N_("Docking Station Microphone") },
2140 { "input-docking-linein", N_("Docking Station Line-In") },
2141 { "input-linein", N_("Line-In") },
2142 { "input-microphone", N_("Microphone") },
2143 { "input-microphone-front", N_("Front Microphone") },
2144 { "input-microphone-rear", N_("Rear Microphone") },
2145 { "input-microphone-external", N_("External Microphone") },
2146 { "input-microphone-internal", N_("Internal Microphone") },
2147 { "input-radio", N_("Radio") },
2148 { "input-video", N_("Video") },
2149 { "input-agc-on", N_("Automatic Gain Control") },
2150 { "input-agc-off", N_("No Automatic Gain Control") },
2151 { "input-boost-on", N_("Boost") },
2152 { "input-boost-off", N_("No Boost") },
2153 { "output-amplifier-on", N_("Amplifier") },
2154 { "output-amplifier-off", N_("No Amplifier") },
2155 { "output-bass-boost-on", N_("Bass Boost") },
2156 { "output-bass-boost-off", N_("No Bass Boost") },
2157 { "output-speaker", N_("Speaker") },
2158 { "output-headphones", N_("Headphones") }
2164 pa_log("No name set for option %s", o
->alsa_name
);
2168 if (o
->element
->enumeration_use
!= PA_ALSA_ENUMERATION_SELECT
&&
2169 o
->element
->switch_use
!= PA_ALSA_SWITCH_SELECT
) {
2170 pa_log("Element %s of option %s not set for select.", o
->element
->alsa_name
, o
->name
);
2174 if (o
->element
->switch_use
== PA_ALSA_SWITCH_SELECT
&&
2175 !pa_streq(o
->alsa_name
, "on") &&
2176 !pa_streq(o
->alsa_name
, "off")) {
2177 pa_log("Switch %s options need be named off or on ", o
->element
->alsa_name
);
2181 if (!o
->description
)
2182 o
->description
= pa_xstrdup(lookup_description(o
->name
,
2183 well_known_descriptions
,
2184 PA_ELEMENTSOF(well_known_descriptions
)));
2185 if (!o
->description
)
2186 o
->description
= pa_xstrdup(o
->name
);
2191 static int element_verify(pa_alsa_element
*e
) {
2196 // 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);
2197 if ((e
->required
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required
== e
->required_absent
) ||
2198 (e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
&& e
->required_any
== e
->required_absent
) ||
2199 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required_any
!= PA_ALSA_REQUIRED_IGNORE
) ||
2200 (e
->required_absent
== PA_ALSA_REQUIRED_ANY
&& e
->required
!= PA_ALSA_REQUIRED_IGNORE
)) {
2201 pa_log("Element %s cannot be required and absent at the same time.", e
->alsa_name
);
2205 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
&& e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
) {
2206 pa_log("Element %s cannot set select for both switch and enumeration.", e
->alsa_name
);
2210 PA_LLIST_FOREACH(o
, e
->options
)
2211 if (option_verify(o
) < 0)
2217 static int path_verify(pa_alsa_path
*p
) {
2218 static const struct description_map well_known_descriptions
[] = {
2219 { "analog-input", N_("Analog Input") },
2220 { "analog-input-microphone", N_("Analog Microphone") },
2221 { "analog-input-microphone-front", N_("Front Microphone") },
2222 { "analog-input-microphone-rear", N_("Rear Microphone") },
2223 { "analog-input-microphone-dock", N_("Docking Station Microphone") },
2224 { "analog-input-microphone-internal", N_("Internal Microphone") },
2225 { "analog-input-linein", N_("Analog Line-In") },
2226 { "analog-input-radio", N_("Analog Radio") },
2227 { "analog-input-video", N_("Analog Video") },
2228 { "analog-output", N_("Analog Output") },
2229 { "analog-output-headphones", N_("Analog Headphones") },
2230 { "analog-output-lfe-on-mono", N_("Analog Output (LFE)") },
2231 { "analog-output-mono", N_("Analog Mono Output") },
2232 { "analog-output-speaker", N_("Analog Speakers") },
2233 { "iec958-stereo-output", N_("Digital Output (IEC958)") },
2234 { "iec958-passthrough-output", N_("Digital Passthrough (IEC958)") }
2241 PA_LLIST_FOREACH(e
, p
->elements
)
2242 if (element_verify(e
) < 0)
2245 if (!p
->description
)
2246 p
->description
= pa_xstrdup(lookup_description(p
->name
,
2247 well_known_descriptions
,
2248 PA_ELEMENTSOF(well_known_descriptions
)));
2250 if (!p
->description
)
2251 p
->description
= pa_xstrdup(p
->name
);
2256 pa_alsa_path
* pa_alsa_path_new(const char *fname
, pa_alsa_direction_t direction
) {
2262 pa_config_item items
[] = {
2264 { "priority", pa_config_parse_unsigned
, NULL
, "General" },
2265 { "description", pa_config_parse_string
, NULL
, "General" },
2266 { "name", pa_config_parse_string
, NULL
, "General" },
2269 { "priority", option_parse_priority
, NULL
, NULL
},
2270 { "name", option_parse_name
, NULL
, NULL
},
2273 { "switch", element_parse_switch
, NULL
, NULL
},
2274 { "volume", element_parse_volume
, NULL
, NULL
},
2275 { "enumeration", element_parse_enumeration
, NULL
, NULL
},
2276 { "override-map.1", element_parse_override_map
, NULL
, NULL
},
2277 { "override-map.2", element_parse_override_map
, NULL
, NULL
},
2278 /* ... later on we might add override-map.3 and so on here ... */
2279 { "required", element_parse_required
, NULL
, NULL
},
2280 { "required-any", element_parse_required
, NULL
, NULL
},
2281 { "required-absent", element_parse_required
, NULL
, NULL
},
2282 { "direction", element_parse_direction
, NULL
, NULL
},
2283 { "direction-try-other", element_parse_direction_try_other
, NULL
, NULL
},
2284 { "volume-limit", element_parse_volume_limit
, NULL
, NULL
},
2285 { NULL
, NULL
, NULL
, NULL
}
2290 p
= pa_xnew0(pa_alsa_path
, 1);
2291 n
= pa_path_get_filename(fname
);
2292 p
->name
= pa_xstrndup(n
, strcspn(n
, "."));
2293 p
->direction
= direction
;
2295 items
[0].data
= &p
->priority
;
2296 items
[1].data
= &p
->description
;
2297 items
[2].data
= &p
->name
;
2299 fn
= pa_maybe_prefix_path(fname
,
2300 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/paths/" :
2303 r
= pa_config_parse(fn
, NULL
, items
, p
);
2309 if (path_verify(p
) < 0)
2315 pa_alsa_path_free(p
);
2319 pa_alsa_path
*pa_alsa_path_synthesize(const char *element
, pa_alsa_direction_t direction
) {
2325 p
= pa_xnew0(pa_alsa_path
, 1);
2326 p
->name
= pa_xstrdup(element
);
2327 p
->direction
= direction
;
2329 e
= pa_xnew0(pa_alsa_element
, 1);
2331 e
->alsa_name
= pa_xstrdup(element
);
2332 e
->direction
= direction
;
2333 e
->volume_limit
= -1;
2335 e
->switch_use
= PA_ALSA_SWITCH_MUTE
;
2336 e
->volume_use
= PA_ALSA_VOLUME_MERGE
;
2338 PA_LLIST_PREPEND(pa_alsa_element
, p
->elements
, e
);
2339 p
->last_element
= e
;
2343 static pa_bool_t
element_drop_unsupported(pa_alsa_element
*e
) {
2344 pa_alsa_option
*o
, *n
;
2348 for (o
= e
->options
; o
; o
= n
) {
2351 if (o
->alsa_idx
< 0) {
2352 PA_LLIST_REMOVE(pa_alsa_option
, e
->options
, o
);
2358 e
->switch_use
!= PA_ALSA_SWITCH_IGNORE
||
2359 e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
||
2360 e
->enumeration_use
!= PA_ALSA_ENUMERATION_IGNORE
;
2363 static void path_drop_unsupported(pa_alsa_path
*p
) {
2364 pa_alsa_element
*e
, *n
;
2368 for (e
= p
->elements
; e
; e
= n
) {
2371 if (!element_drop_unsupported(e
)) {
2372 PA_LLIST_REMOVE(pa_alsa_element
, p
->elements
, e
);
2378 static void path_make_options_unique(pa_alsa_path
*p
) {
2380 pa_alsa_option
*o
, *u
;
2382 PA_LLIST_FOREACH(e
, p
->elements
) {
2383 PA_LLIST_FOREACH(o
, e
->options
) {
2387 for (u
= o
->next
; u
; u
= u
->next
)
2388 if (pa_streq(u
->name
, o
->name
))
2394 m
= pa_xstrdup(o
->name
);
2396 /* OK, this name is not unique, hence let's rename */
2397 for (i
= 1, u
= o
; u
; u
= u
->next
) {
2400 if (!pa_streq(u
->name
, m
))
2403 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2407 nd
= pa_sprintf_malloc("%s %u", u
->description
, i
);
2408 pa_xfree(u
->description
);
2409 u
->description
= nd
;
2419 static pa_bool_t
element_create_settings(pa_alsa_element
*e
, pa_alsa_setting
*template) {
2422 for (; e
; e
= e
->next
)
2423 if (e
->switch_use
== PA_ALSA_SWITCH_SELECT
||
2424 e
->enumeration_use
== PA_ALSA_ENUMERATION_SELECT
)
2430 for (o
= e
->options
; o
; o
= o
->next
) {
2434 s
= pa_xnewdup(pa_alsa_setting
, template, 1);
2435 s
->options
= pa_idxset_copy(template->options
);
2436 s
->name
= pa_sprintf_malloc(_("%s+%s"), template->name
, o
->name
);
2438 (template->description
[0] && o
->description
[0])
2439 ? pa_sprintf_malloc(_("%s / %s"), template->description
, o
->description
)
2440 : (template->description
[0]
2441 ? pa_xstrdup(template->description
)
2442 : pa_xstrdup(o
->description
));
2444 s
->priority
= PA_MAX(template->priority
, o
->priority
);
2446 s
= pa_xnew0(pa_alsa_setting
, 1);
2447 s
->options
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
2448 s
->name
= pa_xstrdup(o
->name
);
2449 s
->description
= pa_xstrdup(o
->description
);
2450 s
->priority
= o
->priority
;
2453 pa_idxset_put(s
->options
, o
, NULL
);
2455 if (element_create_settings(e
->next
, s
))
2456 /* This is not a leaf, so let's get rid of it */
2459 /* This is a leaf, so let's add it */
2460 PA_LLIST_INSERT_AFTER(pa_alsa_setting
, e
->path
->settings
, e
->path
->last_setting
, s
);
2462 e
->path
->last_setting
= s
;
2469 static void path_create_settings(pa_alsa_path
*p
) {
2472 element_create_settings(p
->elements
, NULL
);
2475 int pa_alsa_path_probe(pa_alsa_path
*p
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2477 double min_dB
[PA_CHANNEL_POSITION_MAX
], max_dB
[PA_CHANNEL_POSITION_MAX
];
2478 pa_channel_position_t t
;
2479 pa_channel_position_mask_t path_volume_channels
= 0;
2490 pa_log_debug("Probing path '%s'", p
->name
);
2492 PA_LLIST_FOREACH(e
, p
->elements
) {
2493 if (element_probe(e
, m
) < 0) {
2494 p
->supported
= FALSE
;
2495 pa_log_debug("Probe of element '%s' failed.", e
->alsa_name
);
2498 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
);
2503 if (e
->volume_use
== PA_ALSA_VOLUME_MERGE
) {
2505 if (!p
->has_volume
) {
2506 p
->min_volume
= e
->min_volume
;
2507 p
->max_volume
= e
->max_volume
;
2511 if (!p
->has_volume
) {
2512 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2513 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2514 min_dB
[t
] = e
->min_dB
;
2515 max_dB
[t
] = e
->max_dB
;
2516 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2523 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++)
2524 if (PA_CHANNEL_POSITION_MASK(t
) & e
->merged_mask
) {
2525 min_dB
[t
] += e
->min_dB
;
2526 max_dB
[t
] += e
->max_dB
;
2527 path_volume_channels
|= PA_CHANNEL_POSITION_MASK(t
);
2530 /* Hmm, there's another element before us
2531 * which cannot do dB volumes, so we we need
2532 * to 'neutralize' this slider */
2533 e
->volume_use
= PA_ALSA_VOLUME_ZERO
;
2534 pa_log_info("Zeroing volume of '%s' on path '%s'", e
->alsa_name
, p
->name
);
2537 } else if (p
->has_volume
) {
2538 /* We can't use this volume, so let's ignore it */
2539 e
->volume_use
= PA_ALSA_VOLUME_IGNORE
;
2540 pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e
->alsa_name
, p
->name
);
2542 p
->has_volume
= TRUE
;
2545 if (e
->switch_use
== PA_ALSA_SWITCH_MUTE
)
2549 if (p
->has_req_any
&& !p
->req_any_present
) {
2550 p
->supported
= FALSE
;
2551 pa_log_debug("Skipping path '%s', none of required-any elements preset.", p
->name
);
2555 path_drop_unsupported(p
);
2556 path_make_options_unique(p
);
2557 path_create_settings(p
);
2559 p
->supported
= TRUE
;
2562 p
->min_dB
= INFINITY
;
2563 p
->max_dB
= -INFINITY
;
2565 for (t
= 0; t
< PA_CHANNEL_POSITION_MAX
; t
++) {
2566 if (path_volume_channels
& PA_CHANNEL_POSITION_MASK(t
)) {
2567 if (p
->min_dB
> min_dB
[t
])
2568 p
->min_dB
= min_dB
[t
];
2570 if (p
->max_dB
< max_dB
[t
])
2571 p
->max_dB
= max_dB
[t
];
2578 void pa_alsa_setting_dump(pa_alsa_setting
*s
) {
2581 pa_log_debug("Setting %s (%s) priority=%u",
2583 pa_strnull(s
->description
),
2587 void pa_alsa_option_dump(pa_alsa_option
*o
) {
2590 pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
2592 pa_strnull(o
->name
),
2593 pa_strnull(o
->description
),
2598 void pa_alsa_element_dump(pa_alsa_element
*e
) {
2602 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",
2612 (long long unsigned) e
->merged_mask
,
2614 pa_yes_no(e
->override_map
));
2616 PA_LLIST_FOREACH(o
, e
->options
)
2617 pa_alsa_option_dump(o
);
2620 void pa_alsa_path_dump(pa_alsa_path
*p
) {
2625 pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
2626 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
2628 pa_strnull(p
->description
),
2631 pa_yes_no(p
->probed
),
2632 pa_yes_no(p
->supported
),
2633 pa_yes_no(p
->has_mute
),
2634 pa_yes_no(p
->has_volume
),
2635 pa_yes_no(p
->has_dB
),
2636 p
->min_volume
, p
->max_volume
,
2637 p
->min_dB
, p
->max_dB
);
2639 PA_LLIST_FOREACH(e
, p
->elements
)
2640 pa_alsa_element_dump(e
);
2642 PA_LLIST_FOREACH(s
, p
->settings
)
2643 pa_alsa_setting_dump(s
);
2646 static void element_set_callback(pa_alsa_element
*e
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2647 snd_mixer_selem_id_t
*sid
;
2648 snd_mixer_elem_t
*me
;
2654 SELEM_INIT(sid
, e
->alsa_name
);
2655 if (!(me
= snd_mixer_find_selem(m
, sid
))) {
2656 pa_log_warn("Element %s seems to have disappeared.", e
->alsa_name
);
2660 snd_mixer_elem_set_callback(me
, cb
);
2661 snd_mixer_elem_set_callback_private(me
, userdata
);
2664 void pa_alsa_path_set_callback(pa_alsa_path
*p
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2671 PA_LLIST_FOREACH(e
, p
->elements
)
2672 element_set_callback(e
, m
, cb
, userdata
);
2675 void pa_alsa_path_set_set_callback(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, snd_mixer_elem_callback_t cb
, void *userdata
) {
2682 PA_LLIST_FOREACH(p
, ps
->paths
)
2683 pa_alsa_path_set_callback(p
, m
, cb
, userdata
);
2686 pa_alsa_path_set
*pa_alsa_path_set_new(pa_alsa_mapping
*m
, pa_alsa_direction_t direction
) {
2687 pa_alsa_path_set
*ps
;
2688 char **pn
= NULL
, **en
= NULL
, **ie
;
2689 pa_alsa_decibel_fix
*db_fix
;
2693 pa_assert(m
->profile_set
);
2694 pa_assert(m
->profile_set
->decibel_fixes
);
2695 pa_assert(direction
== PA_ALSA_DIRECTION_OUTPUT
|| direction
== PA_ALSA_DIRECTION_INPUT
);
2697 if (m
->direction
!= PA_ALSA_DIRECTION_ANY
&& m
->direction
!= direction
)
2700 ps
= pa_xnew0(pa_alsa_path_set
, 1);
2701 ps
->direction
= direction
;
2703 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2704 pn
= m
->output_path_names
;
2705 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2706 pn
= m
->input_path_names
;
2711 for (in
= pn
; *in
; in
++) {
2713 pa_bool_t duplicate
= FALSE
;
2716 for (kn
= pn
; kn
< in
; kn
++)
2717 if (pa_streq(*kn
, *in
)) {
2725 fn
= pa_sprintf_malloc("%s.conf", *in
);
2727 if ((p
= pa_alsa_path_new(fn
, direction
))) {
2729 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2739 if (direction
== PA_ALSA_DIRECTION_OUTPUT
)
2740 en
= m
->output_element
;
2741 else if (direction
== PA_ALSA_DIRECTION_INPUT
)
2742 en
= m
->input_element
;
2745 pa_alsa_path_set_free(ps
);
2749 for (ie
= en
; *ie
; ie
++) {
2753 p
= pa_alsa_path_synthesize(*ie
, direction
);
2756 /* Mark all other passed elements for require-absent */
2757 for (je
= en
; *je
; je
++) {
2763 e
= pa_xnew0(pa_alsa_element
, 1);
2765 e
->alsa_name
= pa_xstrdup(*je
);
2766 e
->direction
= direction
;
2767 e
->required_absent
= PA_ALSA_REQUIRED_ANY
;
2768 e
->volume_limit
= -1;
2770 PA_LLIST_INSERT_AFTER(pa_alsa_element
, p
->elements
, p
->last_element
, e
);
2771 p
->last_element
= e
;
2774 PA_LLIST_INSERT_AFTER(pa_alsa_path
, ps
->paths
, ps
->last_path
, p
);
2779 /* Assign decibel fixes to elements. */
2780 PA_HASHMAP_FOREACH(db_fix
, m
->profile_set
->decibel_fixes
, state
) {
2783 PA_LLIST_FOREACH(p
, ps
->paths
) {
2786 PA_LLIST_FOREACH(e
, p
->elements
) {
2787 if (e
->volume_use
!= PA_ALSA_VOLUME_IGNORE
&& pa_streq(db_fix
->name
, e
->alsa_name
)) {
2788 /* The profile set that contains the dB fix may be freed
2789 * before the element, so we have to copy the dB fix
2791 e
->db_fix
= pa_xnewdup(pa_alsa_decibel_fix
, db_fix
, 1);
2792 e
->db_fix
->profile_set
= NULL
;
2793 e
->db_fix
->name
= pa_xstrdup(db_fix
->name
);
2794 e
->db_fix
->db_values
= pa_xmemdup(db_fix
->db_values
, (db_fix
->max_step
- db_fix
->min_step
+ 1) * sizeof(long));
2803 void pa_alsa_path_set_dump(pa_alsa_path_set
*ps
) {
2807 pa_log_debug("Path Set %p, direction=%i, probed=%s",
2810 pa_yes_no(ps
->probed
));
2812 PA_LLIST_FOREACH(p
, ps
->paths
)
2813 pa_alsa_path_dump(p
);
2816 static void path_set_unify(pa_alsa_path_set
*ps
) {
2818 pa_bool_t has_dB
= TRUE
, has_volume
= TRUE
, has_mute
= TRUE
;
2821 /* We have issues dealing with paths that vary too wildly. That
2822 * means for now we have to have all paths support volume/mute/dB
2825 PA_LLIST_FOREACH(p
, ps
->paths
) {
2826 pa_assert(p
->probed
);
2830 else if (!p
->has_dB
)
2837 if (!has_volume
|| !has_dB
|| !has_mute
) {
2840 pa_log_debug("Some paths of the device lack hardware volume control, disabling hardware control altogether.");
2842 pa_log_debug("Some paths of the device lack dB information, disabling dB logic altogether.");
2845 pa_log_debug("Some paths of the device lack hardware mute control, disabling hardware control altogether.");
2847 PA_LLIST_FOREACH(p
, ps
->paths
) {
2849 p
->has_volume
= FALSE
;
2854 p
->has_mute
= FALSE
;
2859 static void path_set_make_paths_unique(pa_alsa_path_set
*ps
) {
2860 pa_alsa_path
*p
, *q
;
2862 PA_LLIST_FOREACH(p
, ps
->paths
) {
2866 for (q
= p
->next
; q
; q
= q
->next
)
2867 if (pa_streq(q
->name
, p
->name
))
2873 m
= pa_xstrdup(p
->name
);
2875 /* OK, this name is not unique, hence let's rename */
2876 for (i
= 1, q
= p
; q
; q
= q
->next
) {
2879 if (!pa_streq(q
->name
, m
))
2882 nn
= pa_sprintf_malloc("%s-%u", m
, i
);
2886 nd
= pa_sprintf_malloc("%s %u", q
->description
, i
);
2887 pa_xfree(q
->description
);
2888 q
->description
= nd
;
2897 void pa_alsa_path_set_probe(pa_alsa_path_set
*ps
, snd_mixer_t
*m
, pa_bool_t ignore_dB
) {
2898 pa_alsa_path
*p
, *n
;
2905 for (p
= ps
->paths
; p
; p
= n
) {
2908 if (pa_alsa_path_probe(p
, m
, ignore_dB
) < 0) {
2909 PA_LLIST_REMOVE(pa_alsa_path
, ps
->paths
, p
);
2910 pa_alsa_path_free(p
);
2915 path_set_make_paths_unique(ps
);
2919 static void mapping_free(pa_alsa_mapping
*m
) {
2923 pa_xfree(m
->description
);
2925 pa_xstrfreev(m
->device_strings
);
2926 pa_xstrfreev(m
->input_path_names
);
2927 pa_xstrfreev(m
->output_path_names
);
2928 pa_xstrfreev(m
->input_element
);
2929 pa_xstrfreev(m
->output_element
);
2931 pa_assert(!m
->input_pcm
);
2932 pa_assert(!m
->output_pcm
);
2937 static void profile_free(pa_alsa_profile
*p
) {
2941 pa_xfree(p
->description
);
2943 pa_xstrfreev(p
->input_mapping_names
);
2944 pa_xstrfreev(p
->output_mapping_names
);
2946 if (p
->input_mappings
)
2947 pa_idxset_free(p
->input_mappings
, NULL
, NULL
);
2949 if (p
->output_mappings
)
2950 pa_idxset_free(p
->output_mappings
, NULL
, NULL
);
2955 void pa_alsa_profile_set_free(pa_alsa_profile_set
*ps
) {
2961 while ((p
= pa_hashmap_steal_first(ps
->profiles
)))
2964 pa_hashmap_free(ps
->profiles
, NULL
, NULL
);
2970 while ((m
= pa_hashmap_steal_first(ps
->mappings
)))
2973 pa_hashmap_free(ps
->mappings
, NULL
, NULL
);
2976 if (ps
->decibel_fixes
) {
2977 pa_alsa_decibel_fix
*db_fix
;
2979 while ((db_fix
= pa_hashmap_steal_first(ps
->decibel_fixes
)))
2980 decibel_fix_free(db_fix
);
2982 pa_hashmap_free(ps
->decibel_fixes
, NULL
, NULL
);
2988 static pa_alsa_mapping
*mapping_get(pa_alsa_profile_set
*ps
, const char *name
) {
2991 if (!pa_startswith(name
, "Mapping "))
2996 if ((m
= pa_hashmap_get(ps
->mappings
, name
)))
2999 m
= pa_xnew0(pa_alsa_mapping
, 1);
3000 m
->profile_set
= ps
;
3001 m
->name
= pa_xstrdup(name
);
3002 pa_channel_map_init(&m
->channel_map
);
3004 pa_hashmap_put(ps
->mappings
, m
->name
, m
);
3009 static pa_alsa_profile
*profile_get(pa_alsa_profile_set
*ps
, const char *name
) {
3012 if (!pa_startswith(name
, "Profile "))
3017 if ((p
= pa_hashmap_get(ps
->profiles
, name
)))
3020 p
= pa_xnew0(pa_alsa_profile
, 1);
3021 p
->profile_set
= ps
;
3022 p
->name
= pa_xstrdup(name
);
3024 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3029 static pa_alsa_decibel_fix
*decibel_fix_get(pa_alsa_profile_set
*ps
, const char *name
) {
3030 pa_alsa_decibel_fix
*db_fix
;
3032 if (!pa_startswith(name
, "DecibelFix "))
3037 if ((db_fix
= pa_hashmap_get(ps
->decibel_fixes
, name
)))
3040 db_fix
= pa_xnew0(pa_alsa_decibel_fix
, 1);
3041 db_fix
->profile_set
= ps
;
3042 db_fix
->name
= pa_xstrdup(name
);
3044 pa_hashmap_put(ps
->decibel_fixes
, db_fix
->name
, db_fix
);
3049 static int mapping_parse_device_strings(
3050 const char *filename
,
3052 const char *section
,
3058 pa_alsa_profile_set
*ps
= userdata
;
3063 if (!(m
= mapping_get(ps
, section
))) {
3064 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3068 pa_xstrfreev(m
->device_strings
);
3069 if (!(m
->device_strings
= pa_split_spaces_strv(rvalue
))) {
3070 pa_log("[%s:%u] Device string list empty of '%s'", filename
, line
, section
);
3077 static int mapping_parse_channel_map(
3078 const char *filename
,
3080 const char *section
,
3086 pa_alsa_profile_set
*ps
= userdata
;
3091 if (!(m
= mapping_get(ps
, section
))) {
3092 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3096 if (!(pa_channel_map_parse(&m
->channel_map
, rvalue
))) {
3097 pa_log("[%s:%u] Channel map invalid of '%s'", filename
, line
, section
);
3104 static int mapping_parse_paths(
3105 const char *filename
,
3107 const char *section
,
3113 pa_alsa_profile_set
*ps
= userdata
;
3118 if (!(m
= mapping_get(ps
, section
))) {
3119 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3123 if (pa_streq(lvalue
, "paths-input")) {
3124 pa_xstrfreev(m
->input_path_names
);
3125 m
->input_path_names
= pa_split_spaces_strv(rvalue
);
3127 pa_xstrfreev(m
->output_path_names
);
3128 m
->output_path_names
= pa_split_spaces_strv(rvalue
);
3134 static int mapping_parse_element(
3135 const char *filename
,
3137 const char *section
,
3143 pa_alsa_profile_set
*ps
= userdata
;
3148 if (!(m
= mapping_get(ps
, section
))) {
3149 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3153 if (pa_streq(lvalue
, "element-input")) {
3154 pa_xstrfreev(m
->input_element
);
3155 m
->input_element
= pa_split_spaces_strv(rvalue
);
3157 pa_xstrfreev(m
->output_element
);
3158 m
->output_element
= pa_split_spaces_strv(rvalue
);
3164 static int mapping_parse_direction(
3165 const char *filename
,
3167 const char *section
,
3173 pa_alsa_profile_set
*ps
= userdata
;
3178 if (!(m
= mapping_get(ps
, section
))) {
3179 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3183 if (pa_streq(rvalue
, "input"))
3184 m
->direction
= PA_ALSA_DIRECTION_INPUT
;
3185 else if (pa_streq(rvalue
, "output"))
3186 m
->direction
= PA_ALSA_DIRECTION_OUTPUT
;
3187 else if (pa_streq(rvalue
, "any"))
3188 m
->direction
= PA_ALSA_DIRECTION_ANY
;
3190 pa_log("[%s:%u] Direction %s invalid.", filename
, line
, rvalue
);
3197 static int mapping_parse_description(
3198 const char *filename
,
3200 const char *section
,
3206 pa_alsa_profile_set
*ps
= userdata
;
3212 if ((m
= mapping_get(ps
, section
))) {
3213 pa_xfree(m
->description
);
3214 m
->description
= pa_xstrdup(rvalue
);
3215 } else if ((p
= profile_get(ps
, section
))) {
3216 pa_xfree(p
->description
);
3217 p
->description
= pa_xstrdup(rvalue
);
3219 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3226 static int mapping_parse_priority(
3227 const char *filename
,
3229 const char *section
,
3235 pa_alsa_profile_set
*ps
= userdata
;
3242 if (pa_atou(rvalue
, &prio
) < 0) {
3243 pa_log("[%s:%u] Priority invalid of '%s'", filename
, line
, section
);
3247 if ((m
= mapping_get(ps
, section
)))
3249 else if ((p
= profile_get(ps
, section
)))
3252 pa_log("[%s:%u] Section name %s invalid.", filename
, line
, section
);
3259 static int profile_parse_mappings(
3260 const char *filename
,
3262 const char *section
,
3268 pa_alsa_profile_set
*ps
= userdata
;
3273 if (!(p
= profile_get(ps
, section
))) {
3274 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3278 if (pa_streq(lvalue
, "input-mappings")) {
3279 pa_xstrfreev(p
->input_mapping_names
);
3280 p
->input_mapping_names
= pa_split_spaces_strv(rvalue
);
3282 pa_xstrfreev(p
->output_mapping_names
);
3283 p
->output_mapping_names
= pa_split_spaces_strv(rvalue
);
3289 static int profile_parse_skip_probe(
3290 const char *filename
,
3292 const char *section
,
3298 pa_alsa_profile_set
*ps
= userdata
;
3304 if (!(p
= profile_get(ps
, section
))) {
3305 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3309 if ((b
= pa_parse_boolean(rvalue
)) < 0) {
3310 pa_log("[%s:%u] Skip probe invalid of '%s'", filename
, line
, section
);
3319 static int decibel_fix_parse_db_values(
3320 const char *filename
,
3322 const char *section
,
3328 pa_alsa_profile_set
*ps
= userdata
;
3329 pa_alsa_decibel_fix
*db_fix
;
3333 unsigned n
= 8; /* Current size of the db_values table. */
3334 unsigned min_step
= 0;
3335 unsigned max_step
= 0;
3336 unsigned i
= 0; /* Index to the items table. */
3337 unsigned prev_step
= 0;
3340 pa_assert(filename
);
3346 if (!(db_fix
= decibel_fix_get(ps
, section
))) {
3347 pa_log("[%s:%u] %s invalid in section %s", filename
, line
, lvalue
, section
);
3351 if (!(items
= pa_split_spaces_strv(rvalue
))) {
3352 pa_log("[%s:%u] Value missing", pa_strnull(filename
), line
);
3356 db_values
= pa_xnew(long, n
);
3358 while ((item
= items
[i
++])) {
3359 char *s
= item
; /* Step value string. */
3360 char *d
= item
; /* dB value string. */
3364 /* Move d forward until it points to a colon or to the end of the item. */
3365 for (; *d
&& *d
!= ':'; ++d
);
3368 /* item started with colon. */
3369 pa_log("[%s:%u] No step value found in %s", filename
, line
, item
);
3373 if (!*d
|| !*(d
+ 1)) {
3374 /* No colon found, or it was the last character in item. */
3375 pa_log("[%s:%u] No dB value found in %s", filename
, line
, item
);
3379 /* pa_atou() needs a null-terminating string. Let's replace the colon
3380 * with a zero byte. */
3383 if (pa_atou(s
, &step
) < 0) {
3384 pa_log("[%s:%u] Invalid step value: %s", filename
, line
, s
);
3388 if (pa_atod(d
, &db
) < 0) {
3389 pa_log("[%s:%u] Invalid dB value: %s", filename
, line
, d
);
3393 if (step
<= prev_step
&& i
!= 1) {
3394 pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename
, line
, step
, prev_step
);
3398 if (db
< prev_db
&& i
!= 1) {
3399 pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", filename
, line
, db
, prev_db
);
3405 db_values
[0] = (long) (db
* 100.0);
3409 /* Interpolate linearly. */
3410 double db_increment
= (db
- prev_db
) / (step
- prev_step
);
3412 for (; prev_step
< step
; ++prev_step
, prev_db
+= db_increment
) {
3414 /* Reallocate the db_values table if it's about to overflow. */
3415 if (prev_step
+ 1 - min_step
== n
) {
3417 db_values
= pa_xrenew(long, db_values
, n
);
3420 db_values
[prev_step
+ 1 - min_step
] = (long) ((prev_db
+ db_increment
) * 100.0);
3427 db_fix
->min_step
= min_step
;
3428 db_fix
->max_step
= max_step
;
3429 pa_xfree(db_fix
->db_values
);
3430 db_fix
->db_values
= db_values
;
3432 pa_xstrfreev(items
);
3437 pa_xstrfreev(items
);
3438 pa_xfree(db_values
);
3443 static int mapping_verify(pa_alsa_mapping
*m
, const pa_channel_map
*bonus
) {
3445 static const struct description_map well_known_descriptions
[] = {
3446 { "analog-mono", N_("Analog Mono") },
3447 { "analog-stereo", N_("Analog Stereo") },
3448 { "analog-surround-21", N_("Analog Surround 2.1") },
3449 { "analog-surround-30", N_("Analog Surround 3.0") },
3450 { "analog-surround-31", N_("Analog Surround 3.1") },
3451 { "analog-surround-40", N_("Analog Surround 4.0") },
3452 { "analog-surround-41", N_("Analog Surround 4.1") },
3453 { "analog-surround-50", N_("Analog Surround 5.0") },
3454 { "analog-surround-51", N_("Analog Surround 5.1") },
3455 { "analog-surround-61", N_("Analog Surround 6.0") },
3456 { "analog-surround-61", N_("Analog Surround 6.1") },
3457 { "analog-surround-70", N_("Analog Surround 7.0") },
3458 { "analog-surround-71", N_("Analog Surround 7.1") },
3459 { "iec958-stereo", N_("Digital Stereo (IEC958)") },
3460 { "iec958-passthrough", N_("Digital Passthrough (IEC958)") },
3461 { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
3462 { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
3463 { "hdmi-stereo", N_("Digital Stereo (HDMI)") }
3468 if (!pa_channel_map_valid(&m
->channel_map
)) {
3469 pa_log("Mapping %s is missing channel map.", m
->name
);
3473 if (!m
->device_strings
) {
3474 pa_log("Mapping %s is missing device strings.", m
->name
);
3478 if ((m
->input_path_names
&& m
->input_element
) ||
3479 (m
->output_path_names
&& m
->output_element
)) {
3480 pa_log("Mapping %s must have either mixer path or mixer element, not both.", m
->name
);
3484 if (!m
->description
)
3485 m
->description
= pa_xstrdup(lookup_description(m
->name
,
3486 well_known_descriptions
,
3487 PA_ELEMENTSOF(well_known_descriptions
)));
3489 if (!m
->description
)
3490 m
->description
= pa_xstrdup(m
->name
);
3493 if (pa_channel_map_equal(&m
->channel_map
, bonus
))
3495 else if (m
->channel_map
.channels
== bonus
->channels
)
3502 void pa_alsa_mapping_dump(pa_alsa_mapping
*m
) {
3503 char cm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
3507 pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
3509 pa_strnull(m
->description
),
3511 pa_channel_map_snprint(cm
, sizeof(cm
), &m
->channel_map
),
3512 pa_yes_no(m
->supported
),
3516 static void profile_set_add_auto_pair(
3517 pa_alsa_profile_set
*ps
,
3518 pa_alsa_mapping
*m
, /* output */
3519 pa_alsa_mapping
*n
/* input */) {
3527 if (m
&& m
->direction
== PA_ALSA_DIRECTION_INPUT
)
3530 if (n
&& n
->direction
== PA_ALSA_DIRECTION_OUTPUT
)
3534 name
= pa_sprintf_malloc("output:%s+input:%s", m
->name
, n
->name
);
3536 name
= pa_sprintf_malloc("output:%s", m
->name
);
3538 name
= pa_sprintf_malloc("input:%s", n
->name
);
3540 if (pa_hashmap_get(ps
->profiles
, name
)) {
3545 p
= pa_xnew0(pa_alsa_profile
, 1);
3546 p
->profile_set
= ps
;
3550 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3551 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3552 p
->priority
+= m
->priority
* 100;
3556 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3557 pa_idxset_put(p
->input_mappings
, n
, NULL
);
3558 p
->priority
+= n
->priority
;
3561 pa_hashmap_put(ps
->profiles
, p
->name
, p
);
3564 static void profile_set_add_auto(pa_alsa_profile_set
*ps
) {
3565 pa_alsa_mapping
*m
, *n
;
3566 void *m_state
, *n_state
;
3570 PA_HASHMAP_FOREACH(m
, ps
->mappings
, m_state
) {
3571 profile_set_add_auto_pair(ps
, m
, NULL
);
3573 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3574 profile_set_add_auto_pair(ps
, m
, n
);
3577 PA_HASHMAP_FOREACH(n
, ps
->mappings
, n_state
)
3578 profile_set_add_auto_pair(ps
, NULL
, n
);
3581 static int profile_verify(pa_alsa_profile
*p
) {
3583 static const struct description_map well_known_descriptions
[] = {
3584 { "output:analog-mono+input:analog-mono", N_("Analog Mono Duplex") },
3585 { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
3586 { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
3587 { "off", N_("Off") }
3592 /* Replace the output mapping names by the actual mappings */
3593 if (p
->output_mapping_names
) {
3596 pa_assert(!p
->output_mappings
);
3597 p
->output_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3599 for (name
= p
->output_mapping_names
; *name
; name
++) {
3602 pa_bool_t duplicate
= FALSE
;
3604 for (in
= name
+ 1; *in
; in
++)
3605 if (pa_streq(*name
, *in
)) {
3613 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_INPUT
) {
3614 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3618 pa_idxset_put(p
->output_mappings
, m
, NULL
);
3624 pa_xstrfreev(p
->output_mapping_names
);
3625 p
->output_mapping_names
= NULL
;
3628 /* Replace the input mapping names by the actual mappings */
3629 if (p
->input_mapping_names
) {
3632 pa_assert(!p
->input_mappings
);
3633 p
->input_mappings
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
3635 for (name
= p
->input_mapping_names
; *name
; name
++) {
3638 pa_bool_t duplicate
= FALSE
;
3640 for (in
= name
+ 1; *in
; in
++)
3641 if (pa_streq(*name
, *in
)) {
3649 if (!(m
= pa_hashmap_get(p
->profile_set
->mappings
, *name
)) || m
->direction
== PA_ALSA_DIRECTION_OUTPUT
) {
3650 pa_log("Profile '%s' refers to unexistant mapping '%s'.", p
->name
, *name
);
3654 pa_idxset_put(p
->input_mappings
, m
, NULL
);
3660 pa_xstrfreev(p
->input_mapping_names
);
3661 p
->input_mapping_names
= NULL
;
3664 if (!p
->input_mappings
&& !p
->output_mappings
) {
3665 pa_log("Profile '%s' lacks mappings.", p
->name
);
3669 if (!p
->description
)
3670 p
->description
= pa_xstrdup(lookup_description(p
->name
,
3671 well_known_descriptions
,
3672 PA_ELEMENTSOF(well_known_descriptions
)));
3674 if (!p
->description
) {
3679 sb
= pa_strbuf_new();
3681 if (p
->output_mappings
)
3682 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3683 if (!pa_strbuf_isempty(sb
))
3684 pa_strbuf_puts(sb
, " + ");
3686 pa_strbuf_printf(sb
, _("%s Output"), m
->description
);
3689 if (p
->input_mappings
)
3690 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3691 if (!pa_strbuf_isempty(sb
))
3692 pa_strbuf_puts(sb
, " + ");
3694 pa_strbuf_printf(sb
, _("%s Input"), m
->description
);
3697 p
->description
= pa_strbuf_tostring_free(sb
);
3703 void pa_alsa_profile_dump(pa_alsa_profile
*p
) {
3708 pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
3710 pa_strnull(p
->description
),
3712 pa_yes_no(p
->supported
),
3713 p
->input_mappings
? pa_idxset_size(p
->input_mappings
) : 0,
3714 p
->output_mappings
? pa_idxset_size(p
->output_mappings
) : 0);
3716 if (p
->input_mappings
)
3717 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
)
3718 pa_log_debug("Input %s", m
->name
);
3720 if (p
->output_mappings
)
3721 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
)
3722 pa_log_debug("Output %s", m
->name
);
3725 static int decibel_fix_verify(pa_alsa_decibel_fix
*db_fix
) {
3728 /* Check that the dB mapping has been configured. Since "db-values" is
3729 * currently the only option in the DecibelFix section, and decibel fix
3730 * objects don't get created if a DecibelFix section is empty, this is
3731 * actually a redundant check. Having this may prevent future bugs,
3733 if (!db_fix
->db_values
) {
3734 pa_log("Decibel fix for element %s lacks the dB values.", db_fix
->name
);
3741 void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix
*db_fix
) {
3742 char *db_values
= NULL
;
3746 if (db_fix
->db_values
) {
3748 unsigned long i
, nsteps
;
3750 pa_assert(db_fix
->min_step
<= db_fix
->max_step
);
3751 nsteps
= db_fix
->max_step
- db_fix
->min_step
+ 1;
3753 buf
= pa_strbuf_new();
3754 for (i
= 0; i
< nsteps
; ++i
)
3755 pa_strbuf_printf(buf
, "[%li]:%0.2f ", i
+ db_fix
->min_step
, db_fix
->db_values
[i
] / 100.0);
3757 db_values
= pa_strbuf_tostring_free(buf
);
3760 pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
3761 db_fix
->name
, db_fix
->min_step
, db_fix
->max_step
, pa_strnull(db_values
));
3763 pa_xfree(db_values
);
3766 pa_alsa_profile_set
* pa_alsa_profile_set_new(const char *fname
, const pa_channel_map
*bonus
) {
3767 pa_alsa_profile_set
*ps
;
3770 pa_alsa_decibel_fix
*db_fix
;
3775 static pa_config_item items
[] = {
3777 { "auto-profiles", pa_config_parse_bool
, NULL
, "General" },
3780 { "device-strings", mapping_parse_device_strings
, NULL
, NULL
},
3781 { "channel-map", mapping_parse_channel_map
, NULL
, NULL
},
3782 { "paths-input", mapping_parse_paths
, NULL
, NULL
},
3783 { "paths-output", mapping_parse_paths
, NULL
, NULL
},
3784 { "element-input", mapping_parse_element
, NULL
, NULL
},
3785 { "element-output", mapping_parse_element
, NULL
, NULL
},
3786 { "direction", mapping_parse_direction
, NULL
, NULL
},
3788 /* Shared by [Mapping ...] and [Profile ...] */
3789 { "description", mapping_parse_description
, NULL
, NULL
},
3790 { "priority", mapping_parse_priority
, NULL
, NULL
},
3793 { "input-mappings", profile_parse_mappings
, NULL
, NULL
},
3794 { "output-mappings", profile_parse_mappings
, NULL
, NULL
},
3795 { "skip-probe", profile_parse_skip_probe
, NULL
, NULL
},
3797 /* [DecibelFix ...] */
3798 { "db-values", decibel_fix_parse_db_values
, NULL
, NULL
},
3799 { NULL
, NULL
, NULL
, NULL
}
3802 ps
= pa_xnew0(pa_alsa_profile_set
, 1);
3803 ps
->mappings
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3804 ps
->profiles
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3805 ps
->decibel_fixes
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
3807 items
[0].data
= &ps
->auto_profiles
;
3810 fname
= "default.conf";
3812 fn
= pa_maybe_prefix_path(fname
,
3813 pa_run_from_build_tree() ? PA_BUILDDIR
"/modules/alsa/mixer/profile-sets/" :
3814 PA_ALSA_PROFILE_SETS_DIR
);
3816 r
= pa_config_parse(fn
, NULL
, items
, ps
);
3822 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
3823 if (mapping_verify(m
, bonus
) < 0)
3826 if (ps
->auto_profiles
)
3827 profile_set_add_auto(ps
);
3829 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
3830 if (profile_verify(p
) < 0)
3833 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
3834 if (decibel_fix_verify(db_fix
) < 0)
3840 pa_alsa_profile_set_free(ps
);
3844 void pa_alsa_profile_set_probe(
3845 pa_alsa_profile_set
*ps
,
3847 const pa_sample_spec
*ss
,
3848 unsigned default_n_fragments
,
3849 unsigned default_fragment_size_msec
) {
3852 pa_alsa_profile
*p
, *last
= NULL
;
3862 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
) {
3863 pa_sample_spec try_ss
;
3864 pa_channel_map try_map
;
3865 snd_pcm_uframes_t try_period_size
, try_buffer_size
;
3868 /* Is this already marked that it is supported? (i.e. from the config file) */
3872 pa_log_debug("Looking at profile %s", p
->name
);
3874 /* Close PCMs from the last iteration we don't need anymore */
3875 if (last
&& last
->output_mappings
)
3876 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
) {
3881 if (last
->supported
)
3884 if (!p
->output_mappings
|| !pa_idxset_get_by_data(p
->output_mappings
, m
, NULL
)) {
3885 snd_pcm_close(m
->output_pcm
);
3886 m
->output_pcm
= NULL
;
3890 if (last
&& last
->input_mappings
)
3891 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
) {
3896 if (last
->supported
)
3899 if (!p
->input_mappings
|| !pa_idxset_get_by_data(p
->input_mappings
, m
, NULL
)) {
3900 snd_pcm_close(m
->input_pcm
);
3901 m
->input_pcm
= NULL
;
3905 p
->supported
= TRUE
;
3907 /* Check if we can open all new ones */
3908 if (p
->output_mappings
)
3909 PA_IDXSET_FOREACH(m
, p
->output_mappings
, idx
) {
3914 pa_log_debug("Checking for playback on %s (%s)", m
->description
, m
->name
);
3915 try_map
= m
->channel_map
;
3917 try_ss
.channels
= try_map
.channels
;
3920 pa_usec_to_bytes(default_fragment_size_msec
* PA_USEC_PER_MSEC
, &try_ss
) /
3921 pa_frame_size(&try_ss
);
3922 try_buffer_size
= default_n_fragments
* try_period_size
;
3924 if (!(m
->output_pcm
= pa_alsa_open_by_template(
3929 SND_PCM_STREAM_PLAYBACK
,
3930 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3932 p
->supported
= FALSE
;
3937 if (p
->input_mappings
&& p
->supported
)
3938 PA_IDXSET_FOREACH(m
, p
->input_mappings
, idx
) {
3943 pa_log_debug("Checking for recording on %s (%s)", m
->description
, m
->name
);
3944 try_map
= m
->channel_map
;
3946 try_ss
.channels
= try_map
.channels
;
3949 pa_usec_to_bytes(default_fragment_size_msec
*PA_USEC_PER_MSEC
, &try_ss
) /
3950 pa_frame_size(&try_ss
);
3951 try_buffer_size
= default_n_fragments
* try_period_size
;
3953 if (!(m
->input_pcm
= pa_alsa_open_by_template(
3958 SND_PCM_STREAM_CAPTURE
,
3959 &try_period_size
, &try_buffer_size
, 0, NULL
, NULL
,
3961 p
->supported
= FALSE
;
3969 pa_log_debug("Profile %s supported.", p
->name
);
3976 if (last
->output_mappings
)
3977 PA_IDXSET_FOREACH(m
, last
->output_mappings
, idx
)
3978 if (m
->output_pcm
) {
3980 if (last
->supported
)
3983 snd_pcm_close(m
->output_pcm
);
3984 m
->output_pcm
= NULL
;
3987 if (last
->input_mappings
)
3988 PA_IDXSET_FOREACH(m
, last
->input_mappings
, idx
)
3991 if (last
->supported
)
3994 snd_pcm_close(m
->input_pcm
);
3995 m
->input_pcm
= NULL
;
3999 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4000 if (!p
->supported
) {
4001 pa_hashmap_remove(ps
->profiles
, p
->name
);
4005 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4006 if (m
->supported
<= 0) {
4007 pa_hashmap_remove(ps
->mappings
, m
->name
);
4014 void pa_alsa_profile_set_dump(pa_alsa_profile_set
*ps
) {
4017 pa_alsa_decibel_fix
*db_fix
;
4022 pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
4025 pa_yes_no(ps
->auto_profiles
),
4026 pa_yes_no(ps
->probed
),
4027 pa_hashmap_size(ps
->mappings
),
4028 pa_hashmap_size(ps
->profiles
),
4029 pa_hashmap_size(ps
->decibel_fixes
));
4031 PA_HASHMAP_FOREACH(m
, ps
->mappings
, state
)
4032 pa_alsa_mapping_dump(m
);
4034 PA_HASHMAP_FOREACH(p
, ps
->profiles
, state
)
4035 pa_alsa_profile_dump(p
);
4037 PA_HASHMAP_FOREACH(db_fix
, ps
->decibel_fixes
, state
)
4038 pa_alsa_decibel_fix_dump(db_fix
);
4041 void pa_alsa_add_ports(pa_hashmap
**p
, pa_alsa_path_set
*ps
) {
4048 /* if there is no path, we don't want a port list */
4052 if (!ps
->paths
->next
){
4055 /* If there is only one path, but no or only one setting, then
4056 * we want a port list either */
4057 if (!ps
->paths
->settings
|| !ps
->paths
->settings
->next
)
4060 /* Ok, there is only one path, however with multiple settings,
4061 * so let's create a port for each setting */
4062 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4064 PA_LLIST_FOREACH(s
, ps
->paths
->settings
) {
4065 pa_device_port
*port
;
4066 pa_alsa_port_data
*data
;
4068 port
= pa_device_port_new(s
->name
, s
->description
, sizeof(pa_alsa_port_data
));
4069 port
->priority
= s
->priority
;
4071 data
= PA_DEVICE_PORT_DATA(port
);
4072 data
->path
= ps
->paths
;
4075 pa_hashmap_put(*p
, port
->name
, port
);
4080 /* We have multiple paths, so let's create a port for each
4081 * one, and each of each settings */
4082 *p
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
4084 PA_LLIST_FOREACH(path
, ps
->paths
) {
4086 if (!path
->settings
|| !path
->settings
->next
) {
4087 pa_device_port
*port
;
4088 pa_alsa_port_data
*data
;
4090 /* If there is no or just one setting we only need a
4093 port
= pa_device_port_new(path
->name
, path
->description
, sizeof(pa_alsa_port_data
));
4094 port
->priority
= path
->priority
* 100;
4097 data
= PA_DEVICE_PORT_DATA(port
);
4099 data
->setting
= path
->settings
;
4101 pa_hashmap_put(*p
, port
->name
, port
);
4105 PA_LLIST_FOREACH(s
, path
->settings
) {
4106 pa_device_port
*port
;
4107 pa_alsa_port_data
*data
;
4110 n
= pa_sprintf_malloc("%s;%s", path
->name
, s
->name
);
4112 if (s
->description
[0])
4113 d
= pa_sprintf_malloc(_("%s / %s"), path
->description
, s
->description
);
4115 d
= pa_xstrdup(path
->description
);
4117 port
= pa_device_port_new(n
, d
, sizeof(pa_alsa_port_data
));
4118 port
->priority
= path
->priority
* 100 + s
->priority
;
4123 data
= PA_DEVICE_PORT_DATA(port
);
4127 pa_hashmap_put(*p
, port
->name
, port
);
4133 pa_log_debug("Added %u ports", pa_hashmap_size(*p
));