From f178a5b12eb5a9eb4fe92672f43d47c528ab221e Mon Sep 17 00:00:00 2001 From: Georg Chini Date: Thu, 7 Jan 2021 19:27:23 +0100 Subject: [PATCH] card: Add messages to enable/disable jack detection With this patch, messages can be sent to the cards to enable/disable jack detection for the whole card or single ports, manually set a port state and to retrieve the current state of jack detection and port availability. --- doc/messaging_api.txt | 25 ++++++++ src/modules/alsa/alsa-ucm.c | 4 +- src/modules/alsa/module-alsa-card.c | 4 +- src/modules/bluetooth/module-bluez5-device.c | 4 +- src/pulsecore/card.c | 61 +++++++++++++++----- src/pulsecore/device-port.c | 15 ++++- src/pulsecore/device-port.h | 3 +- 7 files changed, 95 insertions(+), 21 deletions(-) diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt index ad0774fde..6a1d077c6 100644 --- a/doc/messaging_api.txt +++ b/doc/messaging_api.txt @@ -47,3 +47,28 @@ Object path: /card/ Message: get-profile-sticky Parameters: None Return value: JSON "true" or "false" + +Object path: /cards/ +Message: set-jack-detection +Parameters: {"port_name|all":true|false} +Return value: None + +Object path: /cards/ +Message: get-jack-detection +Parameters: "port_name|all" +Return value: {"port_name":true|false} +If "all" is specified, the returned object looks different: +{"Card name":"card_name", "Detection states":{"port_name":true|false, ...}} + +Object path: /cards/ +Message: set-port-state +Parameters: {"port_name|all":0|1|2} +Return value: None +0=unknown, 1=no, 2=yes + +Object path: /cards/ +Message: get-port-state +Parameters: "port_name|all" +Return value: {"port_name":0|1|2} +If "all" is specified, the returned object looks different: +{"Card name":"card_name", "Port states":{"port_name":0|1|2, ...}} diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 018c01739..af37b44af 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -2480,7 +2480,7 @@ static void device_set_available(pa_alsa_ucm_device *device, pa_available_t avai device->available = available; PA_DYNARRAY_FOREACH(port, device->ucm_ports, idx) - pa_device_port_set_available(port->core_port, port->device->available); + pa_device_port_set_available(port->core_port, port->device->available, false); } void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device) { @@ -2519,7 +2519,7 @@ static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config * port->paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, (pa_free_cb_t) pa_alsa_path_free); - pa_device_port_set_available(port->core_port, port->device->available); + pa_device_port_set_available(port->core_port, port->device->available, false); } static void ucm_port_data_free(pa_device_port *port) { diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 05c87c6bb..80a072e2d 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -435,10 +435,10 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) { for (tp = tports; tp->port; tp++) if (tp->avail != PA_AVAILABLE_NO) - pa_device_port_set_available(tp->port, tp->avail); + pa_device_port_set_available(tp->port, tp->avail, false); for (tp = tports; tp->port; tp++) if (tp->avail == PA_AVAILABLE_NO) - pa_device_port_set_available(tp->port, tp->avail); + pa_device_port_set_available(tp->port, tp->avail, false); for (tp = tports; tp->port; tp++) { pa_alsa_port_data *data; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index dc6809ce5..2d76b6905 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -2340,9 +2340,9 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot /* Update port availability */ pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name)); - pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_OUTPUT)); + pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_OUTPUT), false); pa_assert_se(port = pa_hashmap_get(u->card->ports, u->input_port_name)); - pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_INPUT)); + pa_device_port_set_available(port, get_port_availability(u, PA_DIRECTION_INPUT), false); /* Acquire or release transport as needed */ acquire = (t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile); diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 703855707..b9fbda384 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -163,10 +163,10 @@ static int card_message_handler(const char *object_path, const char *message, co pa_card *c = userdata; const char *port_name; bool jack_detection; + int64_t port_state; void *state = NULL; pa_device_port *port = NULL; const pa_json_object *o; - int64_t current_state; char *message_handler_path; pa_assert(c); @@ -240,7 +240,7 @@ static int card_message_handler(const char *object_path, const char *message, co pa_log_info("Parameters type does not match message command"); return -PA_ERR_INVALID; } - current_state = pa_json_object_get_int(o); + port_state = pa_json_object_get_int(o); } } else { @@ -256,15 +256,35 @@ static int card_message_handler(const char *object_path, const char *message, co } } + state = NULL; + if (pa_streq(message, "set-jack-detection")) { if (!port) { - PA_HASHMAP_FOREACH(port, c->ports, state) - port->jack_detection = jack_detection; + PA_HASHMAP_FOREACH(port, c->ports, state) { + pa_available_t avail = PA_AVAILABLE_UNKNOWN; + + /* If jack detection was enabled, set the port state + * to the hardware state. */ + if (jack_detection) + avail = port->hw_available; + + port->jack_detection = jack_detection; + pa_device_port_set_available(port, avail, true); + } + + } else { + pa_available_t avail = PA_AVAILABLE_UNKNOWN; + + /* If jack detection was enabled, set the port state + * to the hardware state. */ + if (jack_detection) + avail = port->hw_available; - } else port->jack_detection = jack_detection; + pa_device_port_set_available(port, avail, true); + } return PA_OK; @@ -291,10 +311,25 @@ static int card_message_handler(const char *object_path, const char *message, co } else if (pa_streq(message, "set-port-state")) { - /* Not implemented because jack_detection is still unused - * and manually setting a port state would require to disable - * jack detection */ - return -PA_ERR_NOTIMPLEMENTED; + /* Validate port state parameter */ + if ((pa_available_t) port_state > PA_AVAILABLE_YES) + return -PA_ERR_INVALID; + + if (!port) { + + PA_HASHMAP_FOREACH(port, c->ports, state) { + + port->jack_detection = false; + pa_device_port_set_available(port, (pa_available_t) port_state, true); + } + + } else { + + port->jack_detection = false; + pa_device_port_set_available(port, (pa_available_t) port_state, true); + } + + return PA_OK; } else if (pa_streq(message, "get-port-state")) { pa_json_encoder *encoder; @@ -306,14 +341,14 @@ static int card_message_handler(const char *object_path, const char *message, co pa_json_encoder_begin_member_object(encoder, "Port states"); PA_HASHMAP_FOREACH(port, c->ports, state) { - current_state = port->available; - pa_json_encoder_add_member_int(encoder, port->name, current_state); + port_state = port->available; + pa_json_encoder_add_member_int(encoder, port->name, port_state); } pa_json_encoder_end_object(encoder); } else { - current_state = port->available; - pa_json_encoder_add_member_int(encoder, port->name, current_state); + port_state = port->available; + pa_json_encoder_add_member_int(encoder, port->name, port_state); } pa_json_encoder_end_object(encoder); diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c index b70d7eb1d..beb5ab693 100644 --- a/src/pulsecore/device-port.c +++ b/src/pulsecore/device-port.c @@ -90,12 +90,25 @@ void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) } } -void pa_device_port_set_available(pa_device_port *p, pa_available_t status) { +void pa_device_port_set_available(pa_device_port *p, pa_available_t status, bool force) { pa_assert(p); + /* If force is not set, status reflects the state of the port from a + * hardware perspective. We need to keep track of the real port state + * so that we can go back to it once jack detection is enabled for the + * port. If force is set, we are updating the port state manually, so + * the hardware state is unaffected. */ + if (!force) + p->hw_available = status; + if (p->available == status) return; + /* Do not set the port state if jack detection is disabled for the port + * unless we are setting the state manually. */ + if (!force && !p->jack_detection) + return; + /* pa_assert(status != PA_AVAILABLE_UNKNOWN); */ p->available = status; diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h index 81438e64e..2953e9558 100644 --- a/src/pulsecore/device-port.h +++ b/src/pulsecore/device-port.h @@ -50,6 +50,7 @@ struct pa_device_port { unsigned priority; pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */ char *availability_group; /* a string indentifier which determine the group of devices handling the available state simulteneously */ + pa_available_t hw_available; pa_proplist *proplist; pa_hashmap *profiles; /* Does not own the profiles */ @@ -88,7 +89,7 @@ void pa_device_port_new_data_done(pa_device_port_new_data *data); pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra); /* The port's available status has changed */ -void pa_device_port_set_available(pa_device_port *p, pa_available_t available); +void pa_device_port_set_available(pa_device_port *p, pa_available_t available, bool force); void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset); void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp);