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