[RFC PATCH 3/4] alsa-card: Switch profile when the active one becomes unavailable

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When the active profile of a card becomes unavailable and no other
module changes it to a better profile (i.e. there are no available ports
that module-switch-on-port-available could switch to) the card will be
stuck on an unavailable profile with a non-working sink/source and any
active streams connected to that sink/source will remain connected.

This commit switches to a different profile when the active profile
becomes unavailble, looking for a profile with availability yes or
unknown with the highest priority, and ultimately fall-backing to the
OFF profile.

With this fix a card that only has one port can have the streams
connected to its sink/source moved away by module-rescue-stream when
that port becomes unavailable. This has been seen on machines with AMD
graphics, where the HDMI port lives on a separate ALSA card that only
has that port.
---
 src/modules/alsa/module-alsa-card.c | 54 +++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 041d53121..1fc5216fc 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -735,6 +735,57 @@ static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source
     return PA_HOOK_OK;
 }
 
+static pa_card_profile *find_best_profile(pa_card *card) {
+    pa_card_profile *profile = NULL;
+    pa_card_profile *best_profile = NULL;
+    void *state;
+
+    pa_assert(card);
+    best_profile = pa_hashmap_get(card->profiles, "off");
+
+    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+        if (profile->available == PA_AVAILABLE_NO)
+            continue;
+
+        if (!pa_card_profile_has_available_ports(profile, PA_DIRECTION_OUTPUT, PA_AVAILABLE_YES))
+            continue;
+
+        if (profile->priority > best_profile->priority)
+            best_profile = profile;
+    }
+
+    if (pa_safe_streq(best_profile->name, "off")) {
+        PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+            if (profile->available == PA_AVAILABLE_NO)
+                continue;
+
+            if (!pa_card_profile_has_available_ports(profile, PA_DIRECTION_OUTPUT, PA_AVAILABLE_UNKNOWN))
+                continue;
+
+            if (profile->priority > best_profile->priority)
+                best_profile = profile;
+        }
+    }
+
+    return best_profile;
+}
+
+static pa_hook_result_t card_profile_available_changed(pa_core *c, pa_card_profile *profile, struct userdata *u) {
+    pa_card *card;
+
+    pa_assert(profile);
+    pa_assert_se(card = profile->card);
+
+    if (profile->available != PA_AVAILABLE_NO)
+        return PA_HOOK_OK;
+
+    if (!pa_safe_streq(profile->name, card->active_profile->name))
+        return PA_HOOK_OK;
+
+    pa_log_debug("Active profile %s on card %s became unavailable, switching to another profile", profile->name, card->name);
+    return pa_card_set_profile(card, find_best_profile(card), false);
+}
+
 int pa__init(pa_module *m) {
     pa_card_new_data data;
     bool ignore_dB = false;
@@ -912,6 +963,9 @@ int pa__init(pa_module *m) {
 
     init_jacks(u);
 
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], PA_HOOK_NORMAL,
+            (pa_hook_cb_t) card_profile_available_changed, u);
+
     pa_card_choose_initial_profile(u->card);
 
     /* If the "profile" modarg is given, we have to override whatever the usual
-- 
2.18.0



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux