Accepting request 774846 from multimedia:libs

OBS-URL: https://build.opensuse.org/request/show/774846
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/pulseaudio?expand=0&rev=167
This commit is contained in:
Dominique Leuenberger 2020-02-21 15:40:33 +00:00 committed by Git OBS Bridge
commit 24b51185fe
34 changed files with 5568 additions and 0 deletions

View File

@ -0,0 +1,48 @@
From 3dff31e19ca627fc4e0a8f13aeb44923118ecfa1 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen <tanuk@iki.fi>
Date: Fri, 20 Sep 2019 17:09:40 +0300
Subject: [PATCH] alsa-mixer-path-test: Hide unused functions when building
with Meson
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Silences these warnings:
[509/574] Compiling C object 'src/tests/a4ccf2d@@alsa-mixer-path-test@exe/alsa-mixer-path-test.c.o'.
../src/tests/alsa-mixer-path-test.c:24:20: warning: load_makefile defined but not used [-Wunused-function]
static pa_strlist *load_makefile() {
^~~~~~~~~~~~~
../src/tests/alsa-mixer-path-test.c:17:20: warning: get_default_paths_dir defined but not used [-Wunused-function]
static const char *get_default_paths_dir(void) {
^~~~~~~~~~~~~~~~~~~~~
---
src/tests/alsa-mixer-path-test.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/tests/alsa-mixer-path-test.c b/src/tests/alsa-mixer-path-test.c
index ee40587b7b13..75cf086138cc 100644
--- a/src/tests/alsa-mixer-path-test.c
+++ b/src/tests/alsa-mixer-path-test.c
@@ -13,6 +13,10 @@
#include <pulsecore/strlist.h>
#include <modules/alsa/alsa-mixer.h>
+/* This test inspects the Makefile, so this is not applicable when using
+ * Meson. */
+#ifndef MESON_BUILD
+
/* This function was copied from alsa-mixer.c */
static const char *get_default_paths_dir(void) {
if (pa_run_from_build_tree())
@@ -52,6 +56,7 @@ static pa_strlist *load_makefile() {
fclose(f);
return result;
}
+#endif /* end of #ifndef MESON_BUILD */
START_TEST (mixer_path_test) {
#ifdef MESON_BUILD
--
2.16.4

View File

@ -0,0 +1,30 @@
From 248a77c7fd2f9682c58d3d3dd6f3c2ba3ad2c111 Mon Sep 17 00:00:00 2001
From: Tanu Kaskinen <tanuk@iki.fi>
Date: Sat, 2 Sep 2017 15:35:01 +0300
Subject: [PATCH] alsa-mixer: recognize the "Speaker Jack" control
This control has been seen in the wild:
https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-August/028595.html
(The pastebin link in that mail might not work anymore, but the paste
just shows that there's a Speaker Jack control).
---
src/modules/alsa/mixer/paths/analog-output-speaker.conf | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/modules/alsa/mixer/paths/analog-output-speaker.conf b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
index 9f4dac414134..6f9968e1f537 100644
--- a/src/modules/alsa/mixer/paths/analog-output-speaker.conf
+++ b/src/modules/alsa/mixer/paths/analog-output-speaker.conf
@@ -56,6 +56,9 @@ state.unplugged = unknown
state.plugged = no
state.unplugged = unknown
+[Jack Speaker]
+required-any = any
+
[Jack Speaker Phantom]
required-any = any
state.plugged = unknown
--
2.16.4

View File

@ -0,0 +1,104 @@
From 7259e8c22fb97b1ae80ac4909713e51b293afc4a Mon Sep 17 00:00:00 2001
From: Josh <questofiranon@gmail.com>
Date: Sat, 25 May 2019 02:35:01 -0700
Subject: [PATCH] alsa-mixer: add support for SteelSeries Arctis Pro 2019
headset
Signed-off-by: Dave Chiluk <chiluk@ubuntu.com>
---
src/Makefile.am | 6 +++---
src/modules/alsa/90-pulseaudio.rules | 10 +++++++---
...ut-chat.conf => steelseries-arctis-output-chat-common.conf} | 0
...ut-game.conf => steelseries-arctis-output-game-common.conf} | 0
...usb-audio.conf => steelseries-arctis-common-usb-audio.conf} | 4 ++--
5 files changed, 12 insertions(+), 8 deletions(-)
rename src/modules/alsa/mixer/paths/{steelseries-arctis-5-output-chat.conf => steelseries-arctis-output-chat-common.conf} (100%)
rename src/modules/alsa/mixer/paths/{steelseries-arctis-5-output-game.conf => steelseries-arctis-output-game-common.conf} (100%)
rename src/modules/alsa/mixer/profile-sets/{steelseries-arctis-5-usb-audio.conf => steelseries-arctis-common-usb-audio.conf} (80%)
diff --git a/src/Makefile.am b/src/Makefile.am
index e3baf587a8a3..3db921a07e1b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1353,7 +1353,7 @@ dist_alsaprofilesets_DATA = \
modules/alsa/mixer/profile-sets/native-instruments-korecontroller.conf \
modules/alsa/mixer/profile-sets/kinect-audio.conf \
modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf \
- modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf \
+ modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf \
modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf \
modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf \
modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf
@@ -1399,8 +1399,8 @@ dist_alsapaths_DATA = \
modules/alsa/mixer/paths/hdmi-output-5.conf \
modules/alsa/mixer/paths/hdmi-output-6.conf \
modules/alsa/mixer/paths/hdmi-output-7.conf \
- modules/alsa/mixer/paths/steelseries-arctis-5-output-chat.conf \
- modules/alsa/mixer/paths/steelseries-arctis-5-output-game.conf \
+ modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf \
+ modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf \
modules/alsa/mixer/paths/steelseries-arctis-7-input.conf \
modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf \
modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index d85763917d76..9d34e6dfdd38 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -109,16 +109,20 @@ ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", ENV{PULSE_PROFILE_SET}="maudi
ATTRS{idVendor}=="045e", ATTRS{idProduct}=="02bb", ENV{PULSE_PROFILE_SET}="kinect-audio.conf"
ATTRS{idVendor}=="041e", ATTRS{idProduct}=="322c", ENV{PULSE_PROFILE_SET}="sb-omni-surround-5.1.conf"
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4014", ENV{PULSE_PROFILE_SET}="dell-dock-tb16-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1250", ENV{PULSE_PROFILE_SET}="steelseries-arctis-5-usb-audio.conf"
+
# ID 1038:12ad is for the 2018 refresh of the Arctis 7.
-# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7
-# configuration).
+# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7 configuration).
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1260", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12ad", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
+# ID 1038:1250 is for the Arctis 5
+# ID 1038:1252 is for Arctis Pro 2019 edition
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1250", ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1252", ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
+
ATTRS{idVendor}=="147a", ATTRS{idProduct}=="e055", ENV{PULSE_PROFILE_SET}="cmedia-high-speed-true-hdaudio.conf"
GOTO="pulseaudio_end"
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-5-output-chat.conf b/src/modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf
similarity index 100%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-5-output-chat.conf
rename to src/modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-5-output-game.conf b/src/modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf
similarity index 100%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-5-output-game.conf
rename to src/modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf
diff --git a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
similarity index 80%
rename from src/modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf
rename to src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
index fe353c38f09c..80c33707aea4 100644
--- a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-5-usb-audio.conf
+++ b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
@@ -6,13 +6,13 @@ description = Chat
device-strings = hw:%f,0,0
channel-map = left,right
paths-input = analog-input-mic
-paths-output = steelseries-arctis-5-output-chat
+paths-output = steelseries-arctis-output-chat-common
[Mapping analog-game]
description = Game
device-strings = hw:%f,1,0
channel-map = left,right
-paths-output = steelseries-arctis-5-output-game
+paths-output = steelseries-arctis-output-game-common
direction = output
[Profile output:analog-chat+output:analog-game+input:analog-chat]
--
2.16.4

View File

@ -0,0 +1,29 @@
From 79d3b99ba4773651c4ebc082233f9d0b5f68bfad Mon Sep 17 00:00:00 2001
From: Krzysztof Stasiowski <krzys.stasiowski@gmail.com>
Date: Tue, 18 Jun 2019 11:50:35 +0000
Subject: [PATCH] alsa-mixer: Add support for SteelSeries Arctis 5 2019 headset
Signed-off-by: Dave Chiluk <chiluk@ubuntu.com>
---
src/modules/alsa/90-pulseaudio.rules | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index 9d34e6dfdd38..61e7692543e8 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -119,8 +119,10 @@ ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", ENV{PULSE_PROFILE_SET}="steel
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
# ID 1038:1250 is for the Arctis 5
-# ID 1038:1252 is for Arctis Pro 2019 edition
+# ID 1037:12aa is for the Arctis 5 2019
+# ID 1038:1252 is for the Arctis Pro 2019 edition
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1250", ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12aa", ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1252", ENV{PULSE_PROFILE_SET}="steelseries-arctis-common-usb-audio.conf"
ATTRS{idVendor}=="147a", ATTRS{idProduct}=="e055", ENV{PULSE_PROFILE_SET}="cmedia-high-speed-true-hdaudio.conf"
--
2.16.4

View File

@ -0,0 +1,199 @@
From 1ee1f749e154d2f64b4661f833eebaa18ae1a081 Mon Sep 17 00:00:00 2001
From: Dave Chiluk <chiluk@ubuntu.com>
Date: Thu, 8 Aug 2019 23:10:01 -0500
Subject: [PATCH] alsa-mixer: add support for LucidSound LS31, and create
usb-gaming-headset profile
---
src/Makefile.am | 8 ++++----
src/modules/alsa/90-pulseaudio.rules | 10 ++++++----
...tis-7-input.conf => usb-gaming-headset-input.conf} | 12 ++++++++++--
...-mono.conf => usb-gaming-headset-output-mono.conf} | 15 ++++++++++-----
...reo.conf => usb-gaming-headset-output-stereo.conf} | 15 ++++++++++-----
...rctis-7-usb-audio.conf => usb-gaming-headset.conf} | 19 ++++++++++++-------
6 files changed, 52 insertions(+), 27 deletions(-)
rename src/modules/alsa/mixer/paths/{steelseries-arctis-7-input.conf => usb-gaming-headset-input.conf} (66%)
rename src/modules/alsa/mixer/paths/{steelseries-arctis-7-output-mono.conf => usb-gaming-headset-output-mono.conf} (66%)
rename src/modules/alsa/mixer/paths/{steelseries-arctis-7-output-stereo.conf => usb-gaming-headset-output-stereo.conf} (68%)
rename src/modules/alsa/mixer/profile-sets/{steelseries-arctis-7-usb-audio.conf => usb-gaming-headset.conf} (79%)
diff --git a/src/Makefile.am b/src/Makefile.am
index 3db921a07e1b..0f1ded7f96c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1354,7 +1354,7 @@ dist_alsaprofilesets_DATA = \
modules/alsa/mixer/profile-sets/kinect-audio.conf \
modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf \
modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf \
- modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf \
+ modules/alsa/mixer/profile-sets/usb-gaming-headset.conf \
modules/alsa/mixer/profile-sets/dell-dock-tb16-usb-audio.conf \
modules/alsa/mixer/profile-sets/cmedia-high-speed-true-hdaudio.conf
@@ -1401,9 +1401,9 @@ dist_alsapaths_DATA = \
modules/alsa/mixer/paths/hdmi-output-7.conf \
modules/alsa/mixer/paths/steelseries-arctis-output-chat-common.conf \
modules/alsa/mixer/paths/steelseries-arctis-output-game-common.conf \
- modules/alsa/mixer/paths/steelseries-arctis-7-input.conf \
- modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf \
- modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
+ modules/alsa/mixer/paths/usb-gaming-headset-input.conf \
+ modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf \
+ modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
endif
diff --git a/src/modules/alsa/90-pulseaudio.rules b/src/modules/alsa/90-pulseaudio.rules
index 61e7692543e8..fa43cb802a8e 100644
--- a/src/modules/alsa/90-pulseaudio.rules
+++ b/src/modules/alsa/90-pulseaudio.rules
@@ -113,10 +113,12 @@ ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4014", ENV{PULSE_PROFILE_SET}="dell-
# ID 1038:12ad is for the 2018 refresh of the Arctis 7.
# ID 1038:1294 is for Arctis Pro Wireless (which works with the Arctis 7 configuration).
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1260", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12ad", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
-ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", ENV{PULSE_PROFILE_SET}="steelseries-arctis-7-usb-audio.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1260", ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12ad", ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1294", ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1730", ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
+# Lucidsound LS31
+ATTRS{idVendor}=="2f12", ATTRS{idProduct}=="0109", ENV{PULSE_PROFILE_SET}="usb-gaming-headset.conf"
# ID 1038:1250 is for the Arctis 5
# ID 1037:12aa is for the Arctis 5 2019
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-7-input.conf b/src/modules/alsa/mixer/paths/usb-gaming-headset-input.conf
similarity index 66%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-7-input.conf
rename to src/modules/alsa/mixer/paths/usb-gaming-headset-input.conf
index 3fa36e9385a5..9fa7fe908587 100644
--- a/src/modules/alsa/mixer/paths/steelseries-arctis-7-input.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-input.conf
@@ -13,8 +13,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-; Steelseries Arctis 7 USB headset microphone path. Works also with Arctis Pro
-; Wireless.
+; USB gaming headset microphone input path. These headsets usually have two
+; output devices. The first one is mono, meant for voice audio, and the second
+; one is stereo, meant for everything else. The purpose of this unusual design
+; is to provide separate volume controls for voice and other audio, which can
+; be useful in gaming.
+;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
[General]
description-key = analog-input-microphone-headset
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf
similarity index 66%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf
rename to src/modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf
index d8b24a2fbdf2..6df662f069e5 100644
--- a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-mono.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-mono.conf
@@ -13,11 +13,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-; Steelseries Arctis 7 USB headset mono output path. Works also with Arctis Pro
-; Wireless. The headset has two output devices. The first one is mono, meant
-; for voice audio, and the second one is stereo, meant for everything else. The
-; purpose of this unusual design is to provide separate volume controls for
-; voice and other audio, which can be useful in gaming.
+; USB gaming headset mono output path. These headsets usually have two
+; output devices. The first one is mono, meant for voice audio, and the second
+; one is stereo, meant for everything else. The purpose of this unusual design
+; is to provide separate volume controls for voice and other audio, which can
+; be useful in gaming.
+;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
[General]
description-key = analog-output-headphones-mono
diff --git a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
similarity index 68%
rename from src/modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
rename to src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
index fcc58a033e2f..e3f91cd6cd2e 100644
--- a/src/modules/alsa/mixer/paths/steelseries-arctis-7-output-stereo.conf
+++ b/src/modules/alsa/mixer/paths/usb-gaming-headset-output-stereo.conf
@@ -13,11 +13,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-; Steelseries Arctis 7 USB headset stereo output path. Works also with Arctis
-; Pro Wireless. The headset has two output devices. The first one is mono,
-; meant for voice audio, and the second one is stereo, meant for everything
-; else. The purpose of this unusual design is to provide separate volume
-; controls for voice and other audio, which can be useful in gaming.
+; USB gaming headset mono output path. These headsets usually have two
+; output devices. The first one is mono, meant for voice audio, and the second
+; one is stereo, meant for everything else. The purpose of this unusual design
+; is to provide separate volume controls for voice and other audio, which can
+; be useful in gaming.
+;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
;
; This path doesn't provide hardware volume control, because the stereo
; output is controlled by the PCM element with index 1, and currently
diff --git a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
similarity index 79%
rename from src/modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf
rename to src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
index e1394dcfcc3b..01ecf864bcf2 100644
--- a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-7-usb-audio.conf
+++ b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
@@ -13,12 +13,17 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-; Steelseries Arctis 7 USB and Arctis Pro Wireless USB headset. These headsets
-; have a microphone and two output devices. The first output device is mono,
-; meant for voice audio, and the second one is stereo, meant for everything
-; else. The purpose of this unusual design is to provide separate volume
+; USB gaming headset.
+; These headsets usually have two output devices. The first one is mono,
+; meant for voice audio, and the second one is stereo, meant for everything
+; else. The purpose of this unusual design is to provide separate volume
; controls for voice and other audio, which can be useful in gaming.
;
+; Works with:
+; Steelseries Arctis 7
+; Steelseries Arctis Pro Wireless.
+; Lucidsound LS31
+;
; See default.conf for an explanation on the directives used here.
[General]
@@ -27,13 +32,13 @@ auto-profiles = yes
[Mapping analog-mono]
device-strings = hw:%f,0,0
channel-map = mono
-paths-output = steelseries-arctis-7-output-mono
-paths-input = steelseries-arctis-7-input
+paths-output = usb-gaming-headset-output-mono
+paths-input = usb-gaming-headset-input
[Mapping analog-stereo]
device-strings = hw:%f,1,0
channel-map = left,right
-paths-output = steelseries-arctis-7-output-stereo
+paths-output = usb-gaming-headset-output-stereo
direction = output
[Profile output:analog-mono+output:analog-stereo+input:analog-mono]
--
2.16.4

View File

@ -0,0 +1,61 @@
From c8f065250dde966825f171ff817f7301f423a42e Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 23 Nov 2019 15:17:30 +0100
Subject: [PATCH] alsa-ucm: use ucm2 name for the direct card index open
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 0a40ca8fe654..aeb4e59e323c 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -576,17 +576,25 @@ int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
const char **verb_list;
int num_verbs, i, err = 0;
- /* is UCM available for this card ? */
- err = snd_card_get_name(card_index, &card_name);
- if (err < 0) {
- pa_log("Card can't get card_name from card_index %d", card_index);
- goto name_fail;
- }
-
+ /* support multiple card instances, address card directly by index */
+ card_name = pa_sprintf_malloc("hw:%i", card_index);
+ if (card_name == NULL)
+ return -ENOMEM;
err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
if (err < 0) {
- pa_log_info("UCM not available for card %s", card_name);
- goto ucm_mgr_fail;
+ /* fallback longname: is UCM available for this card ? */
+ pa_xfree(card_name);
+ err = snd_card_get_name(card_index, &card_name);
+ if (err < 0) {
+ pa_log("Card can't get card_name from card_index %d", card_index);
+ goto name_fail;
+ }
+
+ err = snd_use_case_mgr_open(&ucm->ucm_mgr, card_name);
+ if (err < 0) {
+ pa_log_info("UCM not available for card %s", card_name);
+ goto ucm_mgr_fail;
+ }
}
pa_log_info("UCM available for card %s", card_name);
@@ -626,7 +634,7 @@ ucm_verb_fail:
}
ucm_mgr_fail:
- free(card_name);
+ pa_xfree(card_name);
name_fail:
return err;
--
2.16.4

View File

