[pulseaudio-discuss] [PATCH] Don't settle on a card profile if it doesn't have any available port

Mario Sanchez Prada mario at endlessm.com
Mon Mar 30 09:58:42 PDT 2015


Hi David,

On 30/03/15 13:36, David Henningsson wrote:
> [...]
>> --- a/src/modules/alsa/alsa-mixer.c
>> +++ b/src/modules/alsa/alsa-mixer.c
>> @@ -4636,12 +4636,16 @@ static pa_device_port*
>> device_port_alsa_init(pa_hashmap *ports, /* card ports */
>>       if (!p) {
>>           pa_alsa_port_data *data;
>>           pa_device_port_new_data port_data;
>> +        pa_available_t pa = PA_AVAILABLE_UNKNOWN;
> 
> Unnecessary assignment - pa is always set further down.
> 
Ok.

>>           pa_device_port_new_data_init(&port_data);
>>           pa_device_port_new_data_set_name(&port_data, name);
>>           pa_device_port_new_data_set_description(&port_data, description);
>>           pa_device_port_new_data_set_direction(&port_data,
>> path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT :
>> PA_DIRECTION_INPUT);
>>
>> +        pa = pa_alsa_availability_from_jacks(path->jacks, NULL, NULL);
>> +        pa_device_port_new_data_set_available(&port_data, pa);
>> +
>>           p = pa_device_port_new(core, &port_data,
>> sizeof(pa_alsa_port_data));
>>           pa_device_port_new_data_done(&port_data);
>>           pa_assert(p);
>> diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
>> index bb79e71..8ac1ef3 100644
>> --- a/src/modules/alsa/alsa-util.c
>> +++ b/src/modules/alsa/alsa-util.c
>> @@ -1702,3 +1702,55 @@ int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem,
>> pa_hdmi_eld *eld) {
>>
>>       return 0;
>>   }
>> +
>> +pa_available_t pa_alsa_availability_from_jacks(pa_alsa_jack *jacks_llist,
>> pa_device_port *port, pa_card *card) {
>> +    pa_alsa_jack *jack;
>> +    pa_available_t pa = PA_AVAILABLE_UNKNOWN;
>> +    pa_device_port *p;
>> +
>> +    PA_LLIST_FOREACH(jack, jacks_llist) {
>> +        pa_available_t cpa;
>> +
>> +        /* If a port has NOT been provided, it means we will trust the list
>> +         * of jacks passed to be the ones we are interested in checking, so
>> +         * get the actual port from the jack only when a port has been
>> passed. */
>> +        if (port) {
> 
> This is a refactoring from u->use_ucm to "if (card)". It works but I found
> it confusing at first. It's because ucm jacks don't have paths, so we have
> to go through the card instead.
> 
Yes, it is exactly that.

> Maybe we could comment this a bit better, like putting this above the function:
> 
> /*
>  * port - can be null, in which case all jacks in the list will be
>  * checked.
>  * card - null for non-UCM scenarios, where we can use the
>  * jack->path->port pointer. Set this to the right card only for UCM
>  * jacks.
>  */
> 
Makes sense, will do that in a follow-up patch.

>> +            if (card)
>> +                p = pa_hashmap_get(card->ports, jack->name);
>> +            else {
>> +                if (jack->path)
>> +                    p = jack->path->port;
>> +                else
>> +                    continue;
>> +            }
>> +
>> +            if (p != port)
>> +                continue;
>> +        }
>> +
>> +        cpa = jack->plugged_in ? jack->state_plugged :
>> jack->state_unplugged;
>> +
>> +        if (cpa == PA_AVAILABLE_NO) {
> 
> Nitpick: this is probably not your fault, but indentation of this function
> is inconsistent.
> 
It is not my fault, since I took this code from module-asla-card.c, but it's
true that this would be a good opportunity to smooth this. I'll do it.

>> +          /* If a plugged-in jack causes the availability to go to NO, it
>> +           * should override all other availability information (like a
>> +           * blacklist) so set and bail */
>> +          if (jack->plugged_in) {
>> +            pa = cpa;
>> +            break;
>> +          }
>> +
>> +          /* If the current availablility is unknown go the more precise no,
>> +           * but otherwise don't change state */
>> +          if (pa == PA_AVAILABLE_UNKNOWN)
>> +            pa = cpa;
>> +        } else if (cpa == PA_AVAILABLE_YES) {
>> +          /* Output is available through at least one jack, so go to that
>> +           * level of availability. We still need to continue iterating
>> through
>> +           * the jacks in case a jack is plugged in that forces the state
>> to no
>> +           */
>> +          pa = cpa;
>> +        }
>> +    }
>> +
>> +    return pa;
>> +}
>> diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
>> index 8345a0b..dc4b210 100644
>> --- a/src/modules/alsa/alsa-util.h
>> +++ b/src/modules/alsa/alsa-util.h
>> @@ -151,4 +151,6 @@ struct pa_hdmi_eld {
>>
>>   int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem, pa_hdmi_eld *eld);
>>
>> +pa_available_t pa_alsa_availability_from_jacks(pa_alsa_jack *jacks_llist,
>> pa_device_port *port, pa_card *card);
>> +
>>   #endif
>> diff --git a/src/modules/alsa/module-alsa-card.c
>> b/src/modules/alsa/module-alsa-card.c
>> index a7fec04..4032f3b 100644
>> --- a/src/modules/alsa/module-alsa-card.c
>> +++ b/src/modules/alsa/module-alsa-card.c
>> @@ -310,54 +310,6 @@ static void init_profile(struct userdata *u) {
>>               am->source = pa_alsa_source_new(u->module, u->modargs,
>> __FILE__, u->card, am);
>>   }
>>
>> -static void report_port_state(pa_device_port *p, struct userdata *u) {
>> -    void *state;
>> -    pa_alsa_jack *jack;
>> -    pa_available_t pa = PA_AVAILABLE_UNKNOWN;
>> -    pa_device_port *port;
>> -
>> -    PA_HASHMAP_FOREACH(jack, u->jacks, state) {
>> -        pa_available_t cpa;
>> -
>> -        if (u->use_ucm)
>> -            port = pa_hashmap_get(u->card->ports, jack->name);
>> -        else {
>> -            if (jack->path)
>> -                port = jack->path->port;
>> -            else
>> -                continue;
>> -        }
>> -
>> -        if (p != port)
>> -            continue;
>> -
>> -        cpa = jack->plugged_in ? jack->state_plugged :
>> jack->state_unplugged;
>> -
>> -        if (cpa == PA_AVAILABLE_NO) {
>> -          /* If a plugged-in jack causes the availability to go to NO, it
>> -           * should override all other availability information (like a
>> -           * blacklist) so set and bail */
>> -          if (jack->plugged_in) {
>> -            pa = cpa;
>> -            break;
>> -          }
>> -
>> -          /* If the current availablility is unknown go the more precise no,
>> -           * but otherwise don't change state */
>> -          if (pa == PA_AVAILABLE_UNKNOWN)
>> -            pa = cpa;
>> -        } else if (cpa == PA_AVAILABLE_YES) {
>> -          /* Output is available through at least one jack, so go to that
>> -           * level of availability. We still need to continue iterating
>> through
>> -           * the jacks in case a jack is plugged in that forces the state
>> to no
>> -           */
>> -          pa = cpa;
>> -        }
>> -    }
>> -
>> -    pa_device_port_set_available(p, pa);
>> -}
>> -
>>   static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) {
>>       struct userdata *u = snd_mixer_elem_get_callback_private(melem);
>>       snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
>> @@ -388,13 +340,16 @@ static int report_jack_state(snd_mixer_elem_t
>> *melem, unsigned int mask) {
>>               if (u->use_ucm) {
>>                   pa_assert(u->card->ports);
>>                   port = pa_hashmap_get(u->card->ports, jack->name);
>> -                pa_assert(port);
>>               }
>>               else {
>>                   pa_assert(jack->path);
>> -                pa_assert_se(port = jack->path->port);
>> +                port = jack->path->port;
>> +            }
> 
> Inconsistent indentation.
> 

Will fix it.

>> +            if (port) {
>> +              pa_card *card = u->use_ucm ? u->card : NULL;
>> +              pa_available_t pa =
>> pa_alsa_availability_from_jacks(u->jacks, port, card);
>> +              pa_device_port_set_available(port, pa);
>>               }
>> -            report_port_state(port, u);
>>           }
>>       return 0;
>>   }
>> @@ -738,6 +693,8 @@ int pa__init(pa_module *m) {
>>           if ((description = pa_proplist_gets(data.proplist,
>> PA_PROP_DEVICE_DESCRIPTION)))
>>               pa_reserve_wrapper_set_application_device_name(reserve,
>> description);
>>
>> +    init_jacks(u);
>> +
>>       add_profiles(u, data.profiles, data.ports);
>>
>>       if (pa_hashmap_isempty(data.profiles)) {
>> @@ -766,7 +723,6 @@ int pa__init(pa_module *m) {
>>       u->card->userdata = u;
>>       u->card->set_profile = card_set_profile;
>>
>> -    init_jacks(u);
>>       init_profile(u);
>>       init_eld_ctls(u);
>>
>> diff --git a/src/modules/module-switch-on-port-available.c
>> b/src/modules/module-switch-on-port-available.c
>> index 5a6b401..b44819e 100644
>> --- a/src/modules/module-switch-on-port-available.c
>> +++ b/src/modules/module-switch-on-port-available.c
>> @@ -30,6 +30,7 @@
>>
>>   struct userdata {
>>        pa_hook_slot *available_slot;
>> +     pa_hook_slot *card_put_slot;
>>        pa_hook_slot *sink_new_slot;
>>        pa_hook_slot *source_new_slot;
>>   };
>> @@ -246,6 +247,60 @@ static pa_device_port *new_sink_source(pa_hashmap
>> *ports, const char *name) {
>>       return p;
>>   }
>>
>> +static bool profile_contains_available_ports(pa_card_profile *profile) {
>> +    pa_card *card;
>> +    pa_device_port *port;
>> +    void *state;
>> +
>> +    pa_assert(profile);
>> +
>> +    card = profile->card;
>> +    pa_assert(card);
>> +
>> +    PA_HASHMAP_FOREACH(port, card->ports, state) {
>> +        if (pa_hashmap_get(port->profiles, profile->name)
>> +            && port->available != PA_AVAILABLE_NO)
>> +            return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +static pa_card_profile *find_best_profile_with_available_ports(pa_card
>> *card) {
>> +    pa_card_profile *profile = NULL;
>> +    pa_card_profile *best_profile = NULL;
>> +    void *state;
>> +
>> +    pa_assert(card);
>> +
>> +    PA_HASHMAP_FOREACH(profile, card->profiles, state) {
>> +        if (profile->available == PA_AVAILABLE_NO)
>> +            continue;
>> +
>> +        if (!profile_contains_available_ports(profile))
>> +            continue;
>> +
>> +        if (!best_profile || profile->priority > best_profile->priority)
>> +            best_profile = profile;
>> +    }
>> +
>> +    return best_profile;
>> +}
>> +
>> +static pa_hook_result_t card_put_hook_callback(pa_core *c, pa_card *card,
>> struct userdata *u) {
>> +    pa_card_profile *alt_profile;
>> +
>> +    if (profile_contains_available_ports(card->active_profile))
>> +        return PA_HOOK_OK;
> 
> We had some discussion about moving this to the card_new hook instead. I
> still think that would be a good idea (but feel free to prove me wrong).
> 

I don't have any interest in proving you wrong, seriously, but rather in
writing the correct patch here, so I'm open to suggestions.

Furthermore, I was already a bit unsure about this bit because the original
idea was to do this on CARD_NEW, and doing it in CARD_PUT never felt quite
right (I think Arun will agree too), but was the best I could do for now.

> You'd need to change the above to:
> 
>     if (card->active_profile &&
> profile_contains_available_ports(card->active_profile))
>         return PA_HOOK_OK;
> 
> ...because card->active_profile isn't always set at that point (but can be,
> in case of module-card-restore).
> 
Good point. I totally missed the interactions with the module-card-restore
module here and did this in CARD_PUT because we saw in pa_card_new() that
the active profile was being set there, right after emitting CARD_NEW.

> Also set save_profile to false when you're overwriting module-card-restore's
> suggestion. (Hmm, maybe I need to do the same for save_port in
> sink/source_new callbacks actually!)
> 
Will do, thanks for the detailed suggestion!

>> +
>> +    /* Try to avoid situations where we could settle on a profile when
>> +       there are not available ports that could be actually used. */
>> +    if (alt_profile = find_best_profile_with_available_ports(card))
>> +        card->active_profile = alt_profile;
>> +
>> +    return PA_HOOK_OK;
>> +}
>> +
>>   static pa_hook_result_t sink_new_hook_callback(pa_core *c,
>> pa_sink_new_data *new_data, struct userdata *u) {
>>
>>       pa_device_port *p = new_sink_source(new_data->ports,
>> new_data->active_port);
>> @@ -276,6 +331,8 @@ int pa__init(pa_module*m) {
>>       m->userdata = u = pa_xnew(struct userdata, 1);
>>
>>       /* Make sure we are after module-device-restore, so we can overwrite
>> that suggestion if necessary */
>> +    u->card_put_slot =
>> pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PUT],
>> +                                       PA_HOOK_NORMAL, (pa_hook_cb_t)
>> card_put_hook_callback, u);
>>       u->sink_new_slot =
>> pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW],
>>                                          PA_HOOK_NORMAL, (pa_hook_cb_t)
>> sink_new_hook_callback, u);
>>       u->source_new_slot =
>> pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW],
>> @@ -298,6 +355,8 @@ void pa__done(pa_module*m) {
>>
>>       if (u->available_slot)
>>           pa_hook_slot_free(u->available_slot);
>> +    if (u->card_put_slot)
>> +        pa_hook_slot_free(u->card_put_slot);
>>       if (u->sink_new_slot)
>>           pa_hook_slot_free(u->sink_new_slot);
>>       if (u->source_new_slot)
>>
> 
> 

Thanks,
Mario


More information about the pulseaudio-discuss mailing list