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

Submitted by João Paulo Rechi Vita on Aug. 8, 2018, 5 a.m.

Details

Message ID 20180808050052.32327-4-jprvita@endlessm.com
State New
Series "Audio routing fixes and improvements"
Headers show

Commit Message

João Paulo Rechi Vita Aug. 8, 2018, 5 a.m.
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(+)

Patch hide | download patch | download mbox

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

Comments

Tanu Kaskinen Oct. 9, 2018, 9:22 a.m.
On Tue, 2018-08-07 at 22:00 -0700, João Paulo Rechi Vita wrote:
> 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.

Sounds good, but have you checked that the profile gets restored back
to something else than off once you plug the device back in? At least
at some point this was an issue, and I think it hasn't been fixed. If
this is still an issue, module-switch-on-port-available needs to be
fixed first to do the profile switch away from off when something gets
plugged in.
João Paulo Rechi Vita Nov. 21, 2018, 7:44 p.m.
On Tue, Oct 9, 2018 at 2:23 AM Tanu Kaskinen <tanuk@iki.fi> wrote:
>
> On Tue, 2018-08-07 at 22:00 -0700, João Paulo Rechi Vita wrote:
> > 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.
>
> Sounds good, but have you checked that the profile gets restored back
> to something else than off once you plug the device back in? At least
> at some point this was an issue, and I think it hasn't been fixed. If
> this is still an issue, module-switch-on-port-available needs to be
> fixed first to do the profile switch away from off when something gets
> plugged in.
>

Yes, once something is plugged again on the card its profile is
updated to reflect the new port availability.

I'm going to re-send this as a MR for inclusion.

--
João Paulo Rechi Vita
http://about.me/jprvita
João Paulo Rechi Vita Nov. 26, 2018, 6:51 p.m.
On Wed, Nov 21, 2018 at 11:44 AM João Paulo Rechi Vita
<jprvita@gmail.com> wrote:
>
> On Tue, Oct 9, 2018 at 2:23 AM Tanu Kaskinen <tanuk@iki.fi> wrote:
> >
> > On Tue, 2018-08-07 at 22:00 -0700, João Paulo Rechi Vita wrote:
> > > 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.
> >
> > Sounds good, but have you checked that the profile gets restored back
> > to something else than off once you plug the device back in? At least
> > at some point this was an issue, and I think it hasn't been fixed. If
> > this is still an issue, module-switch-on-port-available needs to be
> > fixed first to do the profile switch away from off when something gets
> > plugged in.
> >
>
> Yes, once something is plugged again on the card its profile is
> updated to reflect the new port availability.
>
> I'm going to re-send this as a MR for inclusion.
>

Now on https://gitlab.freedesktop.org/pulseaudio/pulseaudio/merge_requests/34,
in case anyone else is interested.

--
João Paulo Rechi Vita
http://about.me/jprvita