2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 Copyright 2011 David Henningsson, Canonical Ltd.
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.
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.
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/>.
22 #include "device-port.h"
23 #include <pulsecore/card.h>
24 #include <pulsecore/core-util.h>
26 PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object);
28 pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data) {
32 data->type = PA_DEVICE_PORT_TYPE_UNKNOWN;
33 data->available = PA_AVAILABLE_UNKNOWN;
37 void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name) {
41 data->name = pa_xstrdup(name);
44 void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description) {
47 pa_xfree(data->description);
48 data->description = pa_xstrdup(description);
51 void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available) {
54 data->available = available;
57 void pa_device_port_new_data_set_availability_group(pa_device_port_new_data *data, const char *group) {
60 pa_xfree(data->availability_group);
61 data->availability_group = pa_xstrdup(group);
64 void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction) {
67 data->direction = direction;
70 void pa_device_port_new_data_set_type(pa_device_port_new_data *data, pa_device_port_type_t type) {
76 void pa_device_port_new_data_done(pa_device_port_new_data *data) {
80 pa_xfree(data->description);
81 pa_xfree(data->availability_group);
84 void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) {
87 if (!pa_safe_streq(p->preferred_profile, new_pp)) {
88 pa_xfree(p->preferred_profile);
89 p->preferred_profile = pa_xstrdup(new_pp);
93 void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
96 if (p->available == status)
99 /* pa_assert(status != PA_AVAILABLE_UNKNOWN); */
101 p->available = status;
102 pa_log_debug("Setting port %s to status %s", p->name, pa_available_to_string(status));
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) {
113 pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
115 sink = pa_device_port_get_sink(p);
116 source = pa_device_port_get_source(p);
118 pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, sink->index);
120 pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, source->index);
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);
128 pa_core_update_default_source(p->core);
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);
136 pa_core_move_streams_to_newly_available_preferred_sink(p->core, sink);
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);
144 pa_core_move_streams_to_newly_available_preferred_source(p->core, source);
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);
155 static void device_port_free(pa_object *o) {
156 pa_device_port *p = PA_DEVICE_PORT(o);
159 pa_assert(pa_device_port_refcnt(p) == 0);
165 pa_proplist_free(p->proplist);
168 pa_hashmap_free(p->profiles);
170 pa_xfree(p->availability_group);
171 pa_xfree(p->preferred_profile);
173 pa_xfree(p->description);
177 pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra) {
181 pa_assert(data->name);
182 pa_assert(data->description);
183 pa_assert(data->direction == PA_DIRECTION_OUTPUT || data->direction == PA_DIRECTION_INPUT);
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;
188 p->name = data->name;
190 p->description = data->description;
191 data->description = NULL;
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;
202 p->latency_offset = 0;
203 p->proplist = pa_proplist_new();
208 void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) {
214 if (offset == p->latency_offset)
217 p->latency_offset = offset;
219 switch (p->direction) {
220 case PA_DIRECTION_OUTPUT: {
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);
233 case PA_DIRECTION_INPUT: {
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);
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);
252 pa_device_port *pa_device_port_find_best(pa_hashmap *ports)
255 pa_device_port *p, *best = NULL;
260 /* First run: skip unavailable ports */
261 PA_HASHMAP_FOREACH(p, ports, state) {
262 if (p->available == PA_AVAILABLE_NO)
265 if (!best || p->priority > best->priority)
269 /* Second run: if only unavailable ports exist, still suggest a port */
271 PA_HASHMAP_FOREACH(p, ports, state)
272 if (!best || p->priority > best->priority)
279 pa_sink *pa_device_port_get_sink(pa_device_port *p) {
284 PA_IDXSET_FOREACH(sink, p->card->sinks, state)
285 if (p == pa_hashmap_get(sink->ports, p->name)) {
292 pa_source *pa_device_port_get_source(pa_device_port *p) {
293 pa_source *rs = NULL;
297 PA_IDXSET_FOREACH(source, p->card->sources, state)
298 if (p == pa_hashmap_get(source->ports, p->name)) {