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
pa_device_port_new_data_init(pa_device_port_new_data * data)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
pa_device_port_new_data_set_name(pa_device_port_new_data * data,const char * name)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
pa_device_port_new_data_set_description(pa_device_port_new_data * data,const char * description)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
pa_device_port_new_data_set_available(pa_device_port_new_data * data,pa_available_t available)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
pa_device_port_new_data_set_availability_group(pa_device_port_new_data * data,const char * group)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
pa_device_port_new_data_set_direction(pa_device_port_new_data * data,pa_direction_t direction)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
pa_device_port_new_data_set_type(pa_device_port_new_data * data,pa_device_port_type_t type)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
pa_device_port_new_data_done(pa_device_port_new_data * data)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
pa_device_port_set_preferred_profile(pa_device_port * p,const char * new_pp)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
pa_device_port_set_available(pa_device_port * p,pa_available_t status)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
device_port_free(pa_object * o)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
pa_device_port_new(pa_core * c,pa_device_port_new_data * data,size_t extra)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
pa_device_port_set_latency_offset(pa_device_port * p,int64_t offset)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
pa_device_port_find_best(pa_hashmap * ports)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
pa_device_port_get_sink(pa_device_port * p)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
pa_device_port_get_source(pa_device_port * p)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 }
304