Previously module-alsa-card assigned to pa_alsa_jack.plugged_in directly, and then did the port availability updating manually. The idea of pa_alsa_jack_set_plugged_in() is to move the availability updating to the mixer infrastructure, where it really belongs. Similarly, pa_alsa_jack.has_control was previously modified directly from several places. The has_control field affects the port availability, and pa_alsa_jack_set_has_control() takes care of updating the availability. For now, pa_alsa_jack_set_plugged_in() and pa_alsa_jack_set_has_control() only update the port availability when using UCM. My plan is to adapt the traditional mixer code later. --- src/modules/alsa/alsa-mixer.c | 35 ++++++++++++++++++++++- src/modules/alsa/alsa-mixer.h | 2 ++ src/modules/alsa/alsa-ucm.c | 56 ++++++++++++++++++++++++++++++++++++- src/modules/alsa/alsa-ucm.h | 3 ++ src/modules/alsa/module-alsa-card.c | 19 +++++++------ src/pulsecore/device-port.c | 14 ++++++---- 6 files changed, 112 insertions(+), 17 deletions(-) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 8fc883d..343c39a 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -139,6 +139,36 @@ void pa_alsa_jack_free(pa_alsa_jack *jack) { pa_xfree(jack); } +void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control) { + pa_alsa_ucm_device *device; + unsigned idx; + + pa_assert(jack); + + if (has_control == jack->has_control) + return; + + jack->has_control = has_control; + + PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx) + pa_alsa_ucm_device_update_available(device); +} + +void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in) { + pa_alsa_ucm_device *device; + unsigned idx; + + pa_assert(jack); + + if (plugged_in == jack->plugged_in) + return; + + jack->plugged_in = plugged_in; + + PA_DYNARRAY_FOREACH(device, jack->ucm_devices, idx) + pa_alsa_ucm_device_update_available(device); +} + void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device) { pa_assert(jack); pa_assert(device); @@ -1755,10 +1785,13 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { } static int jack_probe(pa_alsa_jack *j, snd_mixer_t *m) { + bool has_control; + pa_assert(j); pa_assert(j->path); - j->has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL; + has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL; + pa_alsa_jack_set_has_control(j, has_control); if (j->has_control) { if (j->required_absent != PA_ALSA_REQUIRED_IGNORE) diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 92a61c2..e5258c1 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -174,6 +174,8 @@ struct pa_alsa_jack { pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *name, const char *alsa_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); void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device); /* A path wraps a series of elements into a single entity which can be diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 4b0adff..fde2d0c 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -90,6 +90,7 @@ struct ucm_port { static struct ucm_port *ucm_port_new(pa_alsa_ucm_config *ucm, pa_device_port *core_port, pa_alsa_ucm_device **devices, unsigned n_devices); static void ucm_port_free(struct ucm_port *port); +static void ucm_port_update_available(struct ucm_port *port); static struct ucm_items item[] = { {"PlaybackPCM", PA_ALSA_PROP_UCM_SINK}, @@ -413,6 +414,7 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) { pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_NAME, pa_strnull(dev_list[i])); pa_proplist_sets(d->proplist, PA_ALSA_PROP_UCM_DESCRIPTION, pa_strna(dev_list[i + 1])); d->ucm_ports = pa_dynarray_new(NULL); + d->available = PA_AVAILABLE_UNKNOWN; PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d); } @@ -1465,7 +1467,10 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m) { return; PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) { - dev->jack->has_control = pa_alsa_mixer_find(mixer_handle, dev->jack->alsa_name, 0) != NULL; + bool has_control; + + 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); } @@ -1716,6 +1721,34 @@ static void device_set_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) { device->jack = jack; pa_alsa_jack_add_ucm_device(jack, device); + + pa_alsa_ucm_device_update_available(device); +} + +static void device_set_available(pa_alsa_ucm_device *device, pa_available_t available) { + struct ucm_port *port; + unsigned idx; + + pa_assert(device); + + if (available == device->available) + return; + + device->available = available; + + PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx) + ucm_port_update_available(port); +} + +void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { + pa_available_t available = PA_AVAILABLE_UNKNOWN; + + pa_assert(device); + + if (device->jack && device->jack->has_control) + available = device->jack->plugged_in ? PA_AVAILABLE_YES : PA_AVAILABLE_NO; + + device_set_available(device, available); } static struct ucm_port *ucm_port_new(pa_alsa_ucm_config *ucm, pa_device_port *core_port, pa_alsa_ucm_device **devices, @@ -1737,6 +1770,8 @@ static struct ucm_port *ucm_port_new(pa_alsa_ucm_config *ucm, pa_device_port *co device_add_ucm_port(devices[i], port); } + ucm_port_update_available(port); + return port; } @@ -1749,6 +1784,25 @@ static void ucm_port_free(struct ucm_port *port) { pa_xfree(port); } +static void ucm_port_update_available(struct ucm_port *port) { + pa_alsa_ucm_device *device; + unsigned idx; + pa_available_t available = PA_AVAILABLE_YES; + + pa_assert(port); + + PA_DYNARRAY_FOREACH(device, port->devices, idx) { + if (device->available == PA_AVAILABLE_UNKNOWN) + available = PA_AVAILABLE_UNKNOWN; + else if (device->available == PA_AVAILABLE_NO) { + available = PA_AVAILABLE_NO; + break; + } + } + + pa_device_port_set_available(port->core_port, available); +} + #else /* HAVE_ALSA_UCM */ /* Dummy functions for systems without UCM support */ diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index 727d281..5ccc466 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -148,8 +148,11 @@ struct pa_alsa_ucm_device { pa_dynarray *ucm_ports; /* struct ucm_port */ pa_alsa_jack *jack; + pa_available_t available; }; +void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device); + struct pa_alsa_ucm_modifier { PA_LLIST_FIELDS(pa_alsa_ucm_modifier); diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 382e40d..df4f848 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -378,16 +378,17 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) { PA_HASHMAP_FOREACH(jack, u->jacks, state) if (jack->melem == melem) { - jack->plugged_in = plugged_in; + pa_alsa_jack_set_plugged_in(jack, plugged_in); + 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); + /* When using UCM, pa_alsa_jack_set_plugged_in() maps the jack + * state to port availability. */ + continue; } + + /* When not using UCM, we have to do the jack state -> port + * availability mapping ourselves. */ + pa_assert_se(port = jack->path->port); report_port_state(port, u); } return 0; @@ -515,7 +516,7 @@ static void init_jacks(struct userdata *u) { jack->melem = pa_alsa_mixer_find(u->mixer_handle, jack->alsa_name, 0); if (!jack->melem) { pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name); - jack->has_control = false; + pa_alsa_jack_set_has_control(jack, false); continue; } snd_mixer_elem_set_callback(jack->melem, report_jack_state); diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c index cfe2a80..906ab1f 100644 --- a/src/pulsecore/device-port.c +++ b/src/pulsecore/device-port.c @@ -66,8 +66,6 @@ void pa_device_port_new_data_done(pa_device_port_new_data *data) { } void pa_device_port_set_available(pa_device_port *p, pa_available_t status) { - pa_core *core; - pa_assert(p); if (p->available == status) @@ -80,10 +78,14 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) { status == PA_AVAILABLE_NO ? "no" : "unknown"); /* Post subscriptions to the card which owns us */ - pa_assert_se(core = p->core); - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); - - pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p); + /* XXX: We need to check p->card, because this function may be called + * before the card object has been created. The card object should probably + * be created before port objects, and then p->card could be non-NULL for + * the whole lifecycle of pa_device_port. */ + if (p->card) { + pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); + pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p); + } } static void device_port_free(pa_object *o) { -- 1.9.3