153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 653a5a1b3Sopenharmony_ci Copyright 2011 David Henningsson, Canonical Ltd. 753a5a1b3Sopenharmony_ci 853a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 953a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 1053a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1153a5a1b3Sopenharmony_ci or (at your option) any later version. 1253a5a1b3Sopenharmony_ci 1353a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1453a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1553a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1653a5a1b3Sopenharmony_ci General Public License for more details. 1753a5a1b3Sopenharmony_ci 1853a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1953a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 2053a5a1b3Sopenharmony_ci***/ 2153a5a1b3Sopenharmony_ci 2253a5a1b3Sopenharmony_ci#include "device-port.h" 2353a5a1b3Sopenharmony_ci#include <pulsecore/card.h> 2453a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 2553a5a1b3Sopenharmony_ci 2653a5a1b3Sopenharmony_ciPA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object); 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_cipa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data) { 2953a5a1b3Sopenharmony_ci pa_assert(data); 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci pa_zero(*data); 3253a5a1b3Sopenharmony_ci data->type = PA_DEVICE_PORT_TYPE_UNKNOWN; 3353a5a1b3Sopenharmony_ci data->available = PA_AVAILABLE_UNKNOWN; 3453a5a1b3Sopenharmony_ci return data; 3553a5a1b3Sopenharmony_ci} 3653a5a1b3Sopenharmony_ci 3753a5a1b3Sopenharmony_civoid pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name) { 3853a5a1b3Sopenharmony_ci pa_assert(data); 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci pa_xfree(data->name); 4153a5a1b3Sopenharmony_ci data->name = pa_xstrdup(name); 4253a5a1b3Sopenharmony_ci} 4353a5a1b3Sopenharmony_ci 4453a5a1b3Sopenharmony_civoid pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description) { 4553a5a1b3Sopenharmony_ci pa_assert(data); 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_ci pa_xfree(data->description); 4853a5a1b3Sopenharmony_ci data->description = pa_xstrdup(description); 4953a5a1b3Sopenharmony_ci} 5053a5a1b3Sopenharmony_ci 5153a5a1b3Sopenharmony_civoid pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available) { 5253a5a1b3Sopenharmony_ci pa_assert(data); 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ci data->available = available; 5553a5a1b3Sopenharmony_ci} 5653a5a1b3Sopenharmony_ci 5753a5a1b3Sopenharmony_civoid pa_device_port_new_data_set_availability_group(pa_device_port_new_data *data, const char *group) { 5853a5a1b3Sopenharmony_ci pa_assert(data); 5953a5a1b3Sopenharmony_ci 6053a5a1b3Sopenharmony_ci pa_xfree(data->availability_group); 6153a5a1b3Sopenharmony_ci data->availability_group = pa_xstrdup(group); 6253a5a1b3Sopenharmony_ci} 6353a5a1b3Sopenharmony_ci 6453a5a1b3Sopenharmony_civoid pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction) { 6553a5a1b3Sopenharmony_ci pa_assert(data); 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_ci data->direction = direction; 6853a5a1b3Sopenharmony_ci} 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_civoid pa_device_port_new_data_set_type(pa_device_port_new_data *data, pa_device_port_type_t type) { 7153a5a1b3Sopenharmony_ci pa_assert(data); 7253a5a1b3Sopenharmony_ci 7353a5a1b3Sopenharmony_ci data->type = type; 7453a5a1b3Sopenharmony_ci} 7553a5a1b3Sopenharmony_ci 7653a5a1b3Sopenharmony_civoid pa_device_port_new_data_done(pa_device_port_new_data *data) { 7753a5a1b3Sopenharmony_ci pa_assert(data); 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci pa_xfree(data->name); 8053a5a1b3Sopenharmony_ci pa_xfree(data->description); 8153a5a1b3Sopenharmony_ci pa_xfree(data->availability_group); 8253a5a1b3Sopenharmony_ci} 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_civoid pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) { 8553a5a1b3Sopenharmony_ci pa_assert(p); 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci if (!pa_safe_streq(p->preferred_profile, new_pp)) { 8853a5a1b3Sopenharmony_ci pa_xfree(p->preferred_profile); 8953a5a1b3Sopenharmony_ci p->preferred_profile = pa_xstrdup(new_pp); 9053a5a1b3Sopenharmony_ci } 9153a5a1b3Sopenharmony_ci} 9253a5a1b3Sopenharmony_ci 9353a5a1b3Sopenharmony_civoid pa_device_port_set_available(pa_device_port *p, pa_available_t status) { 9453a5a1b3Sopenharmony_ci pa_assert(p); 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_ci if (p->available == status) 9753a5a1b3Sopenharmony_ci return; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci/* pa_assert(status != PA_AVAILABLE_UNKNOWN); */ 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci p->available = status; 10253a5a1b3Sopenharmony_ci pa_log_debug("Setting port %s to status %s", p->name, pa_available_to_string(status)); 10353a5a1b3Sopenharmony_ci 10453a5a1b3Sopenharmony_ci /* Post subscriptions to the card which owns us */ 10553a5a1b3Sopenharmony_ci /* XXX: We need to check p->card, because this function may be called 10653a5a1b3Sopenharmony_ci * before the card object has been created. The card object should probably 10753a5a1b3Sopenharmony_ci * be created before port objects, and then p->card could be non-NULL for 10853a5a1b3Sopenharmony_ci * the whole lifecycle of pa_device_port. */ 10953a5a1b3Sopenharmony_ci if (p->card && p->card->linked) { 11053a5a1b3Sopenharmony_ci pa_sink *sink; 11153a5a1b3Sopenharmony_ci pa_source *source; 11253a5a1b3Sopenharmony_ci 11353a5a1b3Sopenharmony_ci pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci sink = pa_device_port_get_sink(p); 11653a5a1b3Sopenharmony_ci source = pa_device_port_get_source(p); 11753a5a1b3Sopenharmony_ci if (sink) 11853a5a1b3Sopenharmony_ci pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, sink->index); 11953a5a1b3Sopenharmony_ci if (source) 12053a5a1b3Sopenharmony_ci pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, source->index); 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_ci /* A sink or source whose active port is unavailable can't be the 12353a5a1b3Sopenharmony_ci * default sink/source, so port availability changes may affect the 12453a5a1b3Sopenharmony_ci * default sink/source choice. */ 12553a5a1b3Sopenharmony_ci if (p->direction == PA_DIRECTION_OUTPUT) 12653a5a1b3Sopenharmony_ci pa_core_update_default_sink(p->core); 12753a5a1b3Sopenharmony_ci else 12853a5a1b3Sopenharmony_ci pa_core_update_default_source(p->core); 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_ci if (p->direction == PA_DIRECTION_OUTPUT) { 13153a5a1b3Sopenharmony_ci if (sink && p == sink->active_port) { 13253a5a1b3Sopenharmony_ci if (sink->active_port->available == PA_AVAILABLE_NO) { 13353a5a1b3Sopenharmony_ci if (p->core->rescue_streams) 13453a5a1b3Sopenharmony_ci pa_sink_move_streams_to_default_sink(p->core, sink, false); 13553a5a1b3Sopenharmony_ci } else 13653a5a1b3Sopenharmony_ci pa_core_move_streams_to_newly_available_preferred_sink(p->core, sink); 13753a5a1b3Sopenharmony_ci } 13853a5a1b3Sopenharmony_ci } else { 13953a5a1b3Sopenharmony_ci if (source && p == source->active_port) { 14053a5a1b3Sopenharmony_ci if (source->active_port->available == PA_AVAILABLE_NO) { 14153a5a1b3Sopenharmony_ci if (p->core->rescue_streams) 14253a5a1b3Sopenharmony_ci pa_source_move_streams_to_default_source(p->core, source, false); 14353a5a1b3Sopenharmony_ci } else 14453a5a1b3Sopenharmony_ci pa_core_move_streams_to_newly_available_preferred_source(p->core, source); 14553a5a1b3Sopenharmony_ci } 14653a5a1b3Sopenharmony_ci } 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci /* This may cause the sink and source pointers to become invalid, if 14953a5a1b3Sopenharmony_ci * the availability change causes the card profile to get switched. If 15053a5a1b3Sopenharmony_ci * you add code after this line, remember to take that into account. */ 15153a5a1b3Sopenharmony_ci pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p); 15253a5a1b3Sopenharmony_ci } 15353a5a1b3Sopenharmony_ci} 15453a5a1b3Sopenharmony_ci 15553a5a1b3Sopenharmony_cistatic void device_port_free(pa_object *o) { 15653a5a1b3Sopenharmony_ci pa_device_port *p = PA_DEVICE_PORT(o); 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci pa_assert(p); 15953a5a1b3Sopenharmony_ci pa_assert(pa_device_port_refcnt(p) == 0); 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci if (p->impl_free) 16253a5a1b3Sopenharmony_ci p->impl_free(p); 16353a5a1b3Sopenharmony_ci 16453a5a1b3Sopenharmony_ci if (p->proplist) 16553a5a1b3Sopenharmony_ci pa_proplist_free(p->proplist); 16653a5a1b3Sopenharmony_ci 16753a5a1b3Sopenharmony_ci if (p->profiles) 16853a5a1b3Sopenharmony_ci pa_hashmap_free(p->profiles); 16953a5a1b3Sopenharmony_ci 17053a5a1b3Sopenharmony_ci pa_xfree(p->availability_group); 17153a5a1b3Sopenharmony_ci pa_xfree(p->preferred_profile); 17253a5a1b3Sopenharmony_ci pa_xfree(p->name); 17353a5a1b3Sopenharmony_ci pa_xfree(p->description); 17453a5a1b3Sopenharmony_ci pa_xfree(p); 17553a5a1b3Sopenharmony_ci} 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_cipa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra) { 17853a5a1b3Sopenharmony_ci pa_device_port *p; 17953a5a1b3Sopenharmony_ci 18053a5a1b3Sopenharmony_ci pa_assert(data); 18153a5a1b3Sopenharmony_ci pa_assert(data->name); 18253a5a1b3Sopenharmony_ci pa_assert(data->description); 18353a5a1b3Sopenharmony_ci pa_assert(data->direction == PA_DIRECTION_OUTPUT || data->direction == PA_DIRECTION_INPUT); 18453a5a1b3Sopenharmony_ci 18553a5a1b3Sopenharmony_ci p = PA_DEVICE_PORT(pa_object_new_internal(PA_ALIGN(sizeof(pa_device_port)) + extra, pa_device_port_type_id, pa_device_port_check_type)); 18653a5a1b3Sopenharmony_ci p->parent.free = device_port_free; 18753a5a1b3Sopenharmony_ci 18853a5a1b3Sopenharmony_ci p->name = data->name; 18953a5a1b3Sopenharmony_ci data->name = NULL; 19053a5a1b3Sopenharmony_ci p->description = data->description; 19153a5a1b3Sopenharmony_ci data->description = NULL; 19253a5a1b3Sopenharmony_ci p->core = c; 19353a5a1b3Sopenharmony_ci p->card = NULL; 19453a5a1b3Sopenharmony_ci p->priority = 0; 19553a5a1b3Sopenharmony_ci p->available = data->available; 19653a5a1b3Sopenharmony_ci p->availability_group = data->availability_group; 19753a5a1b3Sopenharmony_ci data->availability_group = NULL; 19853a5a1b3Sopenharmony_ci p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 19953a5a1b3Sopenharmony_ci p->direction = data->direction; 20053a5a1b3Sopenharmony_ci p->type = data->type; 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_ci p->latency_offset = 0; 20353a5a1b3Sopenharmony_ci p->proplist = pa_proplist_new(); 20453a5a1b3Sopenharmony_ci 20553a5a1b3Sopenharmony_ci return p; 20653a5a1b3Sopenharmony_ci} 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_civoid pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) { 20953a5a1b3Sopenharmony_ci uint32_t state; 21053a5a1b3Sopenharmony_ci pa_core *core; 21153a5a1b3Sopenharmony_ci 21253a5a1b3Sopenharmony_ci pa_assert(p); 21353a5a1b3Sopenharmony_ci 21453a5a1b3Sopenharmony_ci if (offset == p->latency_offset) 21553a5a1b3Sopenharmony_ci return; 21653a5a1b3Sopenharmony_ci 21753a5a1b3Sopenharmony_ci p->latency_offset = offset; 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci switch (p->direction) { 22053a5a1b3Sopenharmony_ci case PA_DIRECTION_OUTPUT: { 22153a5a1b3Sopenharmony_ci pa_sink *sink; 22253a5a1b3Sopenharmony_ci 22353a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, p->core->sinks, state) { 22453a5a1b3Sopenharmony_ci if (sink->active_port == p) { 22553a5a1b3Sopenharmony_ci pa_sink_set_port_latency_offset(sink, p->latency_offset); 22653a5a1b3Sopenharmony_ci break; 22753a5a1b3Sopenharmony_ci } 22853a5a1b3Sopenharmony_ci } 22953a5a1b3Sopenharmony_ci 23053a5a1b3Sopenharmony_ci break; 23153a5a1b3Sopenharmony_ci } 23253a5a1b3Sopenharmony_ci 23353a5a1b3Sopenharmony_ci case PA_DIRECTION_INPUT: { 23453a5a1b3Sopenharmony_ci pa_source *source; 23553a5a1b3Sopenharmony_ci 23653a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, p->core->sources, state) { 23753a5a1b3Sopenharmony_ci if (source->active_port == p) { 23853a5a1b3Sopenharmony_ci pa_source_set_port_latency_offset(source, p->latency_offset); 23953a5a1b3Sopenharmony_ci break; 24053a5a1b3Sopenharmony_ci } 24153a5a1b3Sopenharmony_ci } 24253a5a1b3Sopenharmony_ci 24353a5a1b3Sopenharmony_ci break; 24453a5a1b3Sopenharmony_ci } 24553a5a1b3Sopenharmony_ci } 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci pa_assert_se(core = p->core); 24853a5a1b3Sopenharmony_ci pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); 24953a5a1b3Sopenharmony_ci pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], p); 25053a5a1b3Sopenharmony_ci} 25153a5a1b3Sopenharmony_ci 25253a5a1b3Sopenharmony_cipa_device_port *pa_device_port_find_best(pa_hashmap *ports) 25353a5a1b3Sopenharmony_ci{ 25453a5a1b3Sopenharmony_ci void *state; 25553a5a1b3Sopenharmony_ci pa_device_port *p, *best = NULL; 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_ci if (!ports) 25853a5a1b3Sopenharmony_ci return NULL; 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci /* First run: skip unavailable ports */ 26153a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(p, ports, state) { 26253a5a1b3Sopenharmony_ci if (p->available == PA_AVAILABLE_NO) 26353a5a1b3Sopenharmony_ci continue; 26453a5a1b3Sopenharmony_ci 26553a5a1b3Sopenharmony_ci if (!best || p->priority > best->priority) 26653a5a1b3Sopenharmony_ci best = p; 26753a5a1b3Sopenharmony_ci } 26853a5a1b3Sopenharmony_ci 26953a5a1b3Sopenharmony_ci /* Second run: if only unavailable ports exist, still suggest a port */ 27053a5a1b3Sopenharmony_ci if (!best) { 27153a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(p, ports, state) 27253a5a1b3Sopenharmony_ci if (!best || p->priority > best->priority) 27353a5a1b3Sopenharmony_ci best = p; 27453a5a1b3Sopenharmony_ci } 27553a5a1b3Sopenharmony_ci 27653a5a1b3Sopenharmony_ci return best; 27753a5a1b3Sopenharmony_ci} 27853a5a1b3Sopenharmony_ci 27953a5a1b3Sopenharmony_cipa_sink *pa_device_port_get_sink(pa_device_port *p) { 28053a5a1b3Sopenharmony_ci pa_sink *rs = NULL; 28153a5a1b3Sopenharmony_ci pa_sink *sink; 28253a5a1b3Sopenharmony_ci uint32_t state; 28353a5a1b3Sopenharmony_ci 28453a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, p->card->sinks, state) 28553a5a1b3Sopenharmony_ci if (p == pa_hashmap_get(sink->ports, p->name)) { 28653a5a1b3Sopenharmony_ci rs = sink; 28753a5a1b3Sopenharmony_ci break; 28853a5a1b3Sopenharmony_ci } 28953a5a1b3Sopenharmony_ci return rs; 29053a5a1b3Sopenharmony_ci} 29153a5a1b3Sopenharmony_ci 29253a5a1b3Sopenharmony_cipa_source *pa_device_port_get_source(pa_device_port *p) { 29353a5a1b3Sopenharmony_ci pa_source *rs = NULL; 29453a5a1b3Sopenharmony_ci pa_source *source; 29553a5a1b3Sopenharmony_ci uint32_t state; 29653a5a1b3Sopenharmony_ci 29753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, p->card->sources, state) 29853a5a1b3Sopenharmony_ci if (p == pa_hashmap_get(source->ports, p->name)) { 29953a5a1b3Sopenharmony_ci rs = source; 30053a5a1b3Sopenharmony_ci break; 30153a5a1b3Sopenharmony_ci } 30253a5a1b3Sopenharmony_ci return rs; 30353a5a1b3Sopenharmony_ci} 304