• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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