Add default-monitor-time-sec
[platform/upstream/pulseaudio.git] / src / pulsecore / device-port.c
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
26 PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object);
27
28 pa_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
37 void 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
44 void 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
51 void 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
57 void 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
64 void 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
70 void 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
76 void 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
84 void 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
93 void 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
155 static 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
177 pa_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
208 void 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
252 pa_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
279 pa_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
292 pa_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 }