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