@ -0,0 +1,72 @@
From ab5be56a10a83bfdfd7f40c02245db039e6eb730 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 23 Nov 2019 15:50:29 +0100
Subject: [PATCH] alsa-ucm: add mixer IDs to ucm_items
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 6 ++++++
src/modules/alsa/alsa-ucm.h | 18 ++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index aeb4e59e323c..14056825a25f 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -100,11 +100,17 @@ static struct ucm_items item[] = {
{"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
{"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
{"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
+ {"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
+ {"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
+ {"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
{"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
{"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
{"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
{"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
{"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
+ {"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
+ {"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
+ {"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
{"CapturePriority", PA_ALSA_PROP_UCM_CAPTURE_PRIORITY},
{"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
{"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index c926f3cc39a6..4feb8c0bfc3f 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -51,6 +51,15 @@ typedef void snd_use_case_mgr_t;
/** For devices: Playback switch e.g PlaybackSwitch */
#define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH "alsa.ucm.playback.switch"
+/** For devices: Playback mixer identifier */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM "alsa.ucm.playback.mixer.element"
+
+/** For devices: Playback mixer master identifier */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM "alsa.ucm.playback.master.element"
+
+/** For devices: Playback mixer master type */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type"
+
/** For devices: Playback priority */
#define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY "alsa.ucm.playback.priority"
@@ -69,6 +78,15 @@ typedef void snd_use_case_mgr_t;
/** For devices: Capture switch e.g CaptureSwitch */
#define PA_ALSA_PROP_UCM_CAPTURE_SWITCH "alsa.ucm.capture.switch"
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM "alsa.ucm.capture.mixer.element"
+
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM "alsa.ucm.capture.master.element"
+
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type"
+
/** For devices: Capture priority */
#define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY "alsa.ucm.capture.priority"
--
2.16.4

View File

@ -0,0 +1,820 @@
From 7f4b8e1a7c2c6a873ddb0207d9b407605bd3e7d6 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 23 Nov 2019 20:59:24 +0100
Subject: [PATCH] alsa-mixer: handle the index for ALSA mixer element
identifiers
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.c | 286 ++++++++++++++++++++++++++++++------------
src/modules/alsa/alsa-mixer.h | 12 +-
2 files changed, 217 insertions(+), 81 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 1309cec5d8f8..38ace783a997 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -107,6 +107,33 @@ struct description_map {
const char *description;
};
+static char *alsa_id_str(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
+ if (id->index > 0) {
+ snprintf(dst, dst_len, "'%s',%d", id->name, id->index);
+ } else {
+ snprintf(dst, dst_len, "'%s'", id->name);
+ }
+ return dst;
+}
+
+static int alsa_id_decode(const char *src, char *name, int *index) {
+ char *idx;
+
+ *index = 0;
+ strcpy(name, src);
+ idx = strchr(name, ',');
+ if (idx == NULL)
+ return 0;
+ *idx = '\0';
+ idx++;
+ if (*idx < '0' || *idx > '9') {
+ pa_log("Element %s: index value is invalid", src);
+ return 1;
+ }
+ *index = atoi(idx);
+ return 0;
+}
+
pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name) {
pa_alsa_jack *jack;
@@ -641,6 +668,7 @@ static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
pa_xfree(db_fix->name);
pa_xfree(db_fix->db_values);
+ pa_xfree(db_fix->key);
pa_xfree(db_fix);
}
@@ -656,7 +684,7 @@ static void element_free(pa_alsa_element *e) {
if (e->db_fix)
decibel_fix_free(e->db_fix);
- pa_xfree(e->alsa_name);
+ pa_xfree(e->alsa_id.name);
pa_xfree(e);
}
@@ -717,11 +745,11 @@ static pa_volume_t from_alsa_volume(long v, long min, long max) {
return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
}
-#define SELEM_INIT(sid, name) \
- do { \
- snd_mixer_selem_id_alloca(&(sid)); \
- snd_mixer_selem_id_set_name((sid), (name)); \
- snd_mixer_selem_id_set_index((sid), 0); \
+#define SELEM_INIT(sid, aid) \
+ do { \
+ snd_mixer_selem_id_alloca(&(sid)); \
+ snd_mixer_selem_id_set_name((sid), (aid)->name); \
+ snd_mixer_selem_id_set_index((sid), (aid)->index); \
} while(false)
static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
@@ -729,6 +757,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
snd_mixer_elem_t *me;
snd_mixer_selem_channel_id_t c;
pa_channel_position_mask_t mask = 0;
+ char buf[64];
unsigned k;
pa_assert(m);
@@ -736,9 +765,10 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
pa_assert(cm);
pa_assert(v);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -763,14 +793,16 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
if (value < e->db_fix->min_step) {
value = e->db_fix->min_step;
snd_mixer_selem_set_playback_volume(me, c, value);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
- "Volume reset to %0.2f dB.", e->alsa_name, c,
+ "Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
} else if (value > e->db_fix->max_step) {
value = e->db_fix->max_step;
snd_mixer_selem_set_playback_volume(me, c, value);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
- "Volume reset to %0.2f dB.", e->alsa_name, c,
+ "Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
}
@@ -791,14 +823,16 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
if (value < e->db_fix->min_step) {
value = e->db_fix->min_step;
snd_mixer_selem_set_capture_volume(me, c, value);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
- "Volume reset to %0.2f dB.", e->alsa_name, c,
+ "Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
} else if (value > e->db_fix->max_step) {
value = e->db_fix->max_step;
snd_mixer_selem_set_capture_volume(me, c, value);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
- "Volume reset to %0.2f dB.", e->alsa_name, c,
+ "Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
}
@@ -896,14 +930,16 @@ static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
snd_mixer_selem_id_t *sid;
snd_mixer_elem_t *me;
snd_mixer_selem_channel_id_t c;
+ char buf[64];
pa_assert(m);
pa_assert(e);
pa_assert(b);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1057,6 +1093,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
snd_mixer_elem_t *me;
snd_mixer_selem_channel_id_t c;
pa_channel_position_mask_t mask = 0;
+ char buf[64];
unsigned k;
pa_assert(m);
@@ -1065,9 +1102,10 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
pa_assert(v);
pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1250,14 +1288,16 @@ int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_ma
static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
snd_mixer_elem_t *me;
snd_mixer_selem_id_t *sid;
+ char buf[64];
int r;
pa_assert(m);
pa_assert(e);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1266,8 +1306,10 @@ static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
else
r = snd_mixer_selem_set_capture_switch_all(me, b);
- if (r < 0)
- pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+ if (r < 0) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
+ }
return r;
}
@@ -1302,13 +1344,15 @@ static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
int r = 0;
long volume = -1;
bool volume_set = false;
+ char buf[64];
pa_assert(m);
pa_assert(e);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -1351,8 +1395,10 @@ static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
}
- if (r < 0)
- pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+ if (r < 0) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to set volume of %s: %s", buf, pa_alsa_strerror(errno));
+ }
return r;
}
@@ -1530,6 +1576,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
int r;
bool is_mono;
pa_channel_position_t p;
+ char buf[64];
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
if (!snd_mixer_selem_has_playback_volume(me)) {
@@ -1555,29 +1602,33 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
if (r < 0) {
- pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to get volume range of %s: %s", buf, pa_alsa_strerror(r));
return false;
}
if (e->min_volume >= e->max_volume) {
- 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);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Your kernel driver is broken for element %s: it reports a volume range from %li to %li which makes no sense.",
+ buf, e->min_volume, e->max_volume);
return false;
}
if (e->volume_use == PA_ALSA_VOLUME_CONSTANT && (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
- e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
+ e->constant_volume, buf, e->min_volume, e->max_volume);
return false;
}
if (e->db_fix && ((e->min_volume > e->db_fix->min_step) || (e->max_volume < e->db_fix->max_step))) {
- pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
- "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
- e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
+ "real hardware range (%li-%li). Disabling the decibel fix.", buf,
+ e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
- decibel_fix_free(e->db_fix);
- e->db_fix = NULL;
+ decibel_fix_free(e->db_fix);
+ e->db_fix = NULL;
}
if (e->db_fix) {
@@ -1598,19 +1649,22 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
long max_dB_checked = 0;
if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
- pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->min_volume);
return false;
}
if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
- pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->max_volume);
return false;
}
if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
"doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
- "%0.2f dB at level %li.", e->alsa_name, min_dB / 100.0, max_dB / 100.0,
+ "%0.2f dB at level %li.", buf, min_dB / 100.0, max_dB / 100.0,
min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
return false;
}
@@ -1629,11 +1683,12 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
}
if (e->volume_limit >= 0) {
- if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
+ if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
"%li-%li. The volume limit is ignored.",
- e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
- else {
+ buf, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
+ } else {
e->max_volume = e->volume_limit;
if (e->has_dB) {
@@ -1641,7 +1696,8 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
e->db_fix->max_step = e->max_volume;
e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
} else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
- pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to get dB value of %s: %s", buf, pa_alsa_strerror(r));
e->has_dB = false;
} else
e->max_dB = ((double) max_dB) / 100.0;
@@ -1681,7 +1737,8 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
}
if (e->n_channels <= 0) {
- pa_log_warn("Volume element %s with no channels?", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Volume element %s with no channels?", buf);
return false;
} else if (e->n_channels > 2) {
/* FIXME: In some places code like this is used:
@@ -1695,7 +1752,8 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
* Since the array size is fixed at 2, we obviously
* don't support elements with more than two
* channels... */
- pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
return false;
}
@@ -1733,7 +1791,7 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
pa_assert(e);
pa_assert(e->path);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
@@ -1854,6 +1912,8 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m)
static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool prefixed) {
pa_alsa_element *e;
+ char *name;
+ int index;
pa_assert(p);
pa_assert(section);
@@ -1869,16 +1929,22 @@ static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool p
if (strchr(section, ':'))
return NULL;
- if (p->last_element && pa_streq(p->last_element->alsa_name, section))
+ name = alloca(strlen(section) + 1);
+ if (alsa_id_decode(section, name, &index))
+ return NULL;
+
+ if (p->last_element && pa_streq(p->last_element->alsa_id.name, name) &&
+ p->last_element->alsa_id.index == index)
return p->last_element;
PA_LLIST_FOREACH(e, p->elements)
- if (pa_streq(e->alsa_name, section))
+ if (pa_streq(e->alsa_id.name, name) && e->alsa_id.index == index)
goto finish;
e = pa_xnew0(pa_alsa_element, 1);
e->path = p;
- e->alsa_name = pa_xstrdup(section);
+ e->alsa_id.name = pa_xstrdup(name);
+ e->alsa_id.index = index;
e->direction = p->direction;
e->volume_limit = -1;
@@ -1912,10 +1978,12 @@ finish:
}
static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
- char *en;
+ char *en, *name;
const char *on;
pa_alsa_option *o;
pa_alsa_element *e;
+ size_t len;
+ int index;
if (!pa_startswith(section, "Option "))
return NULL;
@@ -1926,18 +1994,25 @@ static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
if (!(on = strchr(section, ':')))
return NULL;
- en = pa_xstrndup(section, on - section);
+ len = on - section;
+ en = alloca(len + 1);
+ strncpy(en, section, len);
+ en[len] = '\0';
+
+ name = alloca(strlen(en) + 1);
+ if (alsa_id_decode(en, name, &index))
+ return NULL;
+
on++;
if (p->last_option &&
- pa_streq(p->last_option->element->alsa_name, en) &&
+ pa_streq(p->last_option->element->alsa_id.name, name) &&
+ p->last_option->element->alsa_id.index == index &&
pa_streq(p->last_option->alsa_name, on)) {
- pa_xfree(en);
return p->last_option;
}
pa_assert_se(e = element_get(p, en, false));
- pa_xfree(en);
PA_LLIST_FOREACH(o, e->options)
if (pa_streq(o->alsa_name, on))
@@ -2393,14 +2468,16 @@ static int jack_parse_append_pcm_to_name(pa_config_parser_state *state) {
static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
snd_mixer_selem_id_t *sid;
snd_mixer_elem_t *me;
+ char buf[64];
int r;
pa_assert(e);
pa_assert(m);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@@ -2411,14 +2488,18 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
else
r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
- if (r < 0)
- pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+ if (r < 0) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
+ }
} else {
pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
- if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
- pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
+ if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Failed to set enumeration of %s: %s", buf, pa_alsa_strerror(errno));
+ }
}
return r;
@@ -2462,6 +2543,7 @@ static int option_verify(pa_alsa_option *o) {
{ "output-speaker", N_("Speaker") },
{ "output-headphones", N_("Headphones") }
};
+ char buf[64];
pa_assert(o);
@@ -2472,14 +2554,16 @@ static int option_verify(pa_alsa_option *o) {
if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
- pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
+ alsa_id_str(buf, sizeof(buf), &o->element->alsa_id);
+ pa_log("Element %s of option %s not set for select.", buf, o->name);
return -1;
}
if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
!pa_streq(o->alsa_name, "on") &&
!pa_streq(o->alsa_name, "off")) {
- pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &o->element->alsa_id);
+ pa_log("Switch %s options need be named off or on ", buf);
return -1;
}
@@ -2495,6 +2579,7 @@ static int option_verify(pa_alsa_option *o) {
static int element_verify(pa_alsa_element *e) {
pa_alsa_option *o;
+ char buf[64];
pa_assert(e);
@@ -2503,12 +2588,14 @@ static int element_verify(pa_alsa_element *e) {
(e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
- pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log("Element %s cannot be required and absent at the same time.", buf);
return -1;
}
if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
- pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log("Element %s cannot set select for both switch and enumeration.", buf);
return -1;
}
@@ -2660,9 +2747,15 @@ fail:
pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
pa_alsa_path *p;
pa_alsa_element *e;
+ char *name;
+ int index;
pa_assert(element);
+ name = alloca(strlen(element) + 1);
+ if (alsa_id_decode(element, name, &index))
+ return NULL;
+
p = pa_xnew0(pa_alsa_path, 1);
p->name = pa_xstrdup(element);
p->direction = direction;
@@ -2670,7 +2763,8 @@ pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t d
e = pa_xnew0(pa_alsa_element, 1);
e->path = p;
- e->alsa_name = pa_xstrdup(element);
+ e->alsa_id.name = pa_xstrdup(name);
+ e->alsa_id.index = index;
e->direction = direction;
e->volume_limit = -1;
@@ -2820,6 +2914,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
pa_channel_position_t t;
pa_channel_position_mask_t path_volume_channels = 0;
+ char buf[64];
pa_assert(p);
pa_assert(m);
@@ -2843,12 +2938,13 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
}
PA_LLIST_FOREACH(e, p->elements) {
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
if (element_probe(e, m) < 0) {
p->supported = false;
- pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
+ pa_log_debug("Probe of element %s failed.", buf);
return -1;
}
- 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);
+ pa_log_debug("Probe of element %s succeeded (volume=%d, switch=%d, enumeration=%d).", buf, e->volume_use, e->switch_use, e->enumeration_use);
if (ignore_dB)
e->has_dB = false;
@@ -2884,13 +2980,13 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
* which cannot do dB volumes, so we we need
* to 'neutralize' this slider */
e->volume_use = PA_ALSA_VOLUME_ZERO;
- pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
+ pa_log_info("Zeroing volume of %s on path '%s'", buf, p->name);
}
}
} else if (p->has_volume) {
/* We can't use this volume, so let's ignore it */
e->volume_use = PA_ALSA_VOLUME_IGNORE;
- pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
+ pa_log_info("Ignoring volume of %s on path '%s' (missing dB info)", buf, p->name);
}
p->has_volume = true;
}
@@ -2954,11 +3050,14 @@ void pa_alsa_option_dump(pa_alsa_option *o) {
}
void pa_alsa_element_dump(pa_alsa_element *e) {
+ char buf[64];
+
pa_alsa_option *o;
pa_assert(e);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
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",
- e->alsa_name,
+ buf,
e->direction,
e->switch_use,
e->volume_use,
@@ -3008,14 +3107,16 @@ void pa_alsa_path_dump(pa_alsa_path *p) {
static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
snd_mixer_selem_id_t *sid;
snd_mixer_elem_t *me;
+ char buf[64];
pa_assert(e);
pa_assert(m);
pa_assert(cb);
- SELEM_INIT(sid, e->alsa_name);
+ SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &e->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return;
}
@@ -3081,6 +3182,8 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
char **pn = NULL, **en = NULL, **ie;
pa_alsa_decibel_fix *db_fix;
void *state, *state2;
+ char name[64];
+ int index;
pa_assert(m);
pa_assert(m->profile_set);
@@ -3160,9 +3263,18 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
if (je == ie)
continue;
+ if (strlen(*je) + 1 >= sizeof(name)) {
+ pa_log("Element identifier %s is too long!", *je);
+ continue;
+ }
+
+ if (alsa_id_decode(*je, name, &index))
+ continue;
+
e = pa_xnew0(pa_alsa_element, 1);
e->path = p;
- e->alsa_name = pa_xstrdup(*je);
+ e->alsa_id.name = pa_xstrdup(name);
+ e->alsa_id.index = index;
e->direction = direction;
e->required_absent = PA_ALSA_REQUIRED_ANY;
e->volume_limit = -1;
@@ -3183,7 +3295,8 @@ finish:
pa_alsa_element *e;
PA_LLIST_FOREACH(e, p->elements) {
- if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
+ if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_id.name) &&
+ db_fix->index == e->alsa_id.index) {
/* The profile set that contains the dB fix may be freed
* before the element, so we have to copy the dB fix
* object. */
@@ -3256,6 +3369,8 @@ static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_o
* Compares two elements to see if a is a subset of b
*/
static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
+ char buf[64];
+
pa_assert(a);
pa_assert(b);
pa_assert(m);
@@ -3293,9 +3408,10 @@ static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
snd_mixer_selem_id_t *sid;
snd_mixer_elem_t *me;
- SELEM_INIT(sid, a->alsa_name);
+ SELEM_INIT(sid, &a->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
- pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
+ alsa_id_str(buf, sizeof(buf), &a->alsa_id);
+ pa_log_warn("Element %s seems to have disappeared.", buf);
return false;
}
@@ -3325,8 +3441,9 @@ static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
return false;
for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
+ alsa_id_str(buf, sizeof(buf), &a->alsa_id);
pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
- a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
+ buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
return false;
}
}
@@ -3423,7 +3540,8 @@ static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
break;
PA_LLIST_FOREACH(eb, p2->elements) {
- if (pa_streq(ea->alsa_name, eb->alsa_name)) {
+ if (pa_streq(ea->alsa_id.name, eb->alsa_id.name) &&
+ ea->alsa_id.index == eb->alsa_id.index) {
found_matching_element = true;
is_subset = element_is_subset(ea, eb, m);
break;
@@ -3600,22 +3718,30 @@ static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
return p;
}
-static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
+static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *alsa_id) {
pa_alsa_decibel_fix *db_fix;
+ char *name;
+ int index;
- if (!pa_startswith(name, "DecibelFix "))
+ if (!pa_startswith(alsa_id, "DecibelFix "))
return NULL;
- name += 11;
+ alsa_id += 11;
- if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
+ if ((db_fix = pa_hashmap_get(ps->decibel_fixes, alsa_id)))
return db_fix;
+ name = alloca(strlen(alsa_id) + 1);
+ if (alsa_id_decode(alsa_id, name, &index))
+ return NULL;
+
db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
db_fix->profile_set = ps;
db_fix->name = pa_xstrdup(name);
+ db_fix->index = index;
+ db_fix->key = pa_xstrdup(alsa_id);
- pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
+ pa_hashmap_put(ps->decibel_fixes, db_fix->key, db_fix);
return db_fix;
}
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 65b071165c6b..709f270fbfa5 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -34,6 +34,7 @@
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata;
typedef struct pa_alsa_setting pa_alsa_setting;
+typedef struct pa_alsa_mixer_id pa_alsa_mixer_id;
typedef struct pa_alsa_option pa_alsa_option;
typedef struct pa_alsa_element pa_alsa_element;
typedef struct pa_alsa_jack pa_alsa_jack;
@@ -97,6 +98,12 @@ struct pa_alsa_setting {
unsigned priority;
};
+/* ALSA mixer element identifier */
+struct pa_alsa_mixer_id {
+ char *name;
+ int index;
+};
+
/* An option belongs to an element and refers to one enumeration item
* of the element is an enumeration item, or a switch status if the
* element is a switch item. */
@@ -123,7 +130,7 @@ struct pa_alsa_element {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_element);
- char *alsa_name;
+ struct pa_alsa_mixer_id alsa_id;
pa_alsa_direction_t direction;
pa_alsa_switch_use_t switch_use;
@@ -315,9 +322,12 @@ struct pa_alsa_profile {
};
struct pa_alsa_decibel_fix {
+ char *key;
+
pa_alsa_profile_set *profile_set;
char *name; /* Alsa volume element name. */
+ int index; /* Alsa volume element index. */
long min_step;
long max_step;
--
2.16.4

View File

@ -0,0 +1,51 @@
From 1c240b7a12e9e2f7c2266d18cbb74130bb81277e Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 26 Nov 2019 10:35:14 +0100
Subject: [PATCH] alsa-mixer: improve alsa_id_decode() function
Accept those identifiers:
Speaker,1
'Speaker',1
"Speaker",1
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 38ace783a997..f57aabe5d885 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -117,11 +117,24 @@ static char *alsa_id_str(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
}
static int alsa_id_decode(const char *src, char *name, int *index) {
- char *idx;
+ char *idx, c;
+ int i;
*index = 0;
- strcpy(name, src);
- idx = strchr(name, ',');
+ c = src[0];
+ /* Strip quotes in entries such as 'Speaker',1 or "Speaker",1 */
+ if (c == '\'' || c == '"') {
+ strcpy(name, src + 1);
+ for (i = 0; name[i] != '\0' && name[i] != c; i++);
+ idx = NULL;
+ if (name[i]) {
+ name[i] = '\0';
+ idx = strchr(name + i + 1, ',');
+ }
+ } else {
+ strcpy(name, src);
+ idx = strchr(name, ',');
+ }
if (idx == NULL)
return 0;
*idx = '\0';
--
2.16.4

View File

@ -0,0 +1,795 @@
From 3dfccada466bef64f73ef9be3d94eaee7b6f9a60 Mon Sep 17 00:00:00 2001
From: Arun Raghavan <git@arunraghavan.net>
Date: Tue, 3 May 2016 18:22:10 +0530
Subject: [PATCH] alsa-ucm: Support Playback/CaptureVolume
This allows us to support the PlaybackVolume and CaptureVolume commands
in UCM, specifying a mixer control to use for hardware volume control.
This only works with ports corresponding to single devices at the
moment, and doesn't support stacking controls for combination ports.
The configuration is intended to provide a control (like Headphone
Playback Volume), but we try to resolve to a simple mixer control
(Headphone) to reuse existing volume paths.
On the UCM side, this also requires that when disabling the device for
the port, the volume should be reset to some default.
When enabling/disabling combination devices, things are a bit iffy since
we have no way to reset the volume before switching to a combination
device. It would be nice to have a combination-transition-sequence
command in UCM to handle this and other similar cases.
PlaybackSwitch and CaptureSwitch are yet to be implemented.
---
src/modules/alsa/alsa-sink.c | 70 +++++++++++----
src/modules/alsa/alsa-source.c | 68 +++++++++++----
src/modules/alsa/alsa-ucm.c | 165 +++++++++++++++++++++++++++++-------
src/modules/alsa/alsa-ucm.h | 26 +++++-
src/modules/alsa/module-alsa-card.c | 4 +-
src/pulsecore/core-util.c | 26 ++++++
src/pulsecore/core-util.h | 2 +
7 files changed, 295 insertions(+), 66 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 4b46708ce4a3..08d4d1f38b80 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1598,7 +1598,7 @@ static void sink_set_mute_cb(pa_sink *s) {
static void mixer_volume_init(struct userdata *u) {
pa_assert(u);
- if (!u->mixer_path->has_volume) {
+ if (!u->mixer_path || !u->mixer_path->has_volume) {
pa_sink_set_write_volume_callback(u->sink, NULL);
pa_sink_set_get_volume_callback(u->sink, NULL);
pa_sink_set_set_volume_callback(u->sink, NULL);
@@ -1633,7 +1633,7 @@ static void mixer_volume_init(struct userdata *u) {
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
}
- if (!u->mixer_path->has_mute) {
+ if (!u->mixer_path || !u->mixer_path->has_mute) {
pa_sink_set_get_mute_callback(u->sink, NULL);
pa_sink_set_set_mute_callback(u->sink, NULL);
pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
@@ -1646,11 +1646,31 @@ static void mixer_volume_init(struct userdata *u) {
static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
struct userdata *u = s->userdata;
+ pa_alsa_ucm_port_data *data;
+
+ data = PA_DEVICE_PORT_DATA(p);
pa_assert(u);
pa_assert(p);
pa_assert(u->ucm_context);
+ u->mixer_path = data->path;
+ mixer_volume_init(u);
+
+ if (u->mixer_path) {
+ pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, s->muted);
+
+ if (s->set_mute)
+ s->set_mute(s);
+ if (s->flags & PA_SINK_DEFERRED_VOLUME) {
+ if (s->write_volume)
+ s->write_volume(s);
+ } else {
+ if (s->set_volume)
+ s->set_volume(s);
+ }
+ }
+
return pa_alsa_ucm_set_port(u->ucm_context, p, true);
}
@@ -2079,6 +2099,11 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
return;
}
+ if (u->ucm_context) {
+ /* We just want to open the device */
+ return;
+ }
+
if (element) {
if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_OUTPUT)))
@@ -2116,16 +2141,31 @@ static int setup_mixer(struct userdata *u, bool ignore_dB) {
return 0;
if (u->sink->active_port) {
- pa_alsa_port_data *data;
+ if (!u->ucm_context) {
+ pa_alsa_port_data *data;
- /* We have a list of supported paths, so let's activate the
- * one that has been chosen as active */
+ /* We have a list of supported paths, so let's activate the
+ * one that has been chosen as active */
+
+ data = PA_DEVICE_PORT_DATA(u->sink->active_port);
+ u->mixer_path = data->path;
+
+ pa_alsa_path_select(data->path, data->setting, u->mixer_handle, u->sink->muted);
+ } else {
+ pa_alsa_ucm_port_data *data;
- data = PA_DEVICE_PORT_DATA(u->sink->active_port);
- u->mixer_path = data->path;
+ /* First activate the port on the UCM side */
+ if (pa_alsa_ucm_set_port(u->ucm_context, u->sink->active_port, true) < 0)
+ return -1;
- pa_alsa_path_select(data->path, data->setting, u->mixer_handle, u->sink->muted);
+ data = PA_DEVICE_PORT_DATA(u->sink->active_port);
+ /* Now activate volume controls, if any */
+ if (data->path) {
+ u->mixer_path = data->path;
+ pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, u->sink->muted);
+ }
+ }
} else {
if (!u->mixer_path && u->mixer_path_set)
@@ -2135,7 +2175,6 @@ static int setup_mixer(struct userdata *u, bool ignore_dB) {
/* Hmm, we have only a single path, then let's activate it */
pa_alsa_path_select(u->mixer_path, u->mixer_path->settings, u->mixer_handle, u->sink->muted);
-
} else
return 0;
}
@@ -2466,8 +2505,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- if (!u->ucm_context)
- find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+ find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
pa_sink_new_data_init(&data);
data.driver = driver;
@@ -2524,7 +2562,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
}
if (u->ucm_context)
- pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, true, card);
+ pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, true, card, u->pcm_handle, ignore_dB);
else if (u->mixer_path_set)
pa_alsa_add_ports(&data, u->mixer_path_set, card);
@@ -2598,10 +2636,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
if (update_sw_params(u, false) < 0)
goto fail;
- if (u->ucm_context) {
- if (u->sink->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->sink->active_port, true) < 0)
- goto fail;
- } else if (setup_mixer(u, ignore_dB) < 0)
+ if (setup_mixer(u, ignore_dB) < 0)
goto fail;
pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
@@ -2725,7 +2760,8 @@ static void userdata_free(struct userdata *u) {
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
- if (u->mixer_path && !u->mixer_path_set)
+ /* Only free the mixer_path if the sink owns it */
+ if (u->mixer_path && !u->mixer_path_set && !u->ucm_context)
pa_alsa_path_free(u->mixer_path);
if (u->mixer_handle)
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index c8bf649e1730..657ed5aeda11 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1469,7 +1469,7 @@ static void source_set_mute_cb(pa_source *s) {
static void mixer_volume_init(struct userdata *u) {
pa_assert(u);
- if (!u->mixer_path->has_volume) {
+ if (!u->mixer_path || !u->mixer_path->has_volume) {
pa_source_set_write_volume_callback(u->source, NULL);
pa_source_set_get_volume_callback(u->source, NULL);
pa_source_set_set_volume_callback(u->source, NULL);
@@ -1504,7 +1504,7 @@ static void mixer_volume_init(struct userdata *u) {
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
}
- if (!u->mixer_path->has_mute) {
+ if (!u->mixer_path || !u->mixer_path->has_mute) {
pa_source_set_get_mute_callback(u->source, NULL);
pa_source_set_set_mute_callback(u->source, NULL);
pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
@@ -1517,11 +1517,31 @@ static void mixer_volume_init(struct userdata *u) {
static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
struct userdata *u = s->userdata;
+ pa_alsa_ucm_port_data *data;
+
+ data = PA_DEVICE_PORT_DATA(p);
pa_assert(u);
pa_assert(p);
pa_assert(u->ucm_context);
+ u->mixer_path = data->path;
+ mixer_volume_init(u);
+
+ if (u->mixer_path) {
+ pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, s->muted);
+
+ if (s->set_mute)
+ s->set_mute(s);
+ if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
+ if (s->write_volume)
+ s->write_volume(s);
+ } else {
+ if (s->set_volume)
+ s->set_volume(s);
+ }
+ }
+
return pa_alsa_ucm_set_port(u->ucm_context, p, false);
}
@@ -1785,6 +1805,11 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
return;
}
+ if (u->ucm_context) {
+ /* We just want to open the device */
+ return;
+ }
+
if (element) {
if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_INPUT)))
@@ -1822,16 +1847,31 @@ static int setup_mixer(struct userdata *u, bool ignore_dB) {
return 0;
if (u->source->active_port) {
- pa_alsa_port_data *data;
+ if (!u->ucm_context) {
+ pa_alsa_port_data *data;
- /* We have a list of supported paths, so let's activate the
- * one that has been chosen as active */
+ /* We have a list of supported paths, so let's activate the
+ * one that has been chosen as active */
- data = PA_DEVICE_PORT_DATA(u->source->active_port);
- u->mixer_path = data->path;
+ data = PA_DEVICE_PORT_DATA(u->source->active_port);
+ u->mixer_path = data->path;
- pa_alsa_path_select(data->path, data->setting, u->mixer_handle, u->source->muted);
+ pa_alsa_path_select(data->path, data->setting, u->mixer_handle, u->source->muted);
+ } else {
+ pa_alsa_ucm_port_data *data;
+
+ /* First activate the port on the UCM side */
+ if (pa_alsa_ucm_set_port(u->ucm_context, u->source->active_port, false) < 0)
+ return -1;
+ data = PA_DEVICE_PORT_DATA(u->source->active_port);
+
+ /* Now activate volume controls, if any */
+ if (data->path) {
+ u->mixer_path = data->path;
+ pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, u->source->muted);
+ }
+ }
} else {
if (!u->mixer_path && u->mixer_path_set)
@@ -2152,8 +2192,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- if (!u->ucm_context)
- find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+ find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
pa_source_new_data_init(&data);
data.driver = driver;
@@ -2210,7 +2249,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
}
if (u->ucm_context)
- pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, false, card);
+ pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, false, card, u->pcm_handle, ignore_dB);
else if (u->mixer_path_set)
pa_alsa_add_ports(&data, u->mixer_path_set, card);
@@ -2276,10 +2315,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
if (update_sw_params(u) < 0)
goto fail;
- if (u->ucm_context) {
- if (u->source->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->source->active_port, false) < 0)
- goto fail;
- } else if (setup_mixer(u, ignore_dB) < 0)
+ if (setup_mixer(u, ignore_dB) < 0)
goto fail;
pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
@@ -2368,7 +2404,7 @@ static void userdata_free(struct userdata *u) {
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
- if (u->mixer_path && !u->mixer_path_set)
+ if (u->mixer_path && !u->mixer_path_set && !u->ucm_context)
pa_alsa_path_free(u->mixer_path);
if (u->mixer_handle)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 14056825a25f..349a59566200 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -81,19 +81,11 @@ static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *ja
static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char *device_name);
-struct ucm_port {
- pa_alsa_ucm_config *ucm;
- pa_device_port *core_port;
-
- /* A single port will be associated with multiple devices if it represents
- * a combination of devices. */
- pa_dynarray *devices; /* pa_alsa_ucm_device */
-};
-static void ucm_port_init(struct ucm_port *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
- pa_alsa_ucm_device **devices, unsigned n_devices);
-static void ucm_port_free(pa_device_port *port);
-static void ucm_port_update_available(struct ucm_port *port);
+static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
+ pa_alsa_ucm_device **devices, unsigned n_devices);
+static void ucm_port_data_free(pa_device_port *port);
+static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
static struct ucm_items item[] = {
{"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
@@ -303,6 +295,18 @@ static int ucm_get_device_property(
else
pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
}
+
+ value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_VOLUME);
+ if (value) {
+ /* Try to get the simple control name, and failing that, just use the name as is */
+ char *selem;
+
+ if (!(selem = pa_str_strip_suffix(value, " Playback Volume")))
+ if (!(selem = pa_str_strip_suffix(value, " Volume")))
+ selem = pa_xstrdup(value);
+
+ pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), selem);
+ }
}
if (device->capture_channels) { /* source device */
@@ -324,6 +328,18 @@ static int ucm_get_device_property(
else
pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
}
+
+ value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_VOLUME);
+ if (value) {
+ /* Try to get the simple control name, and failing that, just use the name as is */
+ char *selem;
+
+ if (!(selem = pa_str_strip_suffix(value, " Capture Volume")))
+ if (!(selem = pa_str_strip_suffix(value, " Volume")))
+ selem = pa_xstrdup(value);
+
+ pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), selem);
+ }
}
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
@@ -427,6 +443,11 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
d->hw_mute_jacks = pa_dynarray_new(NULL);
d->available = PA_AVAILABLE_UNKNOWN;
+ d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
+ pa_xfree);
+ d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
+ pa_xfree);
+
PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
}
@@ -707,6 +728,46 @@ static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
}
+static void probe_volumes(pa_hashmap *hash, snd_pcm_t *pcm_handle, bool ignore_dB) {
+ pa_device_port *port;
+ pa_alsa_path *path;
+ pa_alsa_ucm_port_data *data;
+ snd_mixer_t *mixer_handle;
+ const char *profile;
+ void *state, *state2;
+
+ if (!(mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL))) {
+ pa_log_error("Failed to find a working mixer device.");
+ goto fail;
+ }
+
+ PA_HASHMAP_FOREACH(port, hash, state) {
+ data = PA_DEVICE_PORT_DATA(port);
+
+ PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
+ if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
+ pa_log_warn("Could not probe path: %s, using s/w volume", data->path->name);
+ pa_hashmap_remove(data->paths, profile);
+ } else if (!path->has_volume) {
+ pa_log_warn("Path %s is not a volume control", data->path->name);
+ pa_hashmap_remove(data->paths, profile);
+ } else
+ pa_log_debug("Set up h/w volume using '%s' for %s:%s", path->name, profile, port->name);
+ }
+ }
+
+ snd_mixer_close(mixer_handle);
+
+ return;
+
+fail:
+ /* We could not probe the paths we created. Free them and revert to software volumes. */
+ PA_HASHMAP_FOREACH(port, hash, state) {
+ data = PA_DEVICE_PORT_DATA(port);
+ pa_hashmap_remove_all(data->paths);
+ }
+}
+
static void ucm_add_port_combination(
pa_hashmap *hash,
pa_alsa_ucm_mapping_context *context,
@@ -724,7 +785,10 @@ static void ucm_add_port_combination(
char *name, *desc;
const char *dev_name;
const char *direction;
+ const char *profile, *volume_element;
pa_alsa_ucm_device *sorted[num], *dev;
+ pa_alsa_ucm_port_data *data;
+ void *state;
for (i = 0; i < num; i++)
sorted[i] = pdevices[i];
@@ -772,8 +836,6 @@ static void ucm_add_port_combination(
port = pa_hashmap_get(ports, name);
if (!port) {
- struct ucm_port *ucm_port;
-
pa_device_port_new_data port_data;
pa_device_port_new_data_init(&port_data);
@@ -781,17 +843,33 @@ static void ucm_add_port_combination(
pa_device_port_new_data_set_description(&port_data, desc);
pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
- port = pa_device_port_new(core, &port_data, sizeof(struct ucm_port));
- port->impl_free = ucm_port_free;
+ port = pa_device_port_new(core, &port_data, sizeof(pa_alsa_ucm_port_data));
pa_device_port_new_data_done(&port_data);
- ucm_port = PA_DEVICE_PORT_DATA(port);
- ucm_port_init(ucm_port, context->ucm, port, pdevices, num);
+ data = PA_DEVICE_PORT_DATA(port);
+ ucm_port_data_init(data, context->ucm, port, pdevices, num);
+ port->impl_free = ucm_port_data_free;
pa_hashmap_put(ports, port->name, port);
pa_log_debug("Add port %s: %s", port->name, port->description);
}
+ if (num == 1) {
+ /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
+ * ports. */
+ data = PA_DEVICE_PORT_DATA(port);
+
+ PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
+ pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
+ is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
+
+ if (!path)
+ pa_log_warn("Failed to set up volume control: %s", volume_element);
+ else
+ pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
+ }
+ }
+
port->priority = priority;
pa_xfree(name);
@@ -971,7 +1049,9 @@ void pa_alsa_ucm_add_ports(
pa_proplist *proplist,
pa_alsa_ucm_mapping_context *context,
bool is_sink,
- pa_card *card) {
+ pa_card *card,
+ snd_pcm_t *pcm_handle,
+ bool ignore_dB) {
uint32_t idx;
char *merged_roles;
@@ -986,6 +1066,9 @@ void pa_alsa_ucm_add_ports(
/* add ports first */
pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
+ /* now set up volume paths if any */
+ probe_volumes(*p, pcm_handle, ignore_dB);
+
/* then set property PA_PROP_DEVICE_INTENDED_ROLES */
merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
@@ -1010,10 +1093,13 @@ void pa_alsa_ucm_add_ports(
}
/* Change UCM verb and device to match selected card profile */
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
int ret = 0;
const char *profile;
pa_alsa_ucm_verb *verb;
+ pa_device_port *port;
+ pa_alsa_ucm_port_data *data;
+ void *state;
if (new_profile == old_profile)
return ret;
@@ -1042,6 +1128,12 @@ int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, co
}
}
+ /* select volume controls on ports */
+ PA_HASHMAP_FOREACH(port, card->ports, state) {
+ data = PA_DEVICE_PORT_DATA(port);
+ data->path = pa_hashmap_get(data->paths, new_profile);
+ }
+
return ret;
}
@@ -1650,11 +1742,18 @@ static void free_verb(pa_alsa_ucm_verb *verb) {
if (di->ucm_ports)
pa_dynarray_free(di->ucm_ports);
+ if (di->playback_volumes)
+ pa_hashmap_free(di->playback_volumes);
+ if (di->capture_volumes)
+ pa_hashmap_free(di->capture_volumes);
+
pa_proplist_free(di->proplist);
+
if (di->conflicting_devices)
pa_idxset_free(di->conflicting_devices, NULL);
if (di->supported_devices)
pa_idxset_free(di->supported_devices, NULL);
+
pa_xfree(di);
}
@@ -1785,7 +1884,7 @@ void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_
}
}
-static void device_add_ucm_port(pa_alsa_ucm_device *device, struct ucm_port *port) {
+static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port) {
pa_assert(device);
pa_assert(port);
@@ -1813,7 +1912,7 @@ static void device_add_hw_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *ja
}
static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) {
- struct ucm_port *port;
+ pa_alsa_ucm_port_data *port;
unsigned idx;
pa_assert(device);
@@ -1847,8 +1946,8 @@ void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) {
device_set_available(device, available);
}
-static void ucm_port_init(struct ucm_port *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
- pa_alsa_ucm_device **devices, unsigned n_devices) {
+static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port,
+ pa_alsa_ucm_device **devices, unsigned n_devices) {
unsigned i;
pa_assert(ucm);
@@ -1864,11 +1963,14 @@ static void ucm_port_init(struct ucm_port *port, pa_alsa_ucm_config *ucm, pa_dev
device_add_ucm_port(devices[i], port);
}
+ port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
+ (pa_free_cb_t) pa_alsa_path_free);
+
ucm_port_update_available(port);
}
-static void ucm_port_free(pa_device_port *port) {
- struct ucm_port *ucm_port;
+static void ucm_port_data_free(pa_device_port *port) {
+ pa_alsa_ucm_port_data *ucm_port;
pa_assert(port);
@@ -1876,9 +1978,12 @@ static void ucm_port_free(pa_device_port *port) {
if (ucm_port->devices)
pa_dynarray_free(ucm_port->devices);
+
+ if (ucm_port->paths)
+ pa_hashmap_free(ucm_port->paths);
}
-static void ucm_port_update_available(struct ucm_port *port) {
+static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
pa_alsa_ucm_device *device;
unsigned idx;
pa_available_t available = PA_AVAILABLE_YES;
@@ -1910,7 +2015,7 @@ pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_cha
return NULL;
}
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile) {
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile) {
return -1;
}
@@ -1923,7 +2028,9 @@ void pa_alsa_ucm_add_ports(
pa_proplist *proplist,
pa_alsa_ucm_mapping_context *context,
bool is_sink,
- pa_card *card) {
+ pa_card *card,
+ snd_pcm_t *pcm_handle,
+ bool ignore_dB) {
}
void pa_alsa_ucm_add_ports_combination(
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 4feb8c0bfc3f..2e39a09a51f3 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -113,10 +113,11 @@ typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
+typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
-int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile);
+int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile);
int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
@@ -125,7 +126,9 @@ void pa_alsa_ucm_add_ports(
pa_proplist *proplist,
pa_alsa_ucm_mapping_context *context,
bool is_sink,
- pa_card *card);
+ pa_card *card,
+ snd_pcm_t *pcm_handle,
+ bool ignore_dB);
void pa_alsa_ucm_add_ports_combination(
pa_hashmap *hash,
pa_alsa_ucm_mapping_context *context,
@@ -157,6 +160,11 @@ struct pa_alsa_ucm_device {
unsigned playback_channels;
unsigned capture_channels;
+ /* These may be different per verb, so we store this as a hashmap of verb -> volume_control. We might eventually want to
+ * make this a hashmap of verb -> per-verb-device-properties-struct. */
+ pa_hashmap *playback_volumes;
+ pa_hashmap *capture_volumes;
+
pa_alsa_mapping *playback_mapping;
pa_alsa_mapping *capture_mapping;
@@ -224,4 +232,18 @@ struct pa_alsa_ucm_mapping_context {
pa_idxset *ucm_modifiers;
};
+struct pa_alsa_ucm_port_data {
+ pa_alsa_ucm_config *ucm;
+ pa_device_port *core_port;
+
+ /* A single port will be associated with multiple devices if it represents
+ * a combination of devices. */
+ pa_dynarray *devices; /* pa_alsa_ucm_device */
+
+ /* profile -> pa_alsa_path for volume control */
+ pa_hashmap *paths;
+ /* Current path, set when activating profile */
+ pa_alsa_path *path;
+};
+
#endif
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 1e1090fe024e..e2a86bc1c68d 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -241,7 +241,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
/* if UCM is available for this card then update the verb */
if (u->use_ucm) {
- if (pa_alsa_ucm_set_profile(&u->ucm, nd->profile ? nd->profile->name : NULL,
+ if (pa_alsa_ucm_set_profile(&u->ucm, c, nd->profile ? nd->profile->name : NULL,
od->profile ? od->profile->name : NULL) < 0) {
ret = -1;
goto finish;
@@ -294,7 +294,7 @@ static void init_profile(struct userdata *u) {
if (d->profile && u->use_ucm) {
/* Set initial verb */
- if (pa_alsa_ucm_set_profile(ucm, d->profile->name, NULL) < 0) {
+ if (pa_alsa_ucm_set_profile(ucm, u->card, d->profile->name, NULL) < 0) {
pa_log("Failed to set ucm profile %s", d->profile->name);
return;
}
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index f5ec67b8f3fc..174987e4afda 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2893,6 +2893,32 @@ bool pa_str_in_list_spaces(const char *haystack, const char *needle) {
return false;
}
+char* pa_str_strip_suffix(const char *str, const char *suffix) {
+ size_t str_l, suf_l, prefix;
+ char *ret;
+
+ pa_assert(str);
+ pa_assert(suffix);
+
+ str_l = strlen(str);
+ suf_l = strlen(suffix);
+
+ if (str_l < suf_l)
+ return NULL;
+
+ prefix = str_l - suf_l;
+
+ if (!pa_streq(&str[prefix], suffix))
+ return NULL;
+
+ ret = pa_xmalloc(prefix + 1);
+
+ strncpy(ret, str, prefix);
+ ret[prefix] = '\0';
+
+ return ret;
+}
+
char *pa_get_user_name_malloc(void) {
ssize_t k;
char *u;
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index d1c4ae1c49aa..9440af9172ca 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -232,6 +232,8 @@ static inline bool pa_safe_streq(const char *a, const char *b) {
bool pa_str_in_list_spaces(const char *needle, const char *haystack);
bool pa_str_in_list(const char *haystack, const char *delimiters, const char *needle);
+char* pa_str_strip_suffix(const char *str, const char *suffix);
+
char *pa_get_host_name_malloc(void);
char *pa_get_user_name_malloc(void);
--
2.16.4

View File

@ -0,0 +1,242 @@
From 9acacd9ba3b9f4df0957e9ddaacbcee00396175c Mon Sep 17 00:00:00 2001
From: Jaska Uimonen <jaska.uimonen@intel.com>
Date: Tue, 1 Oct 2019 18:34:17 +0300
Subject: [PATCH] alsa-ucm: Fix volume control based on review
- sync mixer logic added
- mixer path creation, empty set in mapping creation, paths added in path creation
- path creation moved inside new port creation as it might be called twice otherwise
- some comments added
---
src/modules/alsa/alsa-sink.c | 31 ++++++++--------------------
src/modules/alsa/alsa-source.c | 29 ++++++++------------------
src/modules/alsa/alsa-ucm.c | 47 +++++++++++++++++++++++++++++-------------
src/modules/alsa/alsa-ucm.h | 2 +-
4 files changed, 52 insertions(+), 57 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 08d4d1f38b80..0a5c92529af3 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1266,7 +1266,7 @@ static void sync_mixer(struct userdata *u, pa_device_port *port) {
/* port may be NULL, because if we use a synthesized mixer path, then the
* sink has no ports. */
- if (port) {
+ if (port && !u->ucm_context) {
pa_alsa_port_data *data;
data = PA_DEVICE_PORT_DATA(port);
@@ -1648,28 +1648,19 @@ static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
struct userdata *u = s->userdata;
pa_alsa_ucm_port_data *data;
- data = PA_DEVICE_PORT_DATA(p);
-
pa_assert(u);
pa_assert(p);
+ pa_assert(u->mixer_handle);
pa_assert(u->ucm_context);
- u->mixer_path = data->path;
+ data = PA_DEVICE_PORT_DATA(p);
+ pa_assert_se(u->mixer_path = data->path);
mixer_volume_init(u);
- if (u->mixer_path) {
- pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, s->muted);
-
- if (s->set_mute)
- s->set_mute(s);
- if (s->flags & PA_SINK_DEFERRED_VOLUME) {
- if (s->write_volume)
- s->write_volume(s);
- } else {
- if (s->set_volume)
- s->set_volume(s);
- }
- }
+ if (s->flags & PA_SINK_DEFERRED_VOLUME)
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_SYNC_MIXER, p, 0, NULL);
+ else
+ sync_mixer(u, p);
return pa_alsa_ucm_set_port(u->ucm_context, p, true);
}
@@ -2091,6 +2082,7 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de
}
static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, bool ignore_dB) {
+
if (!mapping && !element)
return;
@@ -2099,11 +2091,6 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
return;
}
- if (u->ucm_context) {
- /* We just want to open the device */
- return;
- }
-
if (element) {
if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_OUTPUT)))
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 657ed5aeda11..d186101720b8 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1137,7 +1137,7 @@ static void sync_mixer(struct userdata *u, pa_device_port *port) {
/* port may be NULL, because if we use a synthesized mixer path, then the
* source has no ports. */
- if (port) {
+ if (port && !u->ucm_context) {
pa_alsa_port_data *data;
data = PA_DEVICE_PORT_DATA(port);
@@ -1523,24 +1523,17 @@ static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
pa_assert(u);
pa_assert(p);
+ pa_assert(u->mixer_handle);
pa_assert(u->ucm_context);
- u->mixer_path = data->path;
+ data = PA_DEVICE_PORT_DATA(p);
+ pa_assert_se(u->mixer_path = data->path);
mixer_volume_init(u);
- if (u->mixer_path) {
- pa_alsa_path_select(u->mixer_path, NULL, u->mixer_handle, s->muted);
-
- if (s->set_mute)
- s->set_mute(s);
- if (s->flags & PA_SOURCE_DEFERRED_VOLUME) {
- if (s->write_volume)
- s->write_volume(s);
- } else {
- if (s->set_volume)
- s->set_volume(s);
- }
- }
+ if (s->flags & PA_SOURCE_DEFERRED_VOLUME)
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_SYNC_MIXER, p, 0, NULL);
+ else
+ sync_mixer(u, p);
return pa_alsa_ucm_set_port(u->ucm_context, p, false);
}
@@ -1805,11 +1798,6 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
return;
}
- if (u->ucm_context) {
- /* We just want to open the device */
- return;
- }
-
if (element) {
if (!(u->mixer_path = pa_alsa_path_synthesize(element, PA_ALSA_DIRECTION_INPUT)))
@@ -2404,6 +2392,7 @@ static void userdata_free(struct userdata *u) {
if (u->mixer_fdl)
pa_alsa_fdlist_free(u->mixer_fdl);
+ /* Only free the mixer_path if the sink owns it */
if (u->mixer_path && !u->mixer_path_set && !u->ucm_context)
pa_alsa_path_free(u->mixer_path);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 349a59566200..a812b52f449e 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -852,21 +852,29 @@ static void ucm_add_port_combination(
pa_hashmap_put(ports, port->name, port);
pa_log_debug("Add port %s: %s", port->name, port->description);
- }
-
- if (num == 1) {
- /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
- * ports. */
- data = PA_DEVICE_PORT_DATA(port);
- PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
- pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
- is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
-
- if (!path)
- pa_log_warn("Failed to set up volume control: %s", volume_element);
- else
- pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
+ if (num == 1) {
+ /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination
+ * ports. */
+ data = PA_DEVICE_PORT_DATA(port);
+
+ PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
+ pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
+ is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
+
+ if (!path)
+ pa_log_warn("Failed to set up volume control: %s", volume_element);
+ else {
+ pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
+
+ /* Add path also to already created empty path set */
+ dev = sorted[0];
+ if (is_sink)
+ pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(volume_element), path);
+ else
+ pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(volume_element), path);
+ }
+ }
}
}
@@ -1185,16 +1193,27 @@ int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *p
static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
+ pa_alsa_path_set *ps;
+
+ /* create empty path set for the future path additions */
+ ps = pa_xnew0(pa_alsa_path_set, 1);
+ ps->direction = m->direction;
+ ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
switch (m->direction) {
case PA_ALSA_DIRECTION_ANY:
pa_idxset_put(p->output_mappings, m, NULL);
pa_idxset_put(p->input_mappings, m, NULL);
+ m->output_path_set = ps;
+ m->input_path_set = ps;
break;
case PA_ALSA_DIRECTION_OUTPUT:
pa_idxset_put(p->output_mappings, m, NULL);
+ m->output_path_set = ps;
break;
case PA_ALSA_DIRECTION_INPUT:
pa_idxset_put(p->input_mappings, m, NULL);
+ m->input_path_set = ps;
break;
}
}
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 2e39a09a51f3..d8507a83615c 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -240,7 +240,7 @@ struct pa_alsa_ucm_port_data {
* a combination of devices. */
pa_dynarray *devices; /* pa_alsa_ucm_device */
- /* profile -> pa_alsa_path for volume control */
+ /* profile name -> pa_alsa_path for volume control */
pa_hashmap *paths;
/* Current path, set when activating profile */
pa_alsa_path *path;
--
2.16.4

View File

@ -0,0 +1,120 @@
From dc9dc70fcc1b06788d08b5e7997c9053a13cbce2 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 26 Nov 2019 10:54:15 +0100
Subject: [PATCH] alsa-ucm: use the correct mixer identifiers as first
The mixer identifiers should be used for snd_mixer_selem API.
Use them as first, then try to fallback to the raw control
identifiers.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 78 ++++++++++++++++++++++++++++++++-------------
1 file changed, 56 insertions(+), 22 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index a812b52f449e..65ec6941e8de 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -200,6 +200,50 @@ static void ucm_add_devices_to_idxset(
}
}
+/* Get the volume identifier */
+static char *ucm_get_mixer_id(
+ pa_alsa_ucm_device *device,
+ const char *mprop,
+ const char *cprop,
+ const char *cid)
+{
+#if SND_LIB_VERSION >= 0x10201
+ snd_ctl_elem_id_t *ctl;
+ int err;
+#endif
+ const char *value;
+ char *value2;
+ int index;
+
+ value = pa_proplist_gets(device->proplist, mprop);
+ if (value)
+ return pa_xstrdup(value);
+ value = pa_proplist_gets(device->proplist, cprop);
+ if (value == NULL)
+ return NULL;
+#if SND_LIB_VERSION >= 0x10201
+ snd_ctl_elem_id_alloca(&ctl);
+ err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
+ if (err < 0)
+ return NULL;
+ value = snd_ctl_elem_id_get_name(ctl);
+ index = snd_ctl_elem_id_get_index(ctl);
+#else
+#warning "Upgrade to alsa-lib 1.2.1!"
+ index = 0;
+#endif
+ if (!(value2 = pa_str_strip_suffix(value, " Playback Volume")))
+ if (!(value2 = pa_str_strip_suffix(value, " Capture Volume")))
+ if (!(value2 = pa_str_strip_suffix(value, " Volume")))
+ value2 = pa_xstrdup(value);
+ if (index > 0) {
+ char *mix = pa_sprintf_malloc("'%s',%d", value2, index);
+ pa_xfree(value2);
+ return mix;
+ }
+ return value2;
+}
+
/* Create a property list for this ucm device */
static int ucm_get_device_property(
pa_alsa_ucm_device *device,
@@ -296,17 +340,12 @@ static int ucm_get_device_property(
pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
}
- value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_VOLUME);
- if (value) {
- /* Try to get the simple control name, and failing that, just use the name as is */
- char *selem;
-
- if (!(selem = pa_str_strip_suffix(value, " Playback Volume")))
- if (!(selem = pa_str_strip_suffix(value, " Volume")))
- selem = pa_xstrdup(value);
-
- pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), selem);
- }
+ value = ucm_get_mixer_id(device,
+ PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
+ PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
+ "PlaybackVolume");
+ if (value)
+ pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value);
}
if (device->capture_channels) { /* source device */
@@ -329,17 +368,12 @@ static int ucm_get_device_property(
pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
}
- value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_VOLUME);
- if (value) {
- /* Try to get the simple control name, and failing that, just use the name as is */
- char *selem;
-
- if (!(selem = pa_str_strip_suffix(value, " Capture Volume")))
- if (!(selem = pa_str_strip_suffix(value, " Volume")))
- selem = pa_xstrdup(value);
-
- pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), selem);
- }
+ value = ucm_get_mixer_id(device,
+ PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
+ PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
+ "CaptureVolume");
+ if (value)
+ pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value);
}
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
--
2.16.4

View File

@ -0,0 +1,330 @@
From 6d830bf0f08c7f92418c2d8b0e73c0415dca03c8 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Wed, 27 Nov 2019 11:34:49 +0100
Subject: [PATCH] alsa-ucm: add support for master volume
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.c | 20 +++++-----
src/modules/alsa/alsa-mixer.h | 1 +
src/modules/alsa/alsa-ucm.c | 86 +++++++++++++++++++++++++++++++++----------
src/modules/alsa/alsa-ucm.h | 19 ++++++++++
4 files changed, 96 insertions(+), 30 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index f57aabe5d885..ed06f42d86da 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -1923,7 +1923,7 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m)
return 0;
}
-static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool prefixed) {
+pa_alsa_element * pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed) {
pa_alsa_element *e;
char *name;
int index;
@@ -2025,7 +2025,7 @@ static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
return p->last_option;
}
- pa_assert_se(e = element_get(p, en, false));
+ pa_assert_se(e = pa_alsa_element_get(p, en, false));
PA_LLIST_FOREACH(o, e->options)
if (pa_streq(o->alsa_name, on))
@@ -2054,7 +2054,7 @@ static int element_parse_switch(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
@@ -2085,7 +2085,7 @@ static int element_parse_volume(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
@@ -2121,7 +2121,7 @@ static int element_parse_enumeration(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
@@ -2213,7 +2213,7 @@ static int element_parse_required(pa_config_parser_state *state) {
p = state->userdata;
- e = element_get(p, state->section, true);
+ e = pa_alsa_element_get(p, state->section, true);
o = option_get(p, state->section);
j = jack_get(p, state->section);
if (!e && !o && !j) {
@@ -2279,7 +2279,7 @@ static int element_parse_direction(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
@@ -2305,7 +2305,7 @@ static int element_parse_direction_try_other(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
@@ -2328,7 +2328,7 @@ static int element_parse_volume_limit(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
@@ -2386,7 +2386,7 @@ static int element_parse_override_map(pa_config_parser_state *state) {
p = state->userdata;
- if (!(e = element_get(p, state->section, true))) {
+ if (!(e = pa_alsa_element_get(p, state->section, true))) {
pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
return -1;
}
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 709f270fbfa5..0b634f2f2be1 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -244,6 +244,7 @@ void pa_alsa_element_dump(pa_alsa_element *e);
pa_alsa_path *pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction);
pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction);
+pa_alsa_element *pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed);
int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB);
void pa_alsa_path_dump(pa_alsa_path *p);
int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 65ec6941e8de..46d016541ddf 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -200,6 +200,14 @@ static void ucm_add_devices_to_idxset(
}
}
+static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
+ pa_assert(vol);
+ pa_xfree(vol->mixer_elem);
+ pa_xfree(vol->master_elem);
+ pa_xfree(vol->master_type);
+ pa_xfree(vol);
+}
+
/* Get the volume identifier */
static char *ucm_get_mixer_id(
pa_alsa_ucm_device *device,
@@ -244,6 +252,32 @@ static char *ucm_get_mixer_id(
return value2;
}
+/* Get the volume identifier */
+static pa_alsa_ucm_volume *ucm_get_mixer_volume(
+ pa_alsa_ucm_device *device,
+ const char *mprop,
+ const char *cprop,
+ const char *cid,
+ const char *masterid,
+ const char *mastertype)
+{
+ pa_alsa_ucm_volume *vol;
+ char *mixer_elem;
+
+ mixer_elem = ucm_get_mixer_id(device, mprop, cprop, cid);
+ if (mixer_elem == NULL)
+ return NULL;
+ vol = pa_xnew0(pa_alsa_ucm_volume, 1);
+ if (vol == NULL) {
+ pa_xfree(mixer_elem);
+ return NULL;
+ }
+ vol->mixer_elem = mixer_elem;
+ vol->master_elem = pa_xstrdup(pa_proplist_gets(device->proplist, masterid));
+ vol->master_type = pa_xstrdup(pa_proplist_gets(device->proplist, mastertype));
+ return vol;
+}
+
/* Create a property list for this ucm device */
static int ucm_get_device_property(
pa_alsa_ucm_device *device,
@@ -258,6 +292,7 @@ static int ucm_get_device_property(
int err;
uint32_t ui;
int n_confdev, n_suppdev;
+ pa_alsa_ucm_volume *vol;
for (i = 0; item[i].id; i++) {
id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
@@ -340,12 +375,14 @@ static int ucm_get_device_property(
pa_log_debug("UCM playback priority %s for device %s error", value, device_name);
}
- value = ucm_get_mixer_id(device,
- PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
- PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
- "PlaybackVolume");
- if (value)
- pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value);
+ vol = ucm_get_mixer_volume(device,
+ PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM,
+ PA_ALSA_PROP_UCM_PLAYBACK_VOLUME,
+ "PlaybackVolume",
+ PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM,
+ PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE);
+ if (vol)
+ pa_hashmap_put(device->playback_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
}
if (device->capture_channels) { /* source device */
@@ -368,12 +405,14 @@ static int ucm_get_device_property(
pa_log_debug("UCM capture priority %s for device %s error", value, device_name);
}
- value = ucm_get_mixer_id(device,
- PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
- PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
- "CaptureVolume");
- if (value)
- pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), (void *)value);
+ vol = ucm_get_mixer_volume(device,
+ PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM,
+ PA_ALSA_PROP_UCM_CAPTURE_VOLUME,
+ "CaptureVolume",
+ PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM,
+ PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE);
+ if (vol)
+ pa_hashmap_put(device->capture_volumes, pa_xstrdup(pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME)), vol);
}
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device) || PA_UCM_CAPTURE_PRIORITY_UNSET(device)) {
@@ -478,9 +517,9 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
d->available = PA_AVAILABLE_UNKNOWN;
d->playback_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
- pa_xfree);
+ (pa_free_cb_t) ucm_volume_free);
d->capture_volumes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree,
- pa_xfree);
+ (pa_free_cb_t) ucm_volume_free);
PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
}
@@ -819,9 +858,10 @@ static void ucm_add_port_combination(
char *name, *desc;
const char *dev_name;
const char *direction;
- const char *profile, *volume_element;
+ const char *profile;
pa_alsa_ucm_device *sorted[num], *dev;
pa_alsa_ucm_port_data *data;
+ pa_alsa_ucm_volume *vol;
void *state;
for (i = 0; i < num; i++)
@@ -892,21 +932,27 @@ static void ucm_add_port_combination(
* ports. */
data = PA_DEVICE_PORT_DATA(port);
- PA_HASHMAP_FOREACH_KV(profile, volume_element, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
- pa_alsa_path *path = pa_alsa_path_synthesize(volume_element,
+ PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) {
+ pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem,
is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT);
if (!path)
- pa_log_warn("Failed to set up volume control: %s", volume_element);
+ pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem);
else {
+ if (vol->master_elem) {
+ pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false);
+ e->switch_use = PA_ALSA_SWITCH_MUTE;
+ e->volume_use = PA_ALSA_VOLUME_MERGE;
+ }
+
pa_hashmap_put(data->paths, pa_xstrdup(profile), path);
/* Add path also to already created empty path set */
dev = sorted[0];
if (is_sink)
- pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(volume_element), path);
+ pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
else
- pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(volume_element), path);
+ pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path);
}
}
}
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index d8507a83615c..a6863abc05a6 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -60,6 +60,12 @@ typedef void snd_use_case_mgr_t;
/** For devices: Playback mixer master type */
#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type"
+/** For devices: Playback mixer master identifier */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ID "alsa.ucm.playback.master.id"
+
+/** For devices: Playback mixer master type */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type"
+
/** For devices: Playback priority */
#define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY "alsa.ucm.playback.priority"
@@ -87,6 +93,12 @@ typedef void snd_use_case_mgr_t;
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type"
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ID "alsa.ucm.capture.master.id"
+
+/** For devices: Capture mixer identifier */
+#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type"
+
/** For devices: Capture priority */
#define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY "alsa.ucm.capture.priority"
@@ -114,6 +126,7 @@ typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
+typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume;
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
@@ -246,4 +259,10 @@ struct pa_alsa_ucm_port_data {
pa_alsa_path *path;
};
+struct pa_alsa_ucm_volume {
+ char *mixer_elem; /* mixer element identifier */
+ char *master_elem; /* master mixer element identifier */
+ char *master_type;
+};
+
#endif
--
2.16.4

View File

@ -0,0 +1,59 @@
From 156bd7742490d68701688572ec06f2c608f33db6 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 3 Dec 2019 14:52:08 +0100
Subject: [PATCH] alsa-ucm: split correctly JackHWMute device names
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 46d016541ddf..65b786caf95e 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -200,6 +200,32 @@ static void ucm_add_devices_to_idxset(
}
}
+/* Split a string into words. Like pa_split_spaces() but handle '' and "". */
+static char *ucm_split_devnames(const char *c, const char **state) {
+ const char *current = *state ? *state : c;
+ char h;
+ size_t l;
+
+ if (!*current || *c == 0)
+ return NULL;
+
+ current += strspn(current, "\n\r \t");
+ h = *current;
+ if (h == '\'' || h =='"') {
+ c = ++current;
+ for (l = 0; *c && *c != h; l++) c++;
+ if (*c != h)
+ return NULL;
+ *state = c + 1;
+ } else {
+ l = strcspn(current, "\n\r \t");
+ *state = current+l;
+ }
+
+ return pa_xstrndup(current, l);
+}
+
+
static void ucm_volume_free(pa_alsa_ucm_volume *vol) {
pa_assert(vol);
pa_xfree(vol->mixer_elem);
@@ -1607,7 +1633,7 @@ static int ucm_create_profile(
char *hw_mute_device_name;
const char *state = NULL;
- while ((hw_mute_device_name = pa_split_spaces(jack_hw_mute, &state))) {
+ while ((hw_mute_device_name = ucm_split_devnames(jack_hw_mute, &state))) {
pa_alsa_ucm_verb *verb2;
bool device_found = false;
--
2.16.4

View File

@ -0,0 +1,40 @@
From e04f14ebf3a0898dd2f665434524cc34cb267ddd Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 3 Dec 2019 15:13:48 +0100
Subject: [PATCH] alsa-ucm: fix parsing for JackControl
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 65b786caf95e..221fed719284 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1534,6 +1534,22 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
if (jack_control) {
+#if SND_LIB_VERSION >= 0x10201
+ snd_ctl_elem_id_t *ctl;
+ int err, index;
+ snd_ctl_elem_id_alloca(&ctl);
+ err = snd_use_case_parse_ctl_elem_id(ctl, "JackControl", jack_control);
+ if (err < 0)
+ return NULL;
+ jack_control = snd_ctl_elem_id_get_name(ctl);
+ index = snd_ctl_elem_id_get_index(ctl);
+ if (index > 0) {
+ pa_log("[%s] Invalid JackControl index value: \"%s\",%d", device_name, jack_control, index);
+ return NULL;
+ }
+#else
+#warning "Upgrade to alsa-lib 1.2.1!"
+#endif
if (!pa_endswith(jack_control, " Jack")) {
pa_log("[%s] Invalid JackControl value: \"%s\"", device_name, jack_control);
return NULL;
--
2.16.4

View File

@ -0,0 +1,45 @@
From f5c02dfcd821ab77fc7f91da985254a7bdb658ad Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Wed, 4 Dec 2019 16:29:51 +0100
Subject: [PATCH] alsa-ucm: add comments to ucm_get_mixer_id()
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 221fed719284..3ee271845c19 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -241,7 +241,7 @@ static char *ucm_get_mixer_id(
const char *cprop,
const char *cid)
{
-#if SND_LIB_VERSION >= 0x10201
+#if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
snd_ctl_elem_id_t *ctl;
int err;
#endif
@@ -249,13 +249,17 @@ static char *ucm_get_mixer_id(
char *value2;
int index;
+ /* mixer element as first, if it's found, return it without modifications */
value = pa_proplist_gets(device->proplist, mprop);
if (value)
return pa_xstrdup(value);
+ /* fallback, get the control element identifier */
+ /* and try to do some heuristic to determine the mixer element name */
value = pa_proplist_gets(device->proplist, cprop);
if (value == NULL)
return NULL;
-#if SND_LIB_VERSION >= 0x10201
+#if SND_LIB_VERSION >= 0x10201 /* alsa-lib-1.2.1+ check */
+ /* The new parser may return also element index. */
snd_ctl_elem_id_alloca(&ctl);
err = snd_use_case_parse_ctl_elem_id(ctl, cid, value);
if (err < 0)
--
2.16.4

View File

@ -0,0 +1,81 @@
From e6779ad229d5858f90f5f10c3796c9778f05c3fa Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Wed, 4 Dec 2019 19:33:01 +0100
Subject: [PATCH] alsa-ucm: validate access to PA_DEVICE_PORT_DATA()
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-sink.c | 3 ++-
src/modules/alsa/alsa-source.c | 3 +--
src/modules/alsa/module-alsa-card.c | 6 +++++-
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 0a5c92529af3..19e9efc3674b 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1672,6 +1672,7 @@ static int sink_set_port_cb(pa_sink *s, pa_device_port *p) {
pa_assert(u);
pa_assert(p);
pa_assert(u->mixer_handle);
+ pa_assert(!u->ucm_context);
data = PA_DEVICE_PORT_DATA(p);
pa_assert_se(u->mixer_path = data->path);
@@ -2688,7 +2689,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
* pa_sink_suspend() between pa_sink_new() and pa_sink_put() would
* otherwise work, but currently pa_sink_suspend() will crash if
* pa_sink_put() hasn't been called. */
- if (u->sink->active_port) {
+ if (u->sink->active_port && !u->ucm_context) {
pa_alsa_port_data *port_data;
port_data = PA_DEVICE_PORT_DATA(u->sink->active_port);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index d186101720b8..34946b74c77f 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1519,8 +1519,6 @@ static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
struct userdata *u = s->userdata;
pa_alsa_ucm_port_data *data;
- data = PA_DEVICE_PORT_DATA(p);
-
pa_assert(u);
pa_assert(p);
pa_assert(u->mixer_handle);
@@ -1545,6 +1543,7 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
pa_assert(u);
pa_assert(p);
pa_assert(u->mixer_handle);
+ pa_assert(!u->ucm_context);
data = PA_DEVICE_PORT_DATA(p);
pa_assert_se(u->mixer_path = data->path);
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index e2a86bc1c68d..be260e4badab 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -538,6 +538,9 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) {
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
+ if (u->use_ucm)
+ return 0;
+
p = find_port_with_eld_device(u->card->ports, device);
if (p == NULL) {
pa_log_error("Invalid device changed in ALSA: %d", device);
@@ -900,7 +903,8 @@ int pa__init(pa_module *m) {
* results in an infinite loop of "fill buffer, handle underrun". To work
* around this issue, the suspend_when_unavailable flag is used to stop
* playback when the HDMI cable is unplugged. */
- if (pa_safe_streq(pa_proplist_gets(data.proplist, "alsa.driver_name"), "snd_hdmi_lpe_audio")) {
+ if (!u->use_ucm &&
+ pa_safe_streq(pa_proplist_gets(data.proplist, "alsa.driver_name"), "snd_hdmi_lpe_audio")) {
pa_device_port *port;
void *state;
--
2.16.4

View File

@ -0,0 +1,65 @@
From 734a00c849815a45697970d593068c301a04ebbb Mon Sep 17 00:00:00 2001
From: Kai-Heng Feng <kai.heng.feng@canonical.com>
Date: Tue, 10 Dec 2019 16:16:18 +0800
Subject: [PATCH] alsa: Skip resume PCM if hardware doesn't support it
Hardwares without SNDRV_PCM_INFO_RESUME capability, like USB Audio,
don't support snd_pcm_resume() when it's in suspended state.
Let's use snd_pcm_hw_params_can_resume() to check hardware's capability
before snd_pcm_resume() attempt. If it doesn't support resume, just go
to snd_pcm_drop() to leave suspended state directly.
---
src/modules/alsa/alsa-util.c | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index bd0a47e5072c..a14b061118e6 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1066,6 +1066,7 @@ void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) {
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
snd_pcm_state_t state;
+ snd_pcm_hw_params_t *hwparams;
int err;
pa_assert(pcm);
@@ -1103,16 +1104,25 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
break;
case SND_PCM_STATE_SUSPENDED:
- /* Retry resume 3 times before giving up, then fallback to restarting the stream. */
- for (int i = 0; i < 3; i++) {
- if ((err = snd_pcm_resume(pcm)) == 0)
- return 0;
- if (err != -EAGAIN)
- break;
- pa_msleep(25);
+ snd_pcm_hw_params_alloca(&hwparams);
+
+ if ((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(err));
+ return -1;
}
- pa_log_warn("Could not recover alsa device from SUSPENDED state, trying to restart PCM");
- /* Fall through */
+
+ if (snd_pcm_hw_params_can_resume(hwparams)) {
+ /* Retry resume 3 times before giving up, then fallback to restarting the stream. */
+ for (int i = 0; i < 3; i++) {
+ if ((err = snd_pcm_resume(pcm)) == 0)
+ return 0;
+ if (err != -EAGAIN)
+ break;
+ pa_msleep(25);
+ }
+ pa_log_warn("Could not recover alsa device from SUSPENDED state, trying to restart PCM");
+ }
+ /* Fall through */
default:
--
2.16.4

View File

@ -0,0 +1,125 @@
From 4c64f73c97c7f77426ee838f47fc7bad6a4563b6 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 6 Dec 2019 21:51:47 +0100
Subject: [PATCH] alsa-ucm: parse correctly the device values
The UCM library is used to get the fallback values from the verbs
and the defaults section. There is no reason to duplicate this code
inside application.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 55 ++++++---------------------------------------
1 file changed, 7 insertions(+), 48 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 3ee271845c19..ac1b71e94022 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -141,30 +141,6 @@ static struct ucm_info dev_info[] = {
{NULL, 0}
};
-/* UCM profile properties - The verb data is store so it can be used to fill
- * the new profiles properties */
-static int ucm_get_property(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr, const char *verb_name) {
- const char *value;
- char *id;
- int i;
-
- for (i = 0; item[i].id; i++) {
- int err;
-
- id = pa_sprintf_malloc("=%s//%s", item[i].id, verb_name);
- err = snd_use_case_get(uc_mgr, id, &value);
- pa_xfree(id);
- if (err < 0)
- continue;
-
- pa_log_debug("Got %s for verb %s: %s", item[i].id, verb_name, value);
- pa_proplist_sets(verb->proplist, item[i].property, value);
- free((void*)value);
- }
-
- return 0;
-};
-
static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
pa_alsa_ucm_device *d;
uint32_t idx;
@@ -325,7 +301,7 @@ static int ucm_get_device_property(
pa_alsa_ucm_volume *vol;
for (i = 0; item[i].id; i++) {
- id = pa_sprintf_malloc("=%s/%s", item[i].id, device_name);
+ id = pa_sprintf_malloc("%s/%s", item[i].id, device_name);
err = snd_use_case_get(uc_mgr, id, &value);
pa_xfree(id);
if (err < 0)
@@ -347,14 +323,8 @@ static int ucm_get_device_property(
/* get pcm */
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SINK);
- if (!value) { /* take pcm from verb playback default */
- value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SINK);
- if (value) {
- pa_log_debug("UCM playback device %s fetch pcm from verb default %s", device_name, value);
- pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SINK, value);
- } else
- pa_log("UCM playback device %s fetch pcm failed", device_name);
- }
+ if (!value) /* take pcm from verb playback default */
+ pa_log("UCM playback device %s fetch pcm failed", device_name);
}
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
@@ -367,14 +337,8 @@ static int ucm_get_device_property(
/* get pcm */
value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_SOURCE);
- if (!value) { /* take pcm from verb capture default */
- value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_SOURCE);
- if (value) {
- pa_log_debug("UCM capture device %s fetch pcm from verb default %s", device_name, value);
- pa_proplist_sets(device->proplist, PA_ALSA_PROP_UCM_SOURCE, value);
- } else
- pa_log("UCM capture device %s fetch pcm failed", device_name);
- }
+ if (!value) /* take pcm from verb capture default */
+ pa_log("UCM capture device %s fetch pcm failed", device_name);
}
if (device->playback_channels == 0 && device->capture_channels == 0) {
@@ -387,8 +351,7 @@ static int ucm_get_device_property(
/* get rate and priority of device */
if (device->playback_channels) { /* sink device */
/* get rate */
- if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE)) ||
- (value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
+ if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_PLAYBACK_RATE))) {
if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
pa_log_debug("UCM playback device %s rate %d", device_name, ui);
device->playback_rate = ui;
@@ -417,8 +380,7 @@ static int ucm_get_device_property(
if (device->capture_channels) { /* source device */
/* get rate */
- if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE)) ||
- (value = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
+ if ((value = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_CAPTURE_RATE))) {
if (pa_atou(value, &ui) == 0 && pa_sample_rate_valid(ui)) {
pa_log_debug("UCM capture device %s rate %d", device_name, ui);
device->capture_rate = ui;
@@ -796,9 +758,6 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, cons
if (err < 0)
pa_log("No UCM modifiers for verb %s", verb_name);
- /* Verb properties */
- ucm_get_property(verb, uc_mgr, verb_name);
-
PA_LLIST_FOREACH(d, verb->devices) {
const char *dev_name = pa_proplist_gets(d->proplist, PA_ALSA_PROP_UCM_NAME);
--
2.16.4

View File

@ -0,0 +1,44 @@
From ef1df946274a0499e1fa631a8b6680c23c4eb723 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 6 Dec 2019 15:43:04 +0100
Subject: [PATCH] alsa-ucm: do not try to use UCM device name as jack name by
default
Remove the implicit rule. It is perfectly ok to have the jack with
the same name for another I/O in the driver. Trust only the
value obtained from UCM.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index ac1b71e94022..95f1a47f8b61 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1522,9 +1522,8 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
* end, so drop the trailing " Jack". */
name = pa_xstrndup(jack_control, strlen(jack_control) - 5);
} else {
- /* The jack control hasn't been explicitly configured - try a jack name
- * that is the same as the device name. */
- name = pa_xstrdup(device_name);
+ /* The jack control hasn't been explicitly configured, fail. */
+ return NULL;
}
PA_LLIST_FOREACH(j, ucm->jacks)
@@ -1603,7 +1602,8 @@ static int ucm_create_profile(
ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
jack = ucm_get_jack(ucm, dev);
- device_set_jack(dev, jack);
+ if (jack)
+ device_set_jack(dev, jack);
/* JackHWMute contains a list of device names. Each listed device must
* be associated with the jack object that we just created. */
--
2.16.4

View File

@ -0,0 +1,84 @@
From d8200ee805ed6b508a8174031080b1d98a7c27b3 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 6 Dec 2019 16:05:07 +0100
Subject: [PATCH] alsa-util: do not try to guess the mixer name from the PCM
name
This is just invalid. It results to an error in almost all cases.
The hw:<number> scheme is sufficient to get the right card mixer.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 2 ++
src/modules/alsa/alsa-util.c | 26 +++++++-------------------
2 files changed, 9 insertions(+), 19 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 95f1a47f8b61..45eb83085b38 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1725,6 +1725,8 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
bool has_control;
+ if (!dev->jack)
+ continue;
has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0) != NULL;
pa_alsa_jack_set_has_control(dev->jack, has_control);
pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index a14b061118e6..54fe1361148a 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1743,7 +1743,6 @@ snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device) {
snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
int err;
snd_mixer_t *m;
- const char *dev;
snd_pcm_info_t* info;
snd_pcm_info_alloca(&info);
@@ -1754,15 +1753,6 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
return NULL;
}
- /* First, try by name */
- if ((dev = snd_pcm_name(pcm)))
- if (prepare_mixer(m, dev) >= 0) {
- if (ctl_device)
- *ctl_device = pa_xstrdup(dev);
-
- return m;
- }
-
/* Then, try by card index */
if (snd_pcm_info(pcm, info) >= 0) {
char *md;
@@ -1771,17 +1761,15 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
md = pa_sprintf_malloc("hw:%i", card_idx);
+ if (prepare_mixer(m, md) >= 0) {
- if (!dev || !pa_streq(dev, md))
- if (prepare_mixer(m, md) >= 0) {
+ if (ctl_device)
+ *ctl_device = md;
+ else
+ pa_xfree(md);
- if (ctl_device)
- *ctl_device = md;
- else
- pa_xfree(md);
-
- return m;
- }
+ return m;
+ }
pa_xfree(md);
}
--
2.16.4

View File

@ -0,0 +1,99 @@
From ddd0fdb9970b920ef95e33cfe50a1e492be9d60b Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 6 Dec 2019 20:33:45 +0100
Subject: [PATCH] alsa-ucm: add control and mixer device items
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 5 +++++
src/modules/alsa/alsa-ucm.h | 15 +++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 45eb83085b38..2a594b4df546 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -90,16 +90,20 @@ static void ucm_port_update_available(pa_alsa_ucm_port_data *port);
static struct ucm_items item[] = {
{"PlaybackPCM", PA_ALSA_PROP_UCM_SINK},
{"CapturePCM", PA_ALSA_PROP_UCM_SOURCE},
+ {"PlaybackCTL", PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE},
{"PlaybackVolume", PA_ALSA_PROP_UCM_PLAYBACK_VOLUME},
{"PlaybackSwitch", PA_ALSA_PROP_UCM_PLAYBACK_SWITCH},
+ {"PlaybackMixer", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE},
{"PlaybackMixerElem", PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM},
{"PlaybackMasterElem", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM},
{"PlaybackMasterType", PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE},
{"PlaybackPriority", PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY},
{"PlaybackRate", PA_ALSA_PROP_UCM_PLAYBACK_RATE},
{"PlaybackChannels", PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS},
+ {"CaptureCTL", PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE},
{"CaptureVolume", PA_ALSA_PROP_UCM_CAPTURE_VOLUME},
{"CaptureSwitch", PA_ALSA_PROP_UCM_CAPTURE_SWITCH},
+ {"CaptureMixer", PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE},
{"CaptureMixerElem", PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM},
{"CaptureMasterElem", PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM},
{"CaptureMasterType", PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE},
@@ -107,6 +111,7 @@ static struct ucm_items item[] = {
{"CaptureRate", PA_ALSA_PROP_UCM_CAPTURE_RATE},
{"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
{"TQ", PA_ALSA_PROP_UCM_QOS},
+ {"JackCTL", PA_ALSA_PROP_UCM_JACK_DEVICE},
{"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
{"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
{NULL, NULL},
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index a6863abc05a6..014bc334ad67 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -45,12 +45,18 @@ typedef void snd_use_case_mgr_t;
/** For devices: Playback roles */
#define PA_ALSA_PROP_UCM_PLAYBACK_ROLES "alsa.ucm.playback.roles"
+/** For devices: Playback control device name */
+#define PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE "alsa.ucm.playback.ctldev"
+
/** For devices: Playback control volume ID string. e.g PlaybackVolume */
#define PA_ALSA_PROP_UCM_PLAYBACK_VOLUME "alsa.ucm.playback.volume"
/** For devices: Playback switch e.g PlaybackSwitch */
#define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH "alsa.ucm.playback.switch"
+/** For devices: Playback mixer device name */
+#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE "alsa.ucm.playback.mixer.device"
+
/** For devices: Playback mixer identifier */
#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM "alsa.ucm.playback.mixer.element"
@@ -78,12 +84,18 @@ typedef void snd_use_case_mgr_t;
/** For devices: Capture roles */
#define PA_ALSA_PROP_UCM_CAPTURE_ROLES "alsa.ucm.capture.roles"
+/** For devices: Capture control device name */
+#define PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE "alsa.ucm.capture.ctldev"
+
/** For devices: Capture controls volume ID string. e.g CaptureVolume */
#define PA_ALSA_PROP_UCM_CAPTURE_VOLUME "alsa.ucm.capture.volume"
/** For devices: Capture switch e.g CaptureSwitch */
#define PA_ALSA_PROP_UCM_CAPTURE_SWITCH "alsa.ucm.capture.switch"
+/** For devices: Capture mixer device name */
+#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE "alsa.ucm.capture.mixer.device"
+
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM "alsa.ucm.capture.mixer.element"
@@ -114,6 +126,9 @@ typedef void snd_use_case_mgr_t;
/** For devices: The modifier (if any) that this device corresponds to */
#define PA_ALSA_PROP_UCM_MODIFIER "alsa.ucm.modifier"
+/* Corresponds to the "JackCTL" UCM value. */
+#define PA_ALSA_PROP_UCM_JACK_DEVICE "alsa.ucm.jack_device"
+
/* Corresponds to the "JackControl" UCM value. */
#define PA_ALSA_PROP_UCM_JACK_CONTROL "alsa.ucm.jack_control"
--
2.16.4

View File

@ -0,0 +1,244 @@
From e438382a51f7e0d04fb9439da2f45c183e958e0f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 6 Dec 2019 21:32:21 +0100
Subject: [PATCH] alsa-ucm: get the mixer names from ucm, don't guess
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.h | 1 +
src/modules/alsa/alsa-ucm.c | 96 +++++++++++++++++++++++++++++++++++--------
src/modules/alsa/alsa-util.c | 26 ++++++++----
3 files changed, 97 insertions(+), 26 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 0b634f2f2be1..7864a2c1252f 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -364,6 +364,7 @@ void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
+snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev);
snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device);
pa_alsa_fdlist *pa_alsa_fdlist_new(void);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 2a594b4df546..d02adab650fe 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -289,6 +289,31 @@ static pa_alsa_ucm_volume *ucm_get_mixer_volume(
return vol;
}
+/* Get the ALSA mixer device for the UCM device */
+static const char *get_mixer_device(pa_alsa_ucm_device *dev, bool is_sink)
+{
+ const char *dev_name;
+
+ if (is_sink) {
+ dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE);
+ if (!dev_name)
+ dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE);
+ } else {
+ dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE);
+ if (!dev_name)
+ dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE);
+ }
+ return dev_name;
+}
+
+/* Get the ALSA mixer device for the UCM jack */
+static const char *get_jack_mixer_device(pa_alsa_ucm_device *dev, bool is_sink) {
+ const char *dev_name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_JACK_DEVICE);
+ if (!dev_name)
+ return get_mixer_device(dev, is_sink);
+ return dev_name;
+}
+
/* Create a property list for this ucm device */
static int ucm_get_device_property(
pa_alsa_ucm_device *device,
@@ -795,22 +820,41 @@ static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
}
-static void probe_volumes(pa_hashmap *hash, snd_pcm_t *pcm_handle, bool ignore_dB) {
+static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, bool ignore_dB) {
pa_device_port *port;
pa_alsa_path *path;
pa_alsa_ucm_port_data *data;
- snd_mixer_t *mixer_handle;
- const char *profile;
+ pa_alsa_ucm_device *dev;
+ snd_mixer_t *mixer_handle = NULL;
+ const char *profile, *mdev_opened = NULL, *mdev, *mdev2;
void *state, *state2;
-
- if (!(mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL))) {
- pa_log_error("Failed to find a working mixer device.");
- goto fail;
- }
+ int idx;
PA_HASHMAP_FOREACH(port, hash, state) {
data = PA_DEVICE_PORT_DATA(port);
+ mdev = NULL;
+ PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
+ mdev2 = get_mixer_device(dev, is_sink);
+ if (mdev && !pa_streq(mdev, mdev2)) {
+ pa_log_error("Two mixer device names found ('%s', '%s'), using s/w volume", mdev, mdev2);
+ goto fail;
+ }
+ mdev = mdev2;
+ }
+
+ if (!mdev_opened || !pa_streq(mdev_opened, mdev)) {
+ if (mixer_handle) {
+ snd_mixer_close(mixer_handle);
+ mdev_opened = NULL;
+ }
+ if (!(mixer_handle = pa_alsa_open_mixer_by_name(mdev))) {
+ pa_log_error("Failed to find a working mixer device (%s).", mdev);
+ goto fail;
+ }
+ mdev_opened = mdev;
+ }
+
PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
if (pa_alsa_path_probe(path, NULL, mixer_handle, ignore_dB) < 0) {
pa_log_warn("Could not probe path: %s, using s/w volume", data->path->name);
@@ -823,7 +867,8 @@ static void probe_volumes(pa_hashmap *hash, snd_pcm_t *pcm_handle, bool ignore_d
}
}
- snd_mixer_close(mixer_handle);
+ if (mixer_handle)
+ snd_mixer_close(mixer_handle);
return;
@@ -1149,7 +1194,7 @@ void pa_alsa_ucm_add_ports(
pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
/* now set up volume paths if any */
- probe_volumes(*p, pcm_handle, ignore_dB);
+ probe_volumes(*p, is_sink, pcm_handle, ignore_dB);
/* then set property PA_PROP_DEVICE_INTENDED_ROLES */
merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
@@ -1716,28 +1761,43 @@ static void profile_finalize_probing(pa_alsa_profile *p) {
}
static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
- snd_pcm_t *pcm_handle;
- snd_mixer_t *mixer_handle;
+ snd_mixer_t *mixer_handle = NULL;
pa_alsa_ucm_mapping_context *context = &m->ucm_context;
pa_alsa_ucm_device *dev;
+ bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
+ const char *mdev_opened = NULL, *mdev;
uint32_t idx;
- pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : m->input_pcm;
- mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
- if (!mixer_handle)
- return;
-
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
bool has_control;
if (!dev->jack)
continue;
+
+ mdev = get_jack_mixer_device(dev, is_sink);
+ if (mdev == NULL) {
+ pa_log_error("Unable to determine mixer device for jack %s", dev->jack->name);
+ continue;
+ }
+
+ if (!mdev_opened || !pa_streq(mdev_opened, mdev)) {
+ if (mixer_handle) {
+ snd_mixer_close(mixer_handle);
+ mdev_opened = NULL;
+ }
+ mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+ if (!mixer_handle)
+ continue;
+ mdev_opened = mdev;
+ }
+
has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0) != NULL;
pa_alsa_jack_set_has_control(dev->jack, has_control);
pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
}
- snd_mixer_close(mixer_handle);
+ if (mixer_handle)
+ snd_mixer_close(mixer_handle);
}
static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 54fe1361148a..2df2258d85af 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1740,20 +1740,31 @@ snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device) {
return NULL;
}
-snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
+snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev) {
int err;
snd_mixer_t *m;
- snd_pcm_info_t* info;
- snd_pcm_info_alloca(&info);
- pa_assert(pcm);
+ pa_assert(dev);
if ((err = snd_mixer_open(&m, 0)) < 0) {
pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
return NULL;
}
- /* Then, try by card index */
+ if (prepare_mixer(m, dev) >= 0)
+ return m;
+
+ snd_mixer_close(m);
+ return NULL;
+}
+
+snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
+ snd_mixer_t *m;
+ snd_pcm_info_t* info;
+ snd_pcm_info_alloca(&info);
+
+ pa_assert(pcm);
+
if (snd_pcm_info(pcm, info) >= 0) {
char *md;
int card_idx;
@@ -1761,8 +1772,8 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
md = pa_sprintf_malloc("hw:%i", card_idx);
- if (prepare_mixer(m, md) >= 0) {
-
+ m = pa_alsa_open_mixer_by_name(md);
+ if (m) {
if (ctl_device)
*ctl_device = md;
else
@@ -1775,7 +1786,6 @@ snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
}
}
- snd_mixer_close(m);
return NULL;
}
--
2.16.4

View File

@ -0,0 +1,93 @@
From dacfcbb09c9d91ca20dedfa449da37f0f7e3953f Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 7 Dec 2019 11:50:13 +0100
Subject: [PATCH] alsa-ucm: use the proper mixer name for ucm pcm sink/source
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-sink.c | 9 ++++++++-
src/modules/alsa/alsa-source.c | 10 +++++++++-
src/modules/alsa/alsa-ucm.c | 9 +++++++--
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 19e9efc3674b..5ea4b2597449 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -2083,11 +2083,18 @@ static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *de
}
static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, bool ignore_dB) {
+ const char *mdev;
if (!mapping && !element)
return;
- if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) {
+ mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
+ if (mdev) {
+ u->mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+ } else {
+ u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device);
+ }
+ if (!mdev) {
pa_log_info("Failed to find a working mixer device.");
return;
}
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 34946b74c77f..bd78d45e2a5b 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1789,10 +1789,18 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char
}
static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, bool ignore_dB) {
+ const char *mdev;
+
if (!mapping && !element)
return;
- if (!(u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device))) {
+ mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
+ if (mdev) {
+ u->mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+ } else {
+ u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device);
+ }
+ if (!mdev) {
pa_log_info("Failed to find a working mixer device.");
return;
}
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index d02adab650fe..d1ca62d28619 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1339,7 +1339,8 @@ static void ucm_add_mapping(pa_alsa_profile *p, pa_alsa_mapping *m) {
static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *device) {
char *cur_desc;
- const char *new_desc;
+ const char *new_desc, *mdev;
+ bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
pa_idxset_put(m->ucm_context.ucm_devices, device, NULL);
@@ -1355,10 +1356,14 @@ static void alsa_mapping_add_ucm_device(pa_alsa_mapping *m, pa_alsa_ucm_device *
m->description = m->description ? m->description : pa_xstrdup("");
/* save mapping to ucm device */
- if (m->direction == PA_ALSA_DIRECTION_OUTPUT)
+ if (is_sink)
device->playback_mapping = m;
else
device->capture_mapping = m;
+
+ mdev = get_mixer_device(device, is_sink);
+ if (mdev)
+ pa_proplist_sets(m->proplist, "alsa.mixer_device", mdev);
}
static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
--
2.16.4

View File

@ -0,0 +1,125 @@
From f18b0c3402f5e1f7db9d0a42c6e10cfe1f212da3 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 7 Dec 2019 17:54:04 +0100
Subject: [PATCH] alsa-mixer: handle interface type (CARD,PCM) for mixer
element lookups
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.c | 2 +-
src/modules/alsa/alsa-ucm.c | 2 +-
src/modules/alsa/alsa-util.c | 18 +++++++++++++++++-
src/modules/alsa/alsa-util.h | 3 ++-
src/modules/alsa/module-alsa-card.c | 4 ++--
5 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index ed06f42d86da..094cff817df7 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -1907,7 +1907,7 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m)
j->append_pcm_to_name = false;
}
- has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL;
+ has_control = pa_alsa_mixer_find_card(m, j->alsa_name, 0) != NULL;
pa_alsa_jack_set_has_control(j, has_control);
if (j->has_control) {
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index d1ca62d28619..a64505a0c781 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -1796,7 +1796,7 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
mdev_opened = mdev;
}
- has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0) != NULL;
+ has_control = pa_alsa_mixer_find_card(mixer_handle, dev->jack->alsa_name, 0) != NULL;
pa_alsa_jack_set_has_control(dev->jack, has_control);
pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
}
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 2df2258d85af..08a144782394 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1610,7 +1610,11 @@ bool pa_alsa_may_tsched(bool want) {
#define SND_MIXER_ELEM_PULSEAUDIO (SND_MIXER_ELEM_LAST + 10)
-snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device) {
+static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer,
+ snd_ctl_elem_iface_t iface,
+ const char *name,
+ unsigned int index,
+ unsigned int device) {
snd_mixer_elem_t *elem;
for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) {
@@ -1618,8 +1622,12 @@ snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsig
if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_PULSEAUDIO)
continue;
helem = snd_mixer_elem_get_private(elem);
+ if (snd_hctl_elem_get_interface(helem) != iface)
+ continue;
if (!pa_streq(snd_hctl_elem_get_name(helem), name))
continue;
+ if (snd_hctl_elem_get_index(helem) != index)
+ continue;
if (snd_hctl_elem_get_device(helem) != device)
continue;
return elem;
@@ -1627,6 +1635,14 @@ snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsig
return NULL;
}
+snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device) {
+ return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, name, 0, device);
+}
+
+snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) {
+ return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_PCM, name, 0, device);
+}
+
static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
{
/* Dummy compare function */
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 4ceaa06ee480..ceca48809400 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -141,7 +141,8 @@ const char* pa_alsa_strerror(int errnum);
bool pa_alsa_may_tsched(bool want);
-snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer, const char *name, unsigned int device);
+snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device);
+snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device);
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index be260e4badab..ba7a19a38983 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -588,7 +588,7 @@ static void init_eld_ctls(struct userdata *u) {
if (device < 0)
continue;
- melem = pa_alsa_mixer_find(u->mixer_handle, "ELD", device);
+ melem = pa_alsa_mixer_find_pcm(u->mixer_handle, "ELD", device);
if (melem) {
snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
snd_mixer_elem_set_callback_private(melem, u);
@@ -635,7 +635,7 @@ static void init_jacks(struct userdata *u) {
u->mixer_handle = pa_alsa_open_mixer(u->alsa_card_index, NULL);
if (u->mixer_handle && pa_alsa_fdlist_set_handle(u->mixer_fdl, u->mixer_handle, NULL, u->core->mainloop) >= 0) {
PA_HASHMAP_FOREACH(jack, u->jacks, state) {
- jack->melem = pa_alsa_mixer_find(u->mixer_handle, jack->alsa_name, 0);
+ jack->melem = pa_alsa_mixer_find_card(u->mixer_handle, jack->alsa_name, 0);
if (!jack->melem) {
pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
pa_alsa_jack_set_has_control(jack, false);
--
2.16.4

View File

@ -0,0 +1,51 @@
From c6a0665618975eedc98bdf23e4140935a1af38c2 Mon Sep 17 00:00:00 2001
From: Laurent Bigonville <bigon@bigon.be>
Date: Thu, 7 Mar 2019 11:35:30 +0100
Subject: [PATCH] alsa-mixer: Add the ability to pass the intended-role to the
mapping
https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/640
---
src/modules/alsa/alsa-mixer.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 094cff817df7..7956371b604b 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -3978,6 +3978,24 @@ static int mapping_parse_fallback(pa_config_parser_state *state) {
return 0;
}
+static int mapping_parse_intended_roles(pa_config_parser_state *state) {
+ pa_alsa_profile_set *ps;
+ pa_alsa_mapping *m;
+
+ pa_assert(state);
+
+ ps = state->userdata;
+
+ if (!(m = pa_alsa_mapping_get(ps, state->section))) {
+ pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+ return -1;
+ }
+
+ pa_proplist_sets(m->proplist, PA_PROP_DEVICE_INTENDED_ROLES, state->rvalue);
+
+ return 0;
+}
+
static int profile_parse_mappings(pa_config_parser_state *state) {
pa_alsa_profile_set *ps;
@@ -4569,6 +4587,7 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
{ "element-output", mapping_parse_element, NULL, NULL },
{ "direction", mapping_parse_direction, NULL, NULL },
{ "exact-channels", mapping_parse_exact_channels, NULL, NULL },
+ { "intended-roles", mapping_parse_intended_roles, NULL, NULL },
/* Shared by [Mapping ...] and [Profile ...] */
{ "description", mapping_parse_description, NULL, NULL },
--
2.16.4

View File

@ -0,0 +1,39 @@
From 6438e5c46dc449e4726ec9312859cc70388d2851 Mon Sep 17 00:00:00 2001
From: Laurent Bigonville <bigon@bigon.be>
Date: Thu, 7 Mar 2019 11:36:02 +0100
Subject: [PATCH] alsa-mixer: Set the intended-role of Steelseries Arctis 5/7
headset as phone
Fixes: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/640
---
.../alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf | 1 +
src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
index 80c33707aea4..5f11ed1e26ef 100644
--- a/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
+++ b/src/modules/alsa/mixer/profile-sets/steelseries-arctis-common-usb-audio.conf
@@ -7,6 +7,7 @@ device-strings = hw:%f,0,0
channel-map = left,right
paths-input = analog-input-mic
paths-output = steelseries-arctis-output-chat-common
+intended-roles = phone
[Mapping analog-game]
description = Game
diff --git a/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
index 01ecf864bcf2..f48b44f03767 100644
--- a/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
+++ b/src/modules/alsa/mixer/profile-sets/usb-gaming-headset.conf
@@ -34,6 +34,7 @@ device-strings = hw:%f,0,0
channel-map = mono
paths-output = usb-gaming-headset-output-mono
paths-input = usb-gaming-headset-input
+intended-roles = phone
[Mapping analog-stereo]
device-strings = hw:%f,1,0
--
2.16.4

View File

@ -0,0 +1,761 @@
From 3bd7c70c518d66707cbfde138ab7dcc505e463ac Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 7 Dec 2019 20:39:21 +0100
Subject: [PATCH] alsa: rewrite mixer open/close, cache mixer accesses in probe
The ALSA mixer can be opened multiple times (especially for UCM
in the probe). This adds a simple mixer cache to prevent
multiple open calls.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.c | 19 ++++----
src/modules/alsa/alsa-mixer.h | 19 ++++++--
src/modules/alsa/alsa-sink.c | 21 ++++----
src/modules/alsa/alsa-source.c | 21 ++++----
src/modules/alsa/alsa-ucm.c | 66 ++++++++++----------------
src/modules/alsa/alsa-ucm.h | 1 +
src/modules/alsa/alsa-util.c | 95 +++++++++++++++++++------------------
src/modules/alsa/alsa-util.h | 6 ++-
src/modules/alsa/module-alsa-card.c | 76 ++++++++++++++++++-----------
9 files changed, 175 insertions(+), 149 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 7956371b604b..a3c998b654e9 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -147,13 +147,14 @@ static int alsa_id_decode(const char *src, char *name, int *index) {
return 0;
}
-pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name) {
+pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name) {
pa_alsa_jack *jack;
pa_assert(name);
jack = pa_xnew0(pa_alsa_jack, 1);
jack->path = path;
+ jack->mixer_device_name = pa_xstrdup(mixer_device_name);
jack->name = pa_xstrdup(name);
jack->alsa_name = pa_sprintf_malloc("%s Jack", name);
jack->state_unplugged = PA_AVAILABLE_NO;
@@ -172,6 +173,7 @@ void pa_alsa_jack_free(pa_alsa_jack *jack) {
pa_xfree(jack->alsa_name);
pa_xfree(jack->name);
+ pa_xfree(jack->mixer_device_name);
pa_xfree(jack);
}
@@ -1982,7 +1984,7 @@ static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
if (pa_streq(j->name, section))
goto finish;
- j = pa_alsa_jack_new(p, section);
+ j = pa_alsa_jack_new(p, NULL, section);
PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
finish:
@@ -4160,7 +4162,8 @@ fail:
}
static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
- pa_alsa_direction_t direction, pa_hashmap *used_paths) {
+ pa_alsa_direction_t direction, pa_hashmap *used_paths,
+ pa_hashmap *mixers) {
pa_alsa_path *p;
void *state;
@@ -4185,7 +4188,7 @@ static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
pa_assert(pcm_handle);
- mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
+ mixer_handle = pa_alsa_open_mixer_for_pcm(mixers, pcm_handle, true);
if (!mixer_handle) {
/* Cannot open mixer, remove all entries */
pa_hashmap_remove_all(ps->paths);
@@ -4203,9 +4206,6 @@ static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
path_set_condense(ps, mixer_handle);
path_set_make_path_descriptions_unique(ps);
- if (mixer_handle)
- snd_mixer_close(mixer_handle);
-
PA_HASHMAP_FOREACH(p, ps->paths, state)
pa_hashmap_put(used_paths, p, p);
@@ -4785,6 +4785,7 @@ static void mapping_query_hw_device(pa_alsa_mapping *mapping, snd_pcm_t *pcm) {
void pa_alsa_profile_set_probe(
pa_alsa_profile_set *ps,
+ pa_hashmap *mixers,
const char *dev_id,
const pa_sample_spec *ss,
unsigned default_n_fragments,
@@ -4914,14 +4915,14 @@ void pa_alsa_profile_set_probe(
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
if (m->output_pcm) {
found_output |= !p->fallback_output;
- mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths);
+ mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths, mixers);
}
if (p->input_mappings)
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
if (m->input_pcm) {
found_input |= !p->fallback_input;
- mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths);
+ mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths, mixers);
}
}
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 7864a2c1252f..325b0c998b02 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -32,6 +32,7 @@
#include <pulsecore/rtpoll.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
+typedef struct pa_alsa_mixer pa_alsa_mixer;
typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata;
typedef struct pa_alsa_setting pa_alsa_setting;
typedef struct pa_alsa_mixer_id pa_alsa_mixer_id;
@@ -98,6 +99,13 @@ struct pa_alsa_setting {
unsigned priority;
};
+/* An entry for one ALSA mixer */
+struct pa_alsa_mixer {
+ snd_mixer_t *mixer_handle;
+ pa_alsa_fdlist *fdl;
+ bool used_for_probe_only:1;
+};
+
/* ALSA mixer element identifier */
struct pa_alsa_mixer_id {
char *name;
@@ -165,6 +173,9 @@ struct pa_alsa_jack {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_jack);
+ snd_mixer_t *mixer_handle;
+ char *mixer_device_name;
+
char *name; /* E g "Headphone" */
char *alsa_name; /* E g "Headphone Jack" */
bool has_control; /* is the jack itself present? */
@@ -182,7 +193,7 @@ struct pa_alsa_jack {
bool append_pcm_to_name;
};
-pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name);
+pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name);
void pa_alsa_jack_free(pa_alsa_jack *jack);
void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control);
void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);
@@ -201,6 +212,7 @@ struct pa_alsa_path {
char *description;
unsigned priority;
bool autodetect_eld_device;
+ pa_alsa_mixer *eld_mixer_handle;
int eld_device;
pa_proplist *proplist;
@@ -359,14 +371,11 @@ void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix);
pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name);
pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus);
-void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
+void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, pa_hashmap *mixers, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
-snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev);
-snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device);
-
pa_alsa_fdlist *pa_alsa_fdlist_new(void);
void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl);
int pa_alsa_fdlist_set_handle(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api* m);
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 5ea4b2597449..363b4be2fa25 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -105,6 +105,7 @@ struct userdata {
char *paths_dir;
pa_alsa_fdlist *mixer_fdl;
pa_alsa_mixer_pdata *mixer_pd;
+ pa_hashmap *mixers;
snd_mixer_t *mixer_handle;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
@@ -2088,13 +2089,16 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
if (!mapping && !element)
return;
+ u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+ NULL, (pa_free_cb_t) pa_alsa_mixer_free);
+
mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
if (mdev) {
- u->mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+ u->mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, mdev, true);
} else {
- u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device);
+ u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->mixers, u->pcm_handle, true);
}
- if (!mdev) {
+ if (!u->mixer_handle) {
pa_log_info("Failed to find a working mixer device.");
return;
}
@@ -2121,10 +2125,9 @@ fail:
u->mixer_path = NULL;
}
- if (u->mixer_handle) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
+ u->mixer_handle = NULL;
+ pa_hashmap_free(u->mixers);
+ u->mixers = NULL;
}
static int setup_mixer(struct userdata *u, bool ignore_dB) {
@@ -2759,8 +2762,8 @@ static void userdata_free(struct userdata *u) {
if (u->mixer_path && !u->mixer_path_set && !u->ucm_context)
pa_alsa_path_free(u->mixer_path);
- if (u->mixer_handle)
- snd_mixer_close(u->mixer_handle);
+ if (u->mixers)
+ pa_hashmap_free(u->mixers);
if (u->smoother)
pa_smoother_free(u->smoother);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index bd78d45e2a5b..b46e845cc5a7 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -93,6 +93,7 @@ struct userdata {
char *paths_dir;
pa_alsa_fdlist *mixer_fdl;
pa_alsa_mixer_pdata *mixer_pd;
+ pa_hashmap *mixers;
snd_mixer_t *mixer_handle;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
@@ -1794,13 +1795,16 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
if (!mapping && !element)
return;
+ u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+ NULL, (pa_free_cb_t) pa_alsa_mixer_free);
+
mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
if (mdev) {
- u->mixer_handle = pa_alsa_open_mixer_by_name(mdev);
+ u->mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, mdev, false);
} else {
- u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->pcm_handle, &u->control_device);
+ u->mixer_handle = pa_alsa_open_mixer_for_pcm(u->mixers, u->pcm_handle, false);
}
- if (!mdev) {
+ if (!u->mixer_handle) {
pa_log_info("Failed to find a working mixer device.");
return;
}
@@ -1827,10 +1831,9 @@ fail:
u->mixer_path = NULL;
}
- if (u->mixer_handle) {
- snd_mixer_close(u->mixer_handle);
- u->mixer_handle = NULL;
- }
+ u->mixer_handle = NULL;
+ pa_hashmap_free(u->mixers);
+ u->mixers = NULL;
}
static int setup_mixer(struct userdata *u, bool ignore_dB) {
@@ -2403,8 +2406,8 @@ static void userdata_free(struct userdata *u) {
if (u->mixer_path && !u->mixer_path_set && !u->ucm_context)
pa_alsa_path_free(u->mixer_path);
- if (u->mixer_handle)
- snd_mixer_close(u->mixer_handle);
+ if (u->mixers)
+ pa_hashmap_free(u->mixers);
if (u->smoother)
pa_smoother_free(u->smoother);
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index a64505a0c781..b9a4b8c4b436 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -820,13 +820,13 @@ static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
}
-static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, bool ignore_dB) {
+static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
pa_device_port *port;
pa_alsa_path *path;
pa_alsa_ucm_port_data *data;
pa_alsa_ucm_device *dev;
- snd_mixer_t *mixer_handle = NULL;
- const char *profile, *mdev_opened = NULL, *mdev, *mdev2;
+ snd_mixer_t *mixer_handle;
+ const char *profile, *mdev, *mdev2;
void *state, *state2;
int idx;
@@ -843,16 +843,9 @@ static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle,
mdev = mdev2;
}
- if (!mdev_opened || !pa_streq(mdev_opened, mdev)) {
- if (mixer_handle) {
- snd_mixer_close(mixer_handle);
- mdev_opened = NULL;
- }
- if (!(mixer_handle = pa_alsa_open_mixer_by_name(mdev))) {
- pa_log_error("Failed to find a working mixer device (%s).", mdev);
- goto fail;
- }
- mdev_opened = mdev;
+ if (!(mixer_handle = pa_alsa_open_mixer_by_name(mixers, mdev, true))) {
+ pa_log_error("Failed to find a working mixer device (%s).", mdev);
+ goto fail;
}
PA_HASHMAP_FOREACH_KV(profile, path, data->paths, state2) {
@@ -867,9 +860,6 @@ static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle,
}
}
- if (mixer_handle)
- snd_mixer_close(mixer_handle);
-
return;
fail:
@@ -1194,7 +1184,7 @@ void pa_alsa_ucm_add_ports(
pa_alsa_ucm_add_ports_combination(*p, context, is_sink, card->ports, NULL, card->core);
/* now set up volume paths if any */
- probe_volumes(*p, is_sink, pcm_handle, ignore_dB);
+ probe_volumes(*p, is_sink, pcm_handle, context->ucm->mixers, ignore_dB);
/* then set property PA_PROP_DEVICE_INTENDED_ROLES */
merged_roles = pa_xstrdup(pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES));
@@ -1543,6 +1533,7 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
pa_alsa_jack *j;
const char *device_name;
const char *jack_control;
+ const char *mixer_device_name;
char *name;
pa_assert(ucm);
@@ -1585,7 +1576,14 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
if (pa_streq(j->name, name))
goto finish;
- j = pa_alsa_jack_new(NULL, name);
+ mixer_device_name = get_jack_mixer_device(device, true);
+ if (!mixer_device_name)
+ mixer_device_name = get_jack_mixer_device(device, false);
+ if (!mixer_device_name) {
+ pa_log("[%s] No mixer device name for JackControl \"%s\"", device_name, jack_control);
+ return NULL;
+ }
+ j = pa_alsa_jack_new(NULL, mixer_device_name, name);
PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
finish:
@@ -1765,44 +1763,28 @@ static void profile_finalize_probing(pa_alsa_profile *p) {
}
}
-static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
- snd_mixer_t *mixer_handle = NULL;
+static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
+ snd_mixer_t *mixer_handle;
pa_alsa_ucm_mapping_context *context = &m->ucm_context;
pa_alsa_ucm_device *dev;
- bool is_sink = m->direction == PA_ALSA_DIRECTION_OUTPUT;
- const char *mdev_opened = NULL, *mdev;
uint32_t idx;
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
bool has_control;
- if (!dev->jack)
+ if (!dev->jack || !dev->jack->mixer_device_name)
continue;
- mdev = get_jack_mixer_device(dev, is_sink);
- if (mdev == NULL) {
- pa_log_error("Unable to determine mixer device for jack %s", dev->jack->name);
+ mixer_handle = pa_alsa_open_mixer_by_name(mixers, dev->jack->mixer_device_name, true);
+ if (!mixer_handle) {
+ pa_log_error("Unable to determine open mixer device '%s' for jack %s", dev->jack->mixer_device_name, dev->jack->name);
continue;
}
- if (!mdev_opened || !pa_streq(mdev_opened, mdev)) {
- if (mixer_handle) {
- snd_mixer_close(mixer_handle);
- mdev_opened = NULL;
- }
- mixer_handle = pa_alsa_open_mixer_by_name(mdev);
- if (!mixer_handle)
- continue;
- mdev_opened = mdev;
- }
-
has_control = pa_alsa_mixer_find_card(mixer_handle, dev->jack->alsa_name, 0) != NULL;
pa_alsa_jack_set_has_control(dev->jack, has_control);
pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
}
-
- if (mixer_handle)
- snd_mixer_close(mixer_handle);
}
static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
@@ -1860,11 +1842,11 @@ static void ucm_probe_profile_set(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
if (!PA_UCM_IS_MODIFIER_MAPPING(m))
- ucm_mapping_jack_probe(m);
+ ucm_mapping_jack_probe(m, ucm->mixers);
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
if (!PA_UCM_IS_MODIFIER_MAPPING(m))
- ucm_mapping_jack_probe(m);
+ ucm_mapping_jack_probe(m, ucm->mixers);
profile_finalize_probing(p);
}
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 014bc334ad67..49362992865b 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -248,6 +248,7 @@ struct pa_alsa_ucm_config {
snd_use_case_mgr_t *ucm_mgr;
pa_alsa_ucm_verb *active_verb;
+ pa_hashmap *mixers;
PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
PA_LLIST_HEAD(pa_alsa_jack, jacks);
};
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 08a144782394..34930c9fa155 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1726,83 +1726,86 @@ static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
return 0;
}
-snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device) {
- int err;
- snd_mixer_t *m;
- char *md;
- snd_pcm_info_t* info;
- snd_pcm_info_alloca(&info);
-
- if ((err = snd_mixer_open(&m, 0)) < 0) {
- pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
- return NULL;
- }
-
- /* Then, try by card index */
- md = pa_sprintf_malloc("hw:%i", alsa_card_index);
- if (prepare_mixer(m, md) >= 0) {
-
- if (ctl_device)
- *ctl_device = md;
- else
- pa_xfree(md);
-
- return m;
- }
-
+snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe) {
+ char *md = pa_sprintf_malloc("hw:%i", alsa_card_index);
+ snd_mixer_t *m = pa_alsa_open_mixer_by_name(mixers, md, probe);
pa_xfree(md);
-
- snd_mixer_close(m);
- return NULL;
+ return m;
}
-snd_mixer_t *pa_alsa_open_mixer_by_name(const char *dev) {
+snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, bool probe) {
int err;
snd_mixer_t *m;
+ pa_alsa_mixer *pm;
+ pa_assert(mixers);
pa_assert(dev);
+ pm = pa_hashmap_get(mixers, dev);
+ if (pm) {
+ if (!probe)
+ pm->used_for_probe_only = false;
+ return pm->mixer_handle;
+ }
+
if ((err = snd_mixer_open(&m, 0)) < 0) {
pa_log("Error opening mixer: %s", pa_alsa_strerror(err));
return NULL;
}
- if (prepare_mixer(m, dev) >= 0)
- return m;
+ if (prepare_mixer(m, dev) >= 0) {
+ pm = pa_xnew0(pa_alsa_mixer, 1);
+ if (pm) {
+ pm->used_for_probe_only = probe;
+ pm->mixer_handle = m;
+ pa_hashmap_put(mixers, pa_xstrdup(dev), pm);
+ return m;
+ }
+ }
snd_mixer_close(m);
return NULL;
}
-snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device) {
- snd_mixer_t *m;
+snd_mixer_t *pa_alsa_open_mixer_for_pcm(pa_hashmap *mixers, snd_pcm_t *pcm, bool probe) {
snd_pcm_info_t* info;
snd_pcm_info_alloca(&info);
pa_assert(pcm);
if (snd_pcm_info(pcm, info) >= 0) {
- char *md;
int card_idx;
- if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
+ if ((card_idx = snd_pcm_info_get_card(info)) >= 0)
+ return pa_alsa_open_mixer(mixers, card_idx, probe);
+ }
- md = pa_sprintf_malloc("hw:%i", card_idx);
- m = pa_alsa_open_mixer_by_name(md);
- if (m) {
- if (ctl_device)
- *ctl_device = md;
- else
- pa_xfree(md);
+ return NULL;
+}
- return m;
- }
+void pa_alsa_mixer_set_fdlist(pa_hashmap *mixers, snd_mixer_t *mixer_handle, pa_mainloop_api *ml)
+{
+ pa_alsa_mixer *pm;
+ void *state;
- pa_xfree(md);
+ PA_HASHMAP_FOREACH(pm, mixers, state)
+ if (pm->mixer_handle == mixer_handle) {
+ pm->used_for_probe_only = false;
+ if (!pm->fdl) {
+ pm->fdl = pa_alsa_fdlist_new();
+ if (pm->fdl)
+ pa_alsa_fdlist_set_handle(pm->fdl, pm->mixer_handle, NULL, ml);
+ }
}
- }
+}
- return NULL;
+void pa_alsa_mixer_free(pa_alsa_mixer *mixer)
+{
+ if (mixer->fdl)
+ pa_alsa_fdlist_free(mixer->fdl);
+ if (mixer->mixer_handle)
+ snd_mixer_close(mixer->mixer_handle);
+ pa_xfree(mixer);
}
int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem, pa_hdmi_eld *eld) {
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index ceca48809400..0d3d5af13370 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -144,7 +144,11 @@ bool pa_alsa_may_tsched(bool want);
snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device);
-snd_mixer_t *pa_alsa_open_mixer(int alsa_card_index, char **ctl_device);
+snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe);
+snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, bool probe);
+snd_mixer_t *pa_alsa_open_mixer_for_pcm(pa_hashmap *mixers, snd_pcm_t *pcm, bool probe);
+void pa_alsa_mixer_set_fdlist(pa_hashmap *mixers, snd_mixer_t *mixer, pa_mainloop_api *ml);
+void pa_alsa_mixer_free(pa_alsa_mixer *mixer);
typedef struct pa_hdmi_eld pa_hdmi_eld;
struct pa_hdmi_eld {
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index ba7a19a38983..3b2f99fc4e54 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -111,9 +111,8 @@ struct userdata {
char *device_id;
int alsa_card_index;
- snd_mixer_t *mixer_handle;
+ pa_hashmap *mixers;
pa_hashmap *jacks;
- pa_alsa_fdlist *mixer_fdl;
pa_card *card;
@@ -569,9 +568,6 @@ static void init_eld_ctls(struct userdata *u) {
void *state;
pa_device_port *port;
- if (!u->mixer_handle)
- return;
-
/* The code in this function expects ports to have a pa_alsa_port_data
* struct as their data, but in UCM mode ports don't have any data. Hence,
* the ELD controls can't currently be used in UCM mode. */
@@ -580,6 +576,7 @@ static void init_eld_ctls(struct userdata *u) {
PA_HASHMAP_FOREACH(port, u->card->ports, state) {
pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
+ snd_mixer_t *mixer_handle;
snd_mixer_elem_t* melem;
int device;
@@ -588,8 +585,13 @@ static void init_eld_ctls(struct userdata *u) {
if (device < 0)
continue;
- melem = pa_alsa_mixer_find_pcm(u->mixer_handle, "ELD", device);
+ mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, true);
+ if (!mixer_handle)
+ continue;
+
+ melem = pa_alsa_mixer_find_pcm(mixer_handle, "ELD", device);
if (melem) {
+ pa_alsa_mixer_set_fdlist(u->mixers, mixer_handle, u->core->mainloop);
snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
snd_mixer_elem_set_callback_private(melem, u);
hdmi_eld_changed(melem, 0);
@@ -630,25 +632,31 @@ static void init_jacks(struct userdata *u) {
if (pa_hashmap_size(u->jacks) == 0)
return;
- u->mixer_fdl = pa_alsa_fdlist_new();
-
- u->mixer_handle = pa_alsa_open_mixer(u->alsa_card_index, NULL);
- if (u->mixer_handle && pa_alsa_fdlist_set_handle(u->mixer_fdl, u->mixer_handle, NULL, u->core->mainloop) >= 0) {
- PA_HASHMAP_FOREACH(jack, u->jacks, state) {
- jack->melem = pa_alsa_mixer_find_card(u->mixer_handle, jack->alsa_name, 0);
- if (!jack->melem) {
- pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
- pa_alsa_jack_set_has_control(jack, false);
- continue;
+ PA_HASHMAP_FOREACH(jack, u->jacks, state) {
+ if (!jack->mixer_device_name) {
+ jack->mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, false);
+ if (!jack->mixer_handle) {
+ pa_log("Failed to open mixer for card %d for jack detection", u->alsa_card_index);
+ continue;
+ }
+ } else {
+ jack->mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, jack->mixer_device_name, false);
+ if (!jack->mixer_handle) {
+ pa_log("Failed to open mixer '%s' for jack detection", jack->mixer_device_name);
+ continue;
}
- snd_mixer_elem_set_callback(jack->melem, report_jack_state);
- snd_mixer_elem_set_callback_private(jack->melem, u);
- report_jack_state(jack->melem, 0);
}
-
- } else
- pa_log("Failed to open mixer for jack detection");
-
+ pa_alsa_mixer_set_fdlist(u->mixers, jack->mixer_handle, u->core->mainloop);
+ jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, jack->alsa_name, 0);
+ if (!jack->melem) {
+ pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
+ pa_alsa_jack_set_has_control(jack, false);
+ continue;
+ }
+ snd_mixer_elem_set_callback(jack->melem, report_jack_state);
+ snd_mixer_elem_set_callback_private(jack->melem, u);
+ report_jack_state(jack->melem, 0);
+ }
}
static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) {
@@ -772,6 +780,10 @@ int pa__init(pa_module *m) {
u->use_ucm = true;
u->ucm.core = m->core;
+ u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
+ pa_xfree, (pa_free_cb_t) pa_alsa_mixer_free);
+ u->ucm.mixers = u->mixers; /* alias */
+
if (!(u->modargs = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
@@ -852,7 +864,7 @@ int pa__init(pa_module *m) {
u->profile_set->ignore_dB = ignore_dB;
- pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec);
+ pa_alsa_profile_set_probe(u->profile_set, u->mixers, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec);
pa_alsa_profile_set_dump(u->profile_set);
pa_card_new_data_init(&data);
@@ -952,6 +964,16 @@ int pa__init(pa_module *m) {
init_profile(u);
init_eld_ctls(u);
+ /* Remove all probe only mixers */
+ if (u->mixers) {
+ const char *devname;
+ pa_alsa_mixer *pm;
+ void *state;
+ PA_HASHMAP_FOREACH_KV(devname, pm, u->mixers, state)
+ if (pm->used_for_probe_only)
+ pa_hashmap_remove_and_free(u->mixers, devname);
+ }
+
if (reserve)
pa_reserve_wrapper_unref(reserve);
@@ -1002,10 +1024,8 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
goto finish;
- if (u->mixer_fdl)
- pa_alsa_fdlist_free(u->mixer_fdl);
- if (u->mixer_handle)
- snd_mixer_close(u->mixer_handle);
+ if (u->mixers)
+ pa_hashmap_free(u->mixers);
if (u->jacks)
pa_hashmap_free(u->jacks);
--
2.16.4

View File

@ -0,0 +1,264 @@
From 3ceff8bb3f697ec16dc5c72e658b10ac40bc19f5 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 7 Dec 2019 23:22:33 +0100
Subject: [PATCH] alsa-ucm: add support for HDMI ELD
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 72 +++++++++++++++++++++++++++++++++++--
src/modules/alsa/alsa-ucm.h | 7 ++++
src/modules/alsa/module-alsa-card.c | 56 ++++++++++++++++++-----------
3 files changed, 113 insertions(+), 22 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index b9a4b8c4b436..5695840abaf1 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -820,6 +820,36 @@ static int pa_alsa_ucm_device_cmp(const void *a, const void *b) {
return strcmp(pa_proplist_gets(d1->proplist, PA_ALSA_PROP_UCM_NAME), pa_proplist_gets(d2->proplist, PA_ALSA_PROP_UCM_NAME));
}
+static void set_eld_devices(pa_hashmap *hash)
+{
+ pa_device_port *port;
+ pa_alsa_ucm_port_data *data;
+ pa_alsa_ucm_device *dev;
+ const char *eld_mixer_device_name;
+ void *state;
+ int idx, eld_device;
+
+ PA_HASHMAP_FOREACH(port, hash, state) {
+ data = PA_DEVICE_PORT_DATA(port);
+ eld_mixer_device_name = NULL;
+ eld_device = -1;
+ PA_DYNARRAY_FOREACH(dev, data->devices, idx) {
+ if (dev->eld_device >= 0 && dev->eld_mixer_device_name) {
+ if (eld_device >= 0 && eld_device != dev->eld_device) {
+ pa_log_error("The ELD device is already set!");
+ } else if (eld_mixer_device_name && pa_streq(dev->eld_mixer_device_name, eld_mixer_device_name)) {
+ pa_log_error("The ELD mixer device is already set (%s, %s)!", dev->eld_mixer_device_name, dev->eld_mixer_device_name);
+ } else {
+ eld_mixer_device_name = dev->eld_mixer_device_name;
+ eld_device = dev->eld_device;
+ }
+ }
+ }
+ data->eld_device = eld_device;
+ data->eld_mixer_device_name = pa_xstrdup(eld_mixer_device_name);
+ }
+}
+
static void probe_volumes(pa_hashmap *hash, bool is_sink, snd_pcm_t *pcm_handle, pa_hashmap *mixers, bool ignore_dB) {
pa_device_port *port;
pa_alsa_path *path;
@@ -1159,6 +1189,9 @@ void pa_alsa_ucm_add_ports_combination(
ucm_add_ports_combination(p, context, is_sink, pdevices, 0, PA_IDXSET_INVALID, ports, cp, core);
pa_xfree(pdevices);
}
+
+ /* ELD devices */
+ set_eld_devices(ports);
}
void pa_alsa_ucm_add_ports(
@@ -1709,6 +1742,33 @@ static int ucm_create_profile(
return 0;
}
+static void mapping_init_eld(pa_alsa_mapping *m, snd_pcm_t *pcm)
+{
+ pa_alsa_ucm_mapping_context *context = &m->ucm_context;
+ pa_alsa_ucm_device *dev;
+ uint32_t idx;
+ char *mdev;
+ snd_pcm_info_t *info;
+ int pcm_card, pcm_device;
+
+ snd_pcm_info_alloca(&info);
+ if (snd_pcm_info(pcm, info) < 0)
+ return;
+
+ if ((pcm_card = snd_pcm_info_get_card(info)) < 0)
+ return;
+ if ((pcm_device = snd_pcm_info_get_device(info)) < 0)
+ return;
+
+ PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
+ mdev = pa_sprintf_malloc("hw:%i", pcm_card);
+ if (mdev == NULL)
+ continue;
+ dev->eld_mixer_device_name = mdev;
+ dev->eld_device = pcm_device;
+ }
+}
+
static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m, int mode) {
snd_pcm_t* pcm;
pa_sample_spec try_ss = ucm->core->default_sample_spec;
@@ -1730,8 +1790,11 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m,
pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
&try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels);
- if (pcm && !exact_channels)
- m->channel_map = try_map;
+ if (pcm) {
+ if (!exact_channels)
+ m->channel_map = try_map;
+ mapping_init_eld(m, pcm);
+ }
return pcm;
}
@@ -1912,6 +1975,8 @@ static void free_verb(pa_alsa_ucm_verb *verb) {
if (di->supported_devices)
pa_idxset_free(di->supported_devices, NULL);
+ pa_xfree(di->eld_mixer_device_name);
+
pa_xfree(di);
}
@@ -2115,6 +2180,7 @@ static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *
port->ucm = ucm;
port->core_port = core_port;
port->devices = pa_dynarray_new(NULL);
+ port->eld_device = -1;
for (i = 0; i < n_devices; i++) {
pa_dynarray_append(port->devices, devices[i]);
@@ -2139,6 +2205,8 @@ static void ucm_port_data_free(pa_device_port *port) {
if (ucm_port->paths)
pa_hashmap_free(ucm_port->paths);
+
+ pa_xfree(ucm_port->eld_mixer_device_name);
}
static void ucm_port_update_available(pa_alsa_ucm_port_data *port) {
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 49362992865b..48fd9db3f79f 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -207,6 +207,9 @@ struct pa_alsa_ucm_device {
pa_alsa_jack *jack;
pa_dynarray *hw_mute_jacks; /* pa_alsa_jack */
pa_available_t available;
+
+ char *eld_mixer_device_name;
+ int eld_device;
};
void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device);
@@ -273,6 +276,10 @@ struct pa_alsa_ucm_port_data {
pa_hashmap *paths;
/* Current path, set when activating profile */
pa_alsa_path *path;
+
+ /* ELD info */
+ char *eld_mixer_device_name;
+ int eld_device; /* PCM device number */
};
struct pa_alsa_ucm_volume {
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 3b2f99fc4e54..c5852b43d844 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -512,15 +512,24 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) {
return 0;
}
-static pa_device_port* find_port_with_eld_device(pa_hashmap *ports, int device) {
+static pa_device_port* find_port_with_eld_device(struct userdata *u, int device) {
void *state;
pa_device_port *p;
- PA_HASHMAP_FOREACH(p, ports, state) {
- pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(p);
- pa_assert(data->path);
- if (device == data->path->eld_device)
- return p;
+ if (u->use_ucm) {
+ PA_HASHMAP_FOREACH(p, u->card->ports, state) {
+ pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(p);
+ pa_assert(data->eld_mixer_device_name);
+ if (device == data->eld_device)
+ return p;
+ }
+ } else {
+ PA_HASHMAP_FOREACH(p, u->card->ports, state) {
+ pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(p);
+ pa_assert(data->path);
+ if (device == data->path->eld_device)
+ return p;
+ }
}
return NULL;
}
@@ -537,10 +546,7 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) {
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
- if (u->use_ucm)
- return 0;
-
- p = find_port_with_eld_device(u->card->ports, device);
+ p = find_port_with_eld_device(u, device);
if (p == NULL) {
pa_log_error("Invalid device changed in ALSA: %d", device);
return 0;
@@ -571,21 +577,30 @@ static void init_eld_ctls(struct userdata *u) {
/* The code in this function expects ports to have a pa_alsa_port_data
* struct as their data, but in UCM mode ports don't have any data. Hence,
* the ELD controls can't currently be used in UCM mode. */
- if (u->use_ucm)
- return;
-
PA_HASHMAP_FOREACH(port, u->card->ports, state) {
- pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
snd_mixer_t *mixer_handle;
snd_mixer_elem_t* melem;
int device;
- pa_assert(data->path);
- device = data->path->eld_device;
- if (device < 0)
- continue;
+ if (u->use_ucm) {
+ pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(port);
+ device = data->eld_device;
+ if (device < 0 || !data->eld_mixer_device_name)
+ continue;
+
+ mixer_handle = pa_alsa_open_mixer_by_name(u->mixers, data->eld_mixer_device_name, true);
+ } else {
+ pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
+
+ pa_assert(data->path);
+
+ device = data->path->eld_device;
+ if (device < 0)
+ continue;
+
+ mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, true);
+ }
- mixer_handle = pa_alsa_open_mixer(u->mixers, u->alsa_card_index, true);
if (!mixer_handle)
continue;
@@ -595,9 +610,10 @@ static void init_eld_ctls(struct userdata *u) {
snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
snd_mixer_elem_set_callback_private(melem, u);
hdmi_eld_changed(melem, 0);
+ pa_log_info("ELD device found for port %s (%d).", port->name, device);
}
else
- pa_log_debug("No ELD device found for port %s.", port->name);
+ pa_log_debug("No ELD device found for port %s (%d).", port->name, device);
}
}
--
2.16.4

View File

@ -0,0 +1,83 @@
From 8837c90b7fe6b7c3cbbaeda0a29e2f5d311c3d14 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sun, 8 Dec 2019 22:48:45 +0100
Subject: [PATCH] alsa-mixer: do the quick card number lookup to save mixer
instances
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.h | 1 +
src/modules/alsa/alsa-util.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 325b0c998b02..d740a78c8dce 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -102,6 +102,7 @@ struct pa_alsa_setting {
/* An entry for one ALSA mixer */
struct pa_alsa_mixer {
snd_mixer_t *mixer_handle;
+ int card_index;
pa_alsa_fdlist *fdl;
bool used_for_probe_only:1;
};
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 34930c9fa155..d86f43c10098 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1737,11 +1737,33 @@ snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, boo
int err;
snd_mixer_t *m;
pa_alsa_mixer *pm;
+ char *dev2;
+ void *state;
pa_assert(mixers);
pa_assert(dev);
pm = pa_hashmap_get(mixers, dev);
+
+ /* The quick card number/index lookup (hw:#)
+ * We already know the card number/index, thus use the mixer
+ * from the cache at first.
+ */
+ if (!pm && pa_strneq(dev, "hw:", 3)) {
+ const char *s = dev + 3;
+ int card_index;
+ while (*s && *s >= 0 && *s <= '9') s++;
+ if (*s == '\0' && pa_atoi(dev + 3, &card_index) >= 0) {
+ PA_HASHMAP_FOREACH_KV(dev2, pm, mixers, state) {
+ if (pm->card_index == card_index) {
+ dev = dev2;
+ pm = pa_hashmap_get(mixers, dev);
+ break;
+ }
+ }
+ }
+ }
+
if (pm) {
if (!probe)
pm->used_for_probe_only = false;
@@ -1756,6 +1778,17 @@ snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, boo
if (prepare_mixer(m, dev) >= 0) {
pm = pa_xnew0(pa_alsa_mixer, 1);
if (pm) {
+ snd_hctl_t *hctl;
+ pm->card_index = -1;
+ /* determine the ALSA card number (index) and store it to card_index */
+ err = snd_mixer_get_hctl(m, dev, &hctl);
+ if (err >= 0) {
+ snd_ctl_card_info_t *info;
+ snd_ctl_card_info_alloca(&info);
+ err = snd_ctl_card_info(snd_hctl_ctl(hctl), info);
+ if (err >= 0)
+ pm->card_index = snd_ctl_card_info_get_card(info);
+ }
pm->used_for_probe_only = probe;
pm->mixer_handle = m;
pa_hashmap_put(mixers, pa_xstrdup(dev), pm);
--
2.16.4

View File

@ -0,0 +1,134 @@
From d7dbd0cbe3191661c02ac89d108b36c79474de3c Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sun, 8 Dec 2019 23:17:32 +0100
Subject: [PATCH] alsa-mixer: improve check for the empty path set for
sink/source
The unused mixer instances are created without this code.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-mixer.c | 6 ++++++
src/modules/alsa/alsa-mixer.h | 1 +
src/modules/alsa/alsa-sink.c | 19 +++++++++++++------
src/modules/alsa/alsa-source.c | 9 ++++++---
4 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index a3c998b654e9..d184aec7aee7 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -741,6 +741,12 @@ void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
pa_xfree(ps);
}
+int pa_alsa_path_set_is_empty(pa_alsa_path_set *ps) {
+ if (ps && !pa_hashmap_isempty(ps->paths))
+ return 0;
+ return 1;
+}
+
static long to_alsa_dB(pa_volume_t v) {
return lround(pa_sw_volume_to_dB(v) * 100.0);
}
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index d740a78c8dce..df739cc2e0f3 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -272,6 +272,7 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
void pa_alsa_path_set_dump(pa_alsa_path_set *s);
void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
void pa_alsa_path_set_free(pa_alsa_path_set *s);
+int pa_alsa_path_set_is_empty(pa_alsa_path_set *s);
struct pa_alsa_mapping {
pa_alsa_profile_set *profile_set;
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 363b4be2fa25..042d4dfd9c89 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1651,7 +1651,6 @@ static int sink_set_port_ucm_cb(pa_sink *s, pa_device_port *p) {
pa_assert(u);
pa_assert(p);
- pa_assert(u->mixer_handle);
pa_assert(u->ucm_context);
data = PA_DEVICE_PORT_DATA(p);
@@ -2089,6 +2088,9 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
if (!mapping && !element)
return;
+ if (!element && mapping && pa_alsa_path_set_is_empty(mapping->output_path_set))
+ return;
+
u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
NULL, (pa_free_cb_t) pa_alsa_mixer_free);
@@ -2113,8 +2115,9 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
pa_alsa_path_dump(u->mixer_path);
- } else if (!(u->mixer_path_set = mapping->output_path_set))
- goto fail;
+ } else {
+ u->mixer_path_set = mapping->output_path_set;
+ }
return;
@@ -2559,10 +2562,14 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail;
}
- if (u->ucm_context)
+ if (u->ucm_context) {
pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, true, card, u->pcm_handle, ignore_dB);
- else if (u->mixer_path_set)
- pa_alsa_add_ports(&data, u->mixer_path_set, card);
+ find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+ } else {
+ find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+ if (u->mixer_path_set)
+ pa_alsa_add_ports(&data, u->mixer_path_set, card);
+ }
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | (u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0) |
(set_formats ? PA_SINK_SET_FORMATS : 0));
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index b46e845cc5a7..104de4e266dd 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1522,7 +1522,6 @@ static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
pa_assert(u);
pa_assert(p);
- pa_assert(u->mixer_handle);
pa_assert(u->ucm_context);
data = PA_DEVICE_PORT_DATA(p);
@@ -1795,6 +1794,9 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
if (!mapping && !element)
return;
+ if (!element && mapping && pa_alsa_path_set_is_empty(mapping->input_path_set))
+ return;
+
u->mixers = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
NULL, (pa_free_cb_t) pa_alsa_mixer_free);
@@ -1819,8 +1821,9 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
pa_alsa_path_dump(u->mixer_path);
- } else if (!(u->mixer_path_set = mapping->input_path_set))
- goto fail;
+ } else {
+ u->mixer_path_set = mapping->input_path_set;
+ }
return;
--
2.16.4

View File

@ -0,0 +1,128 @@
From cd4a69374cefe7c720bd6916f06ff7151da9892a Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Tue, 10 Dec 2019 12:34:19 +0100
Subject: [PATCH] alsa-ucm: allow to set profile priority from UCM value
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
src/modules/alsa/alsa-ucm.c | 59 +++++++++++++++++++++++++++++++++------------
src/modules/alsa/alsa-ucm.h | 1 +
2 files changed, 45 insertions(+), 15 deletions(-)
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index 5695840abaf1..a57be6d22497 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -146,6 +146,26 @@ static struct ucm_info dev_info[] = {
{NULL, 0}
};
+
+static char *ucm_verb_value(
+ snd_use_case_mgr_t *uc_mgr,
+ const char *verb_name,
+ const char *id) {
+
+ const char *value;
+ char *_id = pa_sprintf_malloc("=%s//%s", id, verb_name);
+ int err = snd_use_case_get(uc_mgr, _id, &value);
+ pa_xfree(_id);
+ if (err < 0)
+ return NULL;
+ pa_log_debug("Got %s for verb %s: %s", id, verb_name, value);
+ /* Use the cast here to allow free() call without casting for callers.
+ * The snd_use_case_get() returns mallocated string.
+ * See the Note: in use-case.h for snd_use_case_get().
+ */
+ return (char *)value;
+}
+
static int ucm_device_exists(pa_idxset *idxset, pa_alsa_ucm_device *dev) {
pa_alsa_ucm_device *d;
uint32_t idx;
@@ -766,6 +786,8 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, cons
pa_alsa_ucm_device *d;
pa_alsa_ucm_modifier *mod;
pa_alsa_ucm_verb *verb;
+ char *value;
+ unsigned ui;
int err = 0;
*p_verb = NULL;
@@ -780,6 +802,11 @@ int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, cons
pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(verb_name));
pa_proplist_sets(verb->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(verb_desc));
+ value = ucm_verb_value(uc_mgr, verb_name, "Priority");
+ if (value && !pa_atou(value, &ui))
+ verb->priority = ui > 10000 ? 10000 : ui;
+ free(value);
+
err = ucm_get_devices(verb, uc_mgr);
if (err < 0)
pa_log("No UCM devices for verb %s", verb_name);
@@ -1637,7 +1664,7 @@ static int ucm_create_profile(
pa_alsa_ucm_modifier *mod;
int i = 0;
const char *name, *sink, *source;
- char *verb_cmp, *c;
+ unsigned int priority;
pa_assert(ps);
@@ -1657,24 +1684,26 @@ static int ucm_create_profile(
p->supported = true;
pa_hashmap_put(ps->profiles, p->name, p);
- /* TODO: get profile priority from ucm info or policy management */
- c = verb_cmp = pa_xstrdup(verb_name);
- while (*c) {
- if (*c == '_') *c = ' ';
- c++;
- }
+ /* TODO: get profile priority from policy management */
+ priority = verb->priority;
- for (i = 0; verb_info[i].id; i++) {
- if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
- p->priority = verb_info[i].priority;
- break;
+ if (priority == 0) {
+ char *verb_cmp, *c;
+ c = verb_cmp = pa_xstrdup(verb_name);
+ while (*c) {
+ if (*c == '_') *c = ' ';
+ c++;
+ }
+ for (i = 0; verb_info[i].id; i++) {
+ if (strcasecmp(verb_info[i].id, verb_cmp) == 0) {
+ priority = verb_info[i].priority;
+ break;
+ }
}
+ pa_xfree(verb_cmp);
}
- pa_xfree(verb_cmp);
-
- if (verb_info[i].id == NULL)
- p->priority = 1000;
+ p->priority = priority;
PA_LLIST_FOREACH(dev, verb->devices) {
pa_alsa_jack *jack;
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index 48fd9db3f79f..e7a795cedeb7 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -241,6 +241,7 @@ struct pa_alsa_ucm_verb {
PA_LLIST_FIELDS(pa_alsa_ucm_verb);
pa_proplist *proplist;
+ unsigned priority;
PA_LLIST_HEAD(pa_alsa_ucm_device, devices);
PA_LLIST_HEAD(pa_alsa_ucm_modifier, modifiers);
--
2.16.4

View File

@ -1,3 +1,41 @@
-------------------------------------------------------------------
Wed Feb 12 22:01:05 CET 2020 - tiwai@suse.de
- Backport upstream fixes / enhancements about alsa modules:
mainly for UCM support (boo#1160914):
0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch
0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch
0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch
0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch
0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch
0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch
0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch
0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch
0009-alsa-mixer-improve-alsa_id_decode-function.patch
0010-alsa-ucm-Support-Playback-CaptureVolume.patch
0011-alsa-ucm-Fix-volume-control-based-on-review.patch
0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch
0013-alsa-ucm-add-support-for-master-volume.patch
0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch
0015-alsa-ucm-fix-parsing-for-JackControl.patch
0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch
0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch
0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch
0019-alsa-ucm-parse-correctly-the-device-values.patch
0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch
0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch
0022-alsa-ucm-add-control-and-mixer-device-items.patch
0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch
0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch
0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch
0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch
0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch
0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch
0029-alsa-ucm-add-support-for-HDMI-ELD.patch
0030-alsa-mixer-do-the-quick-card-number-lookup-to-save-m.patch
0031-alsa-mixer-improve-check-for-the-empty-path-set-for-.patch
0032-alsa-ucm-allow-to-set-profile-priority-from-UCM-valu.patch
-------------------------------------------------------------------
Mon Feb 3 07:36:31 UTC 2020 - Bjørn Lie <bjorn.lie@gmail.com>

View File

@ -50,6 +50,39 @@ Patch2: pulseaudio-wrong-memset.patch
Patch5: qpaeq-shebang.patch
# PATCH-FIX-OPENSUSE Workaround for old systemd on Leap 15.x
Patch6: pulseaudio-old-systemd-workaround.patch
# PATCH-FIX-UPSTREAM
Patch1001: 0001-alsa-mixer-path-test-Hide-unused-functions-when-buil.patch
Patch1002: 0002-alsa-mixer-recognize-the-Speaker-Jack-control.patch
Patch1003: 0003-alsa-mixer-add-support-for-SteelSeries-Arctis-Pro-20.patch
Patch1004: 0004-alsa-mixer-Add-support-for-SteelSeries-Arctis-5-2019.patch
Patch1005: 0005-alsa-mixer-add-support-for-LucidSound-LS31-and-creat.patch
Patch1006: 0006-alsa-ucm-use-ucm2-name-for-the-direct-card-index-ope.patch
Patch1007: 0007-alsa-ucm-add-mixer-IDs-to-ucm_items.patch
Patch1008: 0008-alsa-mixer-handle-the-index-for-ALSA-mixer-element-i.patch
Patch1009: 0009-alsa-mixer-improve-alsa_id_decode-function.patch
Patch1010: 0010-alsa-ucm-Support-Playback-CaptureVolume.patch
Patch1011: 0011-alsa-ucm-Fix-volume-control-based-on-review.patch
Patch1012: 0012-alsa-ucm-use-the-correct-mixer-identifiers-as-first.patch
Patch1013: 0013-alsa-ucm-add-support-for-master-volume.patch
Patch1014: 0014-alsa-ucm-split-correctly-JackHWMute-device-names.patch
Patch1015: 0015-alsa-ucm-fix-parsing-for-JackControl.patch
Patch1016: 0016-alsa-ucm-add-comments-to-ucm_get_mixer_id.patch
Patch1017: 0017-alsa-ucm-validate-access-to-PA_DEVICE_PORT_DATA.patch
Patch1018: 0018-alsa-Skip-resume-PCM-if-hardware-doesn-t-support-it.patch
Patch1019: 0019-alsa-ucm-parse-correctly-the-device-values.patch
Patch1020: 0020-alsa-ucm-do-not-try-to-use-UCM-device-name-as-jack-n.patch
Patch1021: 0021-alsa-util-do-not-try-to-guess-the-mixer-name-from-th.patch
Patch1022: 0022-alsa-ucm-add-control-and-mixer-device-items.patch
Patch1023: 0023-alsa-ucm-get-the-mixer-names-from-ucm-don-t-guess.patch
Patch1024: 0024-alsa-ucm-use-the-proper-mixer-name-for-ucm-pcm-sink-.patch
Patch1025: 0025-alsa-mixer-handle-interface-type-CARD-PCM-for-mixer-.patch
Patch1026: 0026-alsa-mixer-Add-the-ability-to-pass-the-intended-role.patch
Patch1027: 0027-alsa-mixer-Set-the-intended-role-of-Steelseries-Arct.patch
Patch1028: 0028-alsa-rewrite-mixer-open-close-cache-mixer-accesses-i.patch
Patch1029: 0029-alsa-ucm-add-support-for-HDMI-ELD.patch
Patch1030: 0030-alsa-mixer-do-the-quick-card-number-lookup-to-save-m.patch
Patch1031: 0031-alsa-mixer-improve-check-for-the-empty-path-set-for-.patch
Patch1032: 0032-alsa-ucm-allow-to-set-profile-priority-from-UCM-valu.patch
BuildRequires: alsa-devel >= 1.0.19
BuildRequires: bluez-devel >= 5
BuildRequires: doxygen
@ -338,6 +371,38 @@ Optional dependency offering zsh completion for various PulseAudio utilities
%prep
%setup -q -T -b0
%patch0
%patch1001 -p1
%patch1002 -p1
%patch1003 -p1
%patch1004 -p1
%patch1005 -p1
%patch1006 -p1
%patch1007 -p1
%patch1008 -p1
%patch1009 -p1
%patch1010 -p1
%patch1011 -p1
%patch1012 -p1
%patch1013 -p1
%patch1014 -p1
%patch1015 -p1
%patch1016 -p1
%patch1017 -p1
%patch1018 -p1
%patch1019 -p1
%patch1020 -p1
%patch1021 -p1
%patch1022 -p1
%patch1023 -p1
%patch1024 -p1
%patch1025 -p1
%patch1026 -p1
%patch1027 -p1
%patch1028 -p1
%patch1029 -p1
%patch1030 -p1
%patch1031 -p1
%patch1032 -p1
%patch1 -p1
%patch2
%patch5