[pulseaudio-discuss] [PATCH 6/6] ucm: Add support for "JackHWMute"
Tanu Kaskinen
tanu.kaskinen at linux.intel.com
Thu Feb 12 05:11:53 PST 2015
"JackHWMute" is a UCM device value that, if set, indicates that the
jack of the device mutes some other device at the hardware or driver
level. A common example would be a headphone jack that mutes built-in
speakers. PulseAudio should show such auto-muted devices as
unavailable.
Previously there was only a simple relationship: each UCM device was
related to one jack and vice versa. Now each device is still related
to exactly one jack, but each jack can be related to two devices: one
that the jack enables, and one that the jack disables (mutes).
The patch adds pa_alsa_jack_set_plugged_in() to encapsulate the logic
of marking the appropriate ports available/unavailable.
pa_alsa_jack_set_plugged_in() propagates the status to UCM devices,
and the devices propagate the status further to ports. Previously the
logic of mapping jack status to port availability was done by
module-alsa-card.c, which I think is not the right place, so I didn't
want to add the UCM "mute jack" logic there.
---
src/modules/alsa/alsa-mixer.c | 15 ++
src/modules/alsa/alsa-mixer.h | 14 ++
src/modules/alsa/alsa-ucm.c | 286 +++++++++++++++++++++++++++++++++++-
src/modules/alsa/alsa-ucm.h | 37 +++++
src/modules/alsa/module-alsa-card.c | 18 ++-
src/pulsecore/device-port.c | 14 +-
6 files changed, 362 insertions(+), 22 deletions(-)
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 2fe2ae4..423832f 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -2768,6 +2768,21 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, bool ignore_dB) {
return 0;
}
+void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in) {
+ pa_assert(jack);
+
+ if (plugged_in == jack->plugged_in)
+ return;
+
+ jack->plugged_in = plugged_in;
+
+ if (jack->device)
+ pa_alsa_ucm_device_set_available(jack->device, plugged_in);
+
+ if (jack->hw_mute)
+ pa_alsa_ucm_device_set_available(jack->hw_mute, !plugged_in);
+}
+
void pa_alsa_setting_dump(pa_alsa_setting *s) {
pa_assert(s);
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index ec39fab..dad1ea2 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -168,8 +168,22 @@ struct pa_alsa_jack {
pa_alsa_required_t required;
pa_alsa_required_t required_any;
pa_alsa_required_t required_absent;
+
+ /* UCM specific: this is the "owner" device of this jack. When the jack is
+ * plugged in, the device referenced here gets marked as available, and
+ * when the jack is unplugged, the device gets marked as unavailable. */
+ pa_alsa_ucm_device *device;
+
+ /* UCM specific: if this is non-NULL, then the referenced device gets muted
+ * by the hardware (or driver) when this jack is plugged in. The muting can't
+ * be overridden by PulseAudio, so the device gets marked as
+ * unavailable (so if a device is referenced by this field, the effect is
+ * inverse to being referenced by the "device" field). */
+ pa_alsa_ucm_device *hw_mute;
};
+void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);
+
/* A path wraps a series of elements into a single entity which can be
* used to control it as if it had a single volume slider, a single
* mute switch and a single list of selectable options. */
diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c
index ef6adcd..061b8f7 100644
--- a/src/modules/alsa/alsa-ucm.c
+++ b/src/modules/alsa/alsa-ucm.c
@@ -66,6 +66,17 @@
#ifdef HAVE_ALSA_UCM
+static void device_set_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack);
+
+struct port {
+ pa_alsa_ucm_config *ucm;
+ pa_device_port *core_port;
+
+ /* Usually a port is associated with only one device, but ports can also be
+ * a combination of several devices. */
+ pa_dynarray *devices; /* pa_alsa_ucm_device */
+};
+
struct ucm_items {
const char *id;
const char *property;
@@ -91,6 +102,7 @@ static struct ucm_items item[] = {
{"CaptureChannels", PA_ALSA_PROP_UCM_CAPTURE_CHANNELS},
{"TQ", PA_ALSA_PROP_UCM_QOS},
{"JackControl", PA_ALSA_PROP_UCM_JACK_CONTROL},
+ {"JackHWMute", PA_ALSA_PROP_UCM_JACK_HW_MUTE},
{NULL, NULL},
};
@@ -394,9 +406,12 @@ static int ucm_get_devices(pa_alsa_ucm_verb *verb, snd_use_case_mgr_t *uc_mgr) {
for (i = 0; i < num_dev; i += 2) {
pa_alsa_ucm_device *d = pa_xnew0(pa_alsa_ucm_device, 1);
+ d->verb = verb;
d->proplist = pa_proplist_new();
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->ports = pa_dynarray_new(NULL);
+ d->available = true;
PA_LLIST_PREPEND(pa_alsa_ucm_device, verb->devices, d);
}
@@ -553,6 +568,8 @@ int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index) {
const char **verb_list;
int num_verbs, i, err = 0;
+ ucm->ports = pa_dynarray_new(NULL);
+
/* is UCM available for this card ? */
err = snd_card_get_name(card_index, &card_name);
if (err < 0) {
@@ -670,6 +687,67 @@ 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 device_add_port(pa_alsa_ucm_device *device, struct port *port) {
+ pa_assert(device);
+ pa_assert(port);
+
+ pa_dynarray_append(device->ports, port);
+}
+
+static void device_remove_port(pa_alsa_ucm_device *device, struct port *port) {
+ pa_assert(device);
+ pa_assert(port);
+
+ pa_dynarray_remove_by_data(device->ports, port);
+}
+
+static struct port *port_new(pa_alsa_ucm_config *ucm, pa_device_port *core_port, pa_alsa_ucm_device **devices,
+ unsigned n_devices) {
+ struct port *port;
+ unsigned i;
+ pa_available_t available = PA_AVAILABLE_YES;
+
+ pa_assert(ucm);
+ pa_assert(core_port);
+ pa_assert(devices);
+
+ port = pa_xnew0(struct port, 1);
+ port->ucm = ucm;
+ port->core_port = core_port;
+ port->devices = pa_dynarray_new(NULL);
+
+ for (i = 0; i < n_devices; i++) {
+ device_add_port(devices[i], port);
+ pa_dynarray_append(port->devices, devices[i]);
+
+ if (!devices[i]->available)
+ available = PA_AVAILABLE_NO;
+ }
+
+ pa_device_port_set_available(core_port, available);
+
+ pa_dynarray_append(ucm->ports, port);
+
+ return port;
+}
+
+static void port_free(struct port *port) {
+ pa_assert(port);
+
+ pa_dynarray_remove_by_data(port->ucm->ports, port);
+
+ if (port->devices) {
+ pa_alsa_ucm_device *device;
+
+ while ((device = pa_dynarray_steal_last(port->devices)))
+ device_remove_port(device, port);
+
+ pa_dynarray_free(port->devices);
+ }
+
+ pa_xfree(port);
+}
+
static void ucm_add_port_combination(
pa_hashmap *hash,
pa_alsa_ucm_mapping_context *context,
@@ -688,6 +766,9 @@ static void ucm_add_port_combination(
const char *dev_name;
const char *direction;
pa_alsa_ucm_device *sorted[num], *dev;
+ pa_alsa_ucm_config *ucm;
+
+ ucm = context->ucm;
for (i = 0; i < num; i++)
sorted[i] = pdevices[i];
@@ -735,16 +816,21 @@ static void ucm_add_port_combination(
port = pa_hashmap_get(ports, name);
if (!port) {
+ struct port *ucm_port;
+
pa_device_port_new_data port_data;
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, desc);
pa_device_port_new_data_set_direction(&port_data, is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
+ pa_device_port_new_data_set_available(&port_data, PA_AVAILABLE_YES);
- port = pa_device_port_new(core, &port_data, 0);
+ port = pa_device_port_new(core, &port_data, sizeof(struct port *));
pa_device_port_new_data_done(&port_data);
- pa_assert(port);
+
+ ucm_port = port_new(ucm, port, pdevices, num);
+ *((struct port **) PA_DEVICE_PORT_DATA(port)) = ucm_port;
pa_hashmap_put(ports, port->name, port);
pa_log_debug("Add port %s: %s", port->name, port->description);
@@ -1263,16 +1349,67 @@ static int ucm_create_mapping(
return ret;
}
-static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device, const char *pre_tag) {
+static void device_set_output_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
+ pa_assert(device);
+ pa_assert(!jack || !device->mute_jack);
+
+ if (jack == device->output_jack)
+ return;
+
+ device->output_jack = jack;
+
+ if (jack)
+ pa_alsa_ucm_device_set_available(device, jack->plugged_in);
+ else
+ pa_alsa_ucm_device_set_available(device, true);
+}
+
+static void device_set_input_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
+ pa_assert(device);
+ pa_assert(!jack || !device->mute_jack);
+
+ if (jack == device->input_jack)
+ return;
+
+ device->input_jack = jack;
+
+ if (jack)
+ pa_alsa_ucm_device_set_available(device, jack->plugged_in);
+ else
+ pa_alsa_ucm_device_set_available(device, true);
+}
+
+static void device_set_mute_jack(pa_alsa_ucm_device *device, pa_alsa_jack *jack) {
+ pa_assert(device);
+ pa_assert(!jack || !device->output_jack);
+ pa_assert(!jack || !device->input_jack);
+
+ if (jack == device->mute_jack)
+ return;
+
+ device->mute_jack = jack;
+
+ if (jack)
+ pa_alsa_ucm_device_set_available(device, !jack->plugged_in);
+ else
+ pa_alsa_ucm_device_set_available(device, true);
+}
+
+static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *device, const char *pre_tag, bool implicit) {
+ const char *jack_control;
pa_alsa_jack *j;
const char *device_name;
char *name;
- const char *jack_control;
+ const char *jack_hw_mute;
pa_assert(ucm);
pa_assert(device);
pa_assert(pre_tag);
+ jack_control = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_CONTROL);
+ if (!jack_control && !implicit)
+ return NULL;
+
device_name = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_NAME);
name = pa_sprintf_malloc("%s%s", pre_tag, device_name);
@@ -1281,6 +1418,7 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
goto out;
j = pa_xnew0(pa_alsa_jack, 1);
+ j->device = device;
j->state_unplugged = PA_AVAILABLE_NO;
j->state_plugged = PA_AVAILABLE_YES;
j->name = pa_xstrdup(name);
@@ -1288,9 +1426,38 @@ 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)
j->alsa_name = pa_xstrdup(jack_control);
- else
+ else if (implicit)
j->alsa_name = pa_sprintf_malloc("%s Jack", device_name);
+ jack_hw_mute = pa_proplist_gets(device->proplist, PA_ALSA_PROP_UCM_JACK_HW_MUTE);
+ if (jack_hw_mute) {
+ pa_alsa_ucm_device *device2;
+ bool device_found = false;
+
+ PA_LLIST_FOREACH(device2, device->verb->devices) {
+ const char *device2_name;
+
+ device2_name = pa_proplist_gets(device2->proplist, PA_ALSA_PROP_UCM_NAME);
+
+ if (pa_streq(device2_name, jack_hw_mute)) {
+ device_found = true;
+
+ if (!device2->output_jack && !device2->input_jack) {
+ j->hw_mute = device2;
+ device_set_mute_jack(device2, j);
+ } else {
+ pa_log("[%s] Ignoring \"JackHWMute\", because device availability is already governed by jack \"%s\".",
+ device2_name, device2->output_jack ? device2->output_jack->name : device2->input_jack->name);
+ }
+
+ break;
+ }
+ }
+
+ if (!device_found)
+ pa_log("[%s] JackHWMute references unknown device: %s", device_name, jack_hw_mute);
+ }
+
PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
out:
@@ -1349,7 +1516,12 @@ static int ucm_create_profile(
if (verb_info[i].id == NULL)
p->priority = 1000;
+ /* Get jacks for devices (and also associate the device with mappings). No
+ * implicit jacks are allowed (that is, if "JackControl" is not set, a jack
+ * will not be created). */
PA_LLIST_FOREACH(dev, verb->devices) {
+ pa_alsa_jack *jack;
+
name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
@@ -1358,9 +1530,57 @@ static int ucm_create_profile(
ucm_create_mapping(ucm, ps, p, dev, verb_name, name, sink, source);
if (sink)
- dev->output_jack = ucm_get_jack(ucm, dev, PA_UCM_PRE_TAG_OUTPUT);
+ jack = ucm_get_jack(ucm, dev, PA_UCM_PRE_TAG_OUTPUT, false);
+
+ if (source)
+ jack = ucm_get_jack(ucm, dev, PA_UCM_PRE_TAG_INPUT, false);
+
+ if (jack) {
+ if (dev->mute_jack) {
+ pa_log("[%s] Overriding \"JackHWMute\", because \"JackControl\" is set and takes precedence.", name);
+ device_set_mute_jack(dev, NULL);
+ }
+ }
+
+ if (sink)
+ device_set_output_jack(dev, jack);
+
if (source)
- dev->input_jack = ucm_get_jack(ucm, dev, PA_UCM_PRE_TAG_INPUT);
+ device_set_input_jack(dev, jack);
+ }
+
+ /* Now go over the device list again and get implicit jacks for devices
+ * that don't have "JackControl" set nor are referenced by any other
+ * device's "JackHWMute". "Implicit" means that the kcontrol name is not
+ * explicitly configured; instead, we generate the kcontrol name from the
+ * device name.
+ *
+ * We need a separate loop instead of creating the implicit jacks in the
+ * same loop as the other jacks, because the full set of mute jack
+ * relationships isn't known before we have iterated over all devices,
+ * and the relationships need to be known before creating the implicit
+ * jacks, since otherwise the implicit jacks could override the mute jacks,
+ * which we don't want. */
+ PA_LLIST_FOREACH(dev, verb->devices) {
+ pa_alsa_jack *jack;
+
+ if (dev->output_jack || dev->input_jack || dev->mute_jack)
+ continue;
+
+ name = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_NAME);
+
+ sink = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SINK);
+ source = pa_proplist_gets(dev->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+ if (sink) {
+ jack = ucm_get_jack(ucm, dev, PA_UCM_PRE_TAG_OUTPUT, true);
+ device_set_output_jack(dev, jack);
+ }
+
+ if (source) {
+ jack = ucm_get_jack(ucm, dev, PA_UCM_PRE_TAG_INPUT, true);
+ device_set_input_jack(dev, jack);
+ }
}
/* Now find modifiers that have their own PlaybackPCM and create
@@ -1451,7 +1671,10 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
PA_IDXSET_FOREACH(dev, context->ucm_devices, idx) {
pa_alsa_jack *jack;
jack = m->direction == PA_ALSA_DIRECTION_OUTPUT ? dev->output_jack : dev->input_jack;
- pa_assert (jack);
+
+ if (!jack)
+ continue;
+
jack->has_control = pa_alsa_mixer_find(mixer_handle, jack->alsa_name, 0) != NULL;
pa_log_info("UCM jack %s has_control=%d", jack->name, jack->has_control);
}
@@ -1565,6 +1788,12 @@ static void free_verb(pa_alsa_ucm_verb *verb) {
PA_LLIST_FOREACH_SAFE(di, dn, verb->devices) {
PA_LLIST_REMOVE(pa_alsa_ucm_device, verb->devices, di);
+
+ if (di->ports) {
+ pa_assert(pa_dynarray_size(di->ports) == 0);
+ pa_dynarray_free(di->ports);
+ }
+
pa_proplist_free(di->proplist);
if (di->conflicting_devices)
pa_idxset_free(di->conflicting_devices, NULL);
@@ -1591,6 +1820,15 @@ void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm) {
pa_alsa_ucm_verb *vi, *vn;
pa_alsa_jack *ji, *jn;
+ if (ucm->ports) {
+ struct port *port;
+
+ while ((port = pa_dynarray_last(ucm->ports)))
+ port_free(port);
+
+ pa_dynarray_free(ucm->ports);
+ }
+
PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
free_verb(vi);
@@ -1685,6 +1923,35 @@ void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_
}
}
+void pa_alsa_ucm_device_set_available(pa_alsa_ucm_device *device, bool available) {
+ struct port *port;
+ unsigned idx;
+
+ pa_assert(device);
+
+ if (available == device->available)
+ return;
+
+ device->available = available;
+
+ PA_DYNARRAY_FOREACH(port, device->ports, idx) {
+ pa_alsa_ucm_device *device2;
+ unsigned idx2;
+ pa_available_t port_available = PA_AVAILABLE_YES;
+
+ /* If there are any devices associated with the port that are
+ * unavailable, then the port is considered unavailable. */
+ PA_DYNARRAY_FOREACH(device2, port->devices, idx2) {
+ if (!device2->available) {
+ port_available = PA_AVAILABLE_NO;
+ break;
+ }
+ }
+
+ pa_device_port_set_available(port->core_port, port_available);
+ }
+}
+
#else /* HAVE_ALSA_UCM */
/* Dummy functions for systems without UCM support */
@@ -1739,4 +2006,7 @@ void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, p
void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) {
}
+void pa_alsa_ucm_device_set_available(pa_alsa_ucm_device *device, bool available) {
+}
+
#endif
diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h
index a8c8090..1d76889 100644
--- a/src/modules/alsa/alsa-ucm.h
+++ b/src/modules/alsa/alsa-ucm.h
@@ -30,6 +30,8 @@ typedef void snd_use_case_mgr_t;
#include "alsa-mixer.h"
+#include <pulsecore/dynarray.h>
+
/** For devices: List of verbs, devices or modifiers available */
#define PA_ALSA_PROP_UCM_NAME "alsa.ucm.name"
@@ -87,6 +89,9 @@ typedef void snd_use_case_mgr_t;
/* Corresponds to the "JackControl" UCM value. */
#define PA_ALSA_PROP_UCM_JACK_CONTROL "alsa.ucm.jack_control"
+/* Corresponds to the "JackHWMute" UCM value. */
+#define PA_ALSA_PROP_UCM_JACK_HW_MUTE "alsa.ucm.jack_hw_mute"
+
typedef struct pa_alsa_ucm_verb pa_alsa_ucm_verb;
typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
@@ -122,7 +127,15 @@ void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_
/* UCM - Use Case Manager is available on some audio cards */
+struct pa_alsa_ucm_port {
+ pa_device_port *core_port;
+ pa_dynarray *devices; /* pa_alsa_ucm_device */
+ bool available;
+};
+
struct pa_alsa_ucm_device {
+ pa_alsa_ucm_verb *verb;
+
PA_LLIST_FIELDS(pa_alsa_ucm_device);
pa_proplist *proplist;
@@ -142,10 +155,33 @@ struct pa_alsa_ucm_device {
pa_idxset *conflicting_devices;
pa_idxset *supported_devices;
+ pa_dynarray *ports; /* pa_alsa_ucm_port */
+
+ /* These jacks are owned by this device. When these jacks are plugged in,
+ * the device is marked as available, and when these jacks are unplugged,
+ * the device is marked as unavailable. Not all devices have jacks
+ * associated with them; those devices are always marked as available.
+ *
+ * XXX: It's probably pointless to have separate jacks for input and
+ * output. Both jack objects will anyway reference the same alsa
+ * kcontrol, so they will always have the same state. */
pa_alsa_jack *input_jack;
pa_alsa_jack *output_jack;
+
+ /* The "mute jack" is owned by some other device. The mute jack makes this
+ * device unavailable when it's plugged in. A device can't have both a
+ * "normal" jack (meaning the output_jack or input_jack field) and a mute
+ * jack, because there would be ambiguity about which jack is controlling
+ * the device availability. If the configuration tries to set both types of
+ * jacks for one device, the normal jack has precedence and mute_jack gets
+ * set to NULL. */
+ pa_alsa_jack *mute_jack;
+
+ bool available;
};
+void pa_alsa_ucm_device_set_available(pa_alsa_ucm_device *device, bool available);
+
struct pa_alsa_ucm_modifier {
PA_LLIST_FIELDS(pa_alsa_ucm_modifier);
@@ -185,6 +221,7 @@ struct pa_alsa_ucm_config {
PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
PA_LLIST_HEAD(pa_alsa_jack, jacks);
+ pa_dynarray *ports; /* struct port */
};
struct pa_alsa_ucm_mapping_context {
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index a7fec04..7ac750f 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -384,16 +384,18 @@ 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(jack->path);
+ pa_assert_se(port = jack->path->port);
report_port_state(port, u);
}
return 0;
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
More information about the pulseaudio-discuss
mailing list