1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6  Copyright 2011 David Henningsson, Canonical Ltd.
7
8  PulseAudio is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 2.1 of the License,
11  or (at your option) any later version.
12
13  PulseAudio is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU Lesser General Public License
19  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include "device-port.h"
23#include <pulsecore/card.h>
24#include <pulsecore/core-util.h>
25
26PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object);
27
28pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data) {
29    pa_assert(data);
30
31    pa_zero(*data);
32    data->type = PA_DEVICE_PORT_TYPE_UNKNOWN;
33    data->available = PA_AVAILABLE_UNKNOWN;
34    return data;
35}
36
37void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name) {
38    pa_assert(data);
39
40    pa_xfree(data->name);
41    data->name = pa_xstrdup(name);
42}
43
44void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description) {
45    pa_assert(data);
46
47    pa_xfree(data->description);
48    data->description = pa_xstrdup(description);
49}
50
51void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available) {
52    pa_assert(data);
53
54    data->available = available;
55}
56
57void pa_device_port_new_data_set_availability_group(pa_device_port_new_data *data, const char *group) {
58    pa_assert(data);
59
60    pa_xfree(data->availability_group);
61    data->availability_group = pa_xstrdup(group);
62}
63
64void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction) {
65    pa_assert(data);
66
67    data->direction = direction;
68}
69
70void pa_device_port_new_data_set_type(pa_device_port_new_data *data, pa_device_port_type_t type) {
71    pa_assert(data);
72
73    data->type = type;
74}
75
76void pa_device_port_new_data_done(pa_device_port_new_data *data) {
77    pa_assert(data);
78
79    pa_xfree(data->name);
80    pa_xfree(data->description);
81    pa_xfree(data->availability_group);
82}
83
84void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) {
85    pa_assert(p);
86
87    if (!pa_safe_streq(p->preferred_profile, new_pp)) {
88        pa_xfree(p->preferred_profile);
89        p->preferred_profile = pa_xstrdup(new_pp);
90    }
91}
92
93void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
94    pa_assert(p);
95
96    if (p->available == status)
97        return;
98
99/*    pa_assert(status != PA_AVAILABLE_UNKNOWN); */
100
101    p->available = status;
102    pa_log_debug("Setting port %s to status %s", p->name, pa_available_to_string(status));
103
104    /* Post subscriptions to the card which owns us */
105    /* XXX: We need to check p->card, because this function may be called
106     * before the card object has been created. The card object should probably
107     * be created before port objects, and then p->card could be non-NULL for
108     * the whole lifecycle of pa_device_port. */
109    if (p->card && p->card->linked) {
110        pa_sink *sink;
111        pa_source *source;
112
113        pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
114
115        sink = pa_device_port_get_sink(p);
116        source = pa_device_port_get_source(p);
117        if (sink)
118            pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, sink->index);
119        if (source)
120            pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, source->index);
121
122        /* A sink or source whose active port is unavailable can't be the
123         * default sink/source, so port availability changes may affect the
124         * default sink/source choice. */
125        if (p->direction == PA_DIRECTION_OUTPUT)
126            pa_core_update_default_sink(p->core);
127        else
128            pa_core_update_default_source(p->core);
129
130        if (p->direction == PA_DIRECTION_OUTPUT) {
131            if (sink && p == sink->active_port) {
132                if (sink->active_port->available == PA_AVAILABLE_NO) {
133                    if (p->core->rescue_streams)
134                        pa_sink_move_streams_to_default_sink(p->core, sink, false);
135                } else
136                    pa_core_move_streams_to_newly_available_preferred_sink(p->core, sink);
137            }
138        } else {
139            if (source && p == source->active_port) {
140                if (source->active_port->available == PA_AVAILABLE_NO) {
141                    if (p->core->rescue_streams)
142                        pa_source_move_streams_to_default_source(p->core, source, false);
143                } else
144                    pa_core_move_streams_to_newly_available_preferred_source(p->core, source);
145            }
146        }
147
148        /* This may cause the sink and source pointers to become invalid, if
149         * the availability change causes the card profile to get switched. If
150         * you add code after this line, remember to take that into account. */
151        pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p);
152    }
153}
154
155static void device_port_free(pa_object *o) {
156    pa_device_port *p = PA_DEVICE_PORT(o);
157
158    pa_assert(p);
159    pa_assert(pa_device_port_refcnt(p) == 0);
160
161    if (p->impl_free)
162        p->impl_free(p);
163
164    if (p->proplist)
165        pa_proplist_free(p->proplist);
166
167    if (p->profiles)
168        pa_hashmap_free(p->profiles);
169
170    pa_xfree(p->availability_group);
171    pa_xfree(p->preferred_profile);
172    pa_xfree(p->name);
173    pa_xfree(p->description);
174    pa_xfree(p);
175}
176
177pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra) {
178    pa_device_port *p;
179
180    pa_assert(data);
181    pa_assert(data->name);
182    pa_assert(data->description);
183    pa_assert(data->direction == PA_DIRECTION_OUTPUT || data->direction == PA_DIRECTION_INPUT);
184
185    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));
186    p->parent.free = device_port_free;
187
188    p->name = data->name;
189    data->name = NULL;
190    p->description = data->description;
191    data->description = NULL;
192    p->core = c;
193    p->card = NULL;
194    p->priority = 0;
195    p->available = data->available;
196    p->availability_group = data->availability_group;
197    data->availability_group = NULL;
198    p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
199    p->direction = data->direction;
200    p->type = data->type;
201
202    p->latency_offset = 0;
203    p->proplist = pa_proplist_new();
204
205    return p;
206}
207
208void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) {
209    uint32_t state;
210    pa_core *core;
211
212    pa_assert(p);
213
214    if (offset == p->latency_offset)
215        return;
216
217    p->latency_offset = offset;
218
219    switch (p->direction) {
220        case PA_DIRECTION_OUTPUT: {
221            pa_sink *sink;
222
223            PA_IDXSET_FOREACH(sink, p->core->sinks, state) {
224                if (sink->active_port == p) {
225                    pa_sink_set_port_latency_offset(sink, p->latency_offset);
226                    break;
227                }
228            }
229
230            break;
231        }
232
233        case PA_DIRECTION_INPUT: {
234            pa_source *source;
235
236            PA_IDXSET_FOREACH(source, p->core->sources, state) {
237                if (source->active_port == p) {
238                    pa_source_set_port_latency_offset(source, p->latency_offset);
239                    break;
240                }
241            }
242
243            break;
244        }
245    }
246
247    pa_assert_se(core = p->core);
248    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
249    pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], p);
250}
251
252pa_device_port *pa_device_port_find_best(pa_hashmap *ports)
253{
254    void *state;
255    pa_device_port *p, *best = NULL;
256
257    if (!ports)
258        return NULL;
259
260    /* First run: skip unavailable ports */
261    PA_HASHMAP_FOREACH(p, ports, state) {
262        if (p->available == PA_AVAILABLE_NO)
263            continue;
264
265        if (!best || p->priority > best->priority)
266            best = p;
267    }
268
269    /* Second run: if only unavailable ports exist, still suggest a port */
270    if (!best) {
271        PA_HASHMAP_FOREACH(p, ports, state)
272            if (!best || p->priority > best->priority)
273                best = p;
274    }
275
276    return best;
277}
278
279pa_sink *pa_device_port_get_sink(pa_device_port *p) {
280    pa_sink *rs = NULL;
281    pa_sink *sink;
282    uint32_t state;
283
284    PA_IDXSET_FOREACH(sink, p->card->sinks, state)
285        if (p == pa_hashmap_get(sink->ports, p->name)) {
286            rs = sink;
287            break;
288        }
289    return rs;
290}
291
292pa_source *pa_device_port_get_source(pa_device_port *p) {
293    pa_source *rs = NULL;
294    pa_source *source;
295    uint32_t state;
296
297    PA_IDXSET_FOREACH(source, p->card->sources, state)
298        if (p == pa_hashmap_get(source->ports, p->name)) {
299            rs = source;
300            break;
301        }
302    return rs;
303}
304