1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27 28#include <pulse/xmalloc.h> 29#include <pulse/util.h> 30 31#include <pulsecore/json.h> 32#include <pulsecore/log.h> 33#include <pulsecore/macro.h> 34#include <pulsecore/core-util.h> 35#include <pulsecore/namereg.h> 36#include <pulsecore/message-handler.h> 37#include <pulsecore/device-port.h> 38 39#include "card.h" 40 41static int card_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata); 42 43static char* make_message_handler_path(const char *name) { 44 return pa_sprintf_malloc("/card/%s", name); 45} 46 47const char *pa_available_to_string(pa_available_t available) { 48 switch (available) { 49 case PA_AVAILABLE_UNKNOWN: 50 return "unknown"; 51 case PA_AVAILABLE_NO: 52 return "no"; 53 case PA_AVAILABLE_YES: 54 return "yes"; 55 default: 56 pa_assert_not_reached(); 57 } 58} 59 60pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra) { 61 pa_card_profile *c; 62 63 pa_assert(name); 64 65 c = pa_xmalloc0(PA_ALIGN(sizeof(pa_card_profile)) + extra); 66 c->name = pa_xstrdup(name); 67 c->description = pa_xstrdup(description); 68 c->available = PA_AVAILABLE_UNKNOWN; 69 70 return c; 71} 72 73void pa_card_profile_free(pa_card_profile *c) { 74 pa_assert(c); 75 76 pa_xfree(c->input_name); 77 pa_xfree(c->output_name); 78 pa_xfree(c->name); 79 pa_xfree(c->description); 80 pa_xfree(c); 81} 82 83void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available) { 84 pa_core *core; 85 86 pa_assert(c); 87 pa_assert(c->card); /* Modify member variable directly during creation instead of using this function */ 88 89 if (c->available == available) 90 return; 91 92 c->available = available; 93 pa_log_debug("Setting card %s profile %s to availability status %s", c->card->name, c->name, 94 pa_available_to_string(available)); 95 96 /* Post subscriptions to the card which owns us */ 97 pa_assert_se(core = c->card->core); 98 pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->card->index); 99 100 if (c->card->linked) 101 pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], c); 102} 103 104pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) { 105 pa_assert(data); 106 107 memset(data, 0, sizeof(*data)); 108 data->proplist = pa_proplist_new(); 109 data->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_card_profile_free); 110 data->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_device_port_unref); 111 return data; 112} 113 114void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) { 115 pa_assert(data); 116 117 pa_xfree(data->name); 118 data->name = pa_xstrdup(name); 119} 120 121void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port) { 122 pa_assert(data); 123 124 if (direction == PA_DIRECTION_INPUT) 125 data->preferred_input_port = port; 126 else 127 data->preferred_output_port = port; 128} 129 130void pa_card_new_data_done(pa_card_new_data *data) { 131 132 pa_assert(data); 133 134 pa_proplist_free(data->proplist); 135 136 if (data->profiles) 137 pa_hashmap_free(data->profiles); 138 139 if (data->ports) 140 pa_hashmap_free(data->ports); 141 142 pa_xfree(data->name); 143} 144 145pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { 146 pa_card *c; 147 const char *name, *tmp; 148 char *object_path, *description; 149 void *state; 150 pa_card_profile *profile; 151 pa_device_port *port; 152 153 pa_core_assert_ref(core); 154 pa_assert(data); 155 pa_assert(data->name); 156 pa_assert(data->profiles); 157 pa_assert(!pa_hashmap_isempty(data->profiles)); 158 159 c = pa_xnew0(pa_card, 1); 160 161 if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) { 162 pa_xfree(c); 163 return NULL; 164 } 165 166 pa_card_new_data_set_name(data, name); 167 pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data); 168 169 c->core = core; 170 c->name = pa_xstrdup(data->name); 171 c->proplist = pa_proplist_copy(data->proplist); 172 c->driver = pa_xstrdup(pa_path_get_filename(data->driver)); 173 c->module = data->module; 174 175 c->sinks = pa_idxset_new(NULL, NULL); 176 c->sources = pa_idxset_new(NULL, NULL); 177 178 /* As a minor optimization we just steal the list instead of 179 * copying it here */ 180 pa_assert_se(c->profiles = data->profiles); 181 data->profiles = NULL; 182 pa_assert_se(c->ports = data->ports); 183 data->ports = NULL; 184 185 PA_HASHMAP_FOREACH(profile, c->profiles, state) 186 profile->card = c; 187 188 PA_HASHMAP_FOREACH(port, c->ports, state) 189 port->card = c; 190 191 c->preferred_input_port = data->preferred_input_port; 192 c->preferred_output_port = data->preferred_output_port; 193 194 pa_device_init_description(c->proplist, c); 195 pa_device_init_icon(c->proplist, true); 196 pa_device_init_intended_roles(c->proplist); 197 198 object_path = make_message_handler_path(c->name); 199 if (!(tmp = pa_proplist_gets(c->proplist, PA_PROP_DEVICE_DESCRIPTION))) 200 tmp = c->name; 201 description = pa_sprintf_malloc("Message handler for card \"%s\"", tmp); 202 pa_message_handler_register(c->core, object_path, description, card_message_handler, (void *) c); 203 pa_xfree(object_path); 204 pa_xfree(description); 205 206 return c; 207} 208 209void pa_card_choose_initial_profile(pa_card *card) { 210 pa_card_profile *profile; 211 void *state; 212 pa_card_profile *best = NULL; 213 214 pa_assert(card); 215 216 /* By default, pick the highest priority profile that is not unavailable, 217 * or if all profiles are unavailable, pick the profile with the highest 218 * priority regardless of its availability. */ 219 220 pa_log_debug("Looking for initial profile for card %s", card->name); 221 PA_HASHMAP_FOREACH(profile, card->profiles, state) { 222 pa_log_debug("%s availability %s", profile->name, pa_available_to_string(profile->available)); 223 if (profile->available == PA_AVAILABLE_NO) 224 continue; 225 226 if (!best || profile->priority > best->priority) 227 best = profile; 228 } 229 230 if (!best) { 231 PA_HASHMAP_FOREACH(profile, card->profiles, state) { 232 if (!best || profile->priority > best->priority) 233 best = profile; 234 } 235 } 236 pa_assert(best); 237 238 card->active_profile = best; 239 card->save_profile = false; 240 card->profile_is_sticky = false; 241 pa_log_info("%s: active_profile: %s", card->name, card->active_profile->name); 242 243 /* Let policy modules override the default. */ 244 pa_hook_fire(&card->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], card); 245} 246 247void pa_card_put(pa_card *card) { 248 pa_assert(card); 249 250 pa_assert_se(pa_idxset_put(card->core->cards, card, &card->index) >= 0); 251 card->linked = true; 252 253 pa_log_info("Created %u \"%s\"", card->index, card->name); 254 pa_hook_fire(&card->core->hooks[PA_CORE_HOOK_CARD_PUT], card); 255 pa_subscription_post(card->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, card->index); 256} 257 258void pa_card_free(pa_card *c) { 259 pa_core *core; 260 char *object_path; 261 262 pa_assert(c); 263 pa_assert(c->core); 264 265 core = c->core; 266 267 if (c->linked) { 268 pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_UNLINK], c); 269 270 pa_idxset_remove_by_data(c->core->cards, c, NULL); 271 pa_log_info("Freed %u \"%s\"", c->index, c->name); 272 pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); 273 } 274 275 object_path = make_message_handler_path(c->name); 276 pa_message_handler_unregister(core, object_path); 277 pa_xfree(object_path); 278 279 pa_namereg_unregister(core, c->name); 280 281 pa_assert(pa_idxset_isempty(c->sinks)); 282 pa_idxset_free(c->sinks, NULL); 283 pa_assert(pa_idxset_isempty(c->sources)); 284 pa_idxset_free(c->sources, NULL); 285 286 pa_hashmap_free(c->ports); 287 288 if (c->profiles) 289 pa_hashmap_free(c->profiles); 290 291 pa_proplist_free(c->proplist); 292 pa_xfree(c->driver); 293 pa_xfree(c->name); 294 pa_xfree(c); 295} 296 297void pa_card_add_profile(pa_card *c, pa_card_profile *profile) { 298 pa_assert(c); 299 pa_assert(profile); 300 301 /* take ownership of the profile */ 302 pa_assert_se(pa_hashmap_put(c->profiles, profile->name, profile) >= 0); 303 profile->card = c; 304 305 pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); 306 307 pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], profile); 308} 309 310static const char* profile_name_for_dir(pa_card_profile *cp, pa_direction_t dir) { 311 if (dir == PA_DIRECTION_OUTPUT && cp->output_name) 312 return cp->output_name; 313 if (dir == PA_DIRECTION_INPUT && cp->input_name) 314 return cp->input_name; 315 return cp->name; 316} 317 318static void update_port_preferred_profile(pa_card *c) { 319 pa_sink *sink; 320 pa_source *source; 321 uint32_t state; 322 323 PA_IDXSET_FOREACH(sink, c->sinks, state) 324 if (sink->active_port) 325 pa_device_port_set_preferred_profile(sink->active_port, profile_name_for_dir(c->active_profile, PA_DIRECTION_OUTPUT)); 326 PA_IDXSET_FOREACH(source, c->sources, state) 327 if (source->active_port) 328 pa_device_port_set_preferred_profile(source->active_port, profile_name_for_dir(c->active_profile, PA_DIRECTION_INPUT)); 329} 330 331static int card_set_profile_is_sticky(pa_card *c, bool profile_is_sticky) { 332 pa_assert(c); 333 334 if (c->profile_is_sticky == profile_is_sticky) 335 return 0; 336 337 pa_log_debug("%s: profile_is_sticky: %s -> %s", 338 c->name, pa_yes_no(c->profile_is_sticky), pa_yes_no(profile_is_sticky)); 339 340 c->profile_is_sticky = profile_is_sticky; 341 342 if (c->linked) { 343 pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c); 344 pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); 345 } 346 347 return 0; 348} 349 350int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) { 351 int r; 352 353 pa_assert(c); 354 pa_assert(profile); 355 pa_assert(profile->card == c); 356 357 if (!c->set_profile) { 358 pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name); 359 return -PA_ERR_NOTIMPLEMENTED; 360 } 361 362 if (c->active_profile == profile) { 363 if (save && !c->save_profile) { 364 update_port_preferred_profile(c); 365 c->save_profile = true; 366 } 367 return 0; 368 } 369 370 /* If we're setting the initial profile, we shouldn't call set_profile(), 371 * because the implementations don't expect that (for historical reasons). 372 * We should just set c->active_profile, and the implementations will 373 * properly set up that profile after pa_card_put() has returned. It would 374 * be probably good to change this so that also the initial profile can be 375 * set up in set_profile(), but if set_profile() fails, that would need 376 * some better handling than what we do here currently. */ 377 if (c->linked && (r = c->set_profile(c, profile)) < 0) 378 return r; 379 380 pa_log_debug("%s: active_profile: %s -> %s", c->name, c->active_profile->name, profile->name); 381 c->active_profile = profile; 382 c->save_profile = save; 383 384 if (save) 385 update_port_preferred_profile(c); 386 387 if (c->linked) { 388 pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c); 389 pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); 390 } 391 392 return 0; 393} 394 395void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port) { 396 pa_device_port *old_port; 397 const char *old_port_str; 398 const char *new_port_str; 399 pa_card_preferred_port_changed_hook_data data; 400 401 pa_assert(c); 402 403 if (direction == PA_DIRECTION_INPUT) { 404 old_port = c->preferred_input_port; 405 old_port_str = c->preferred_input_port ? c->preferred_input_port->name : "(unset)"; 406 } else { 407 old_port = c->preferred_output_port; 408 old_port_str = c->preferred_output_port ? c->preferred_output_port->name : "(unset)"; 409 } 410 411 if (port == old_port) 412 return; 413 414 new_port_str = port ? port->name : "(unset)"; 415 416 if (direction == PA_DIRECTION_INPUT) { 417 c->preferred_input_port = port; 418 pa_log_debug("%s: preferred_input_port: %s -> %s", c->name, old_port_str, new_port_str); 419 } else { 420 c->preferred_output_port = port; 421 pa_log_debug("%s: preferred_output_port: %s -> %s", c->name, old_port_str, new_port_str); 422 } 423 424 data.card = c; 425 data.direction = direction; 426 pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], &data); 427} 428 429int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) { 430 pa_sink *sink; 431 pa_source *source; 432 pa_suspend_cause_t suspend_cause; 433 uint32_t idx; 434 int ret = 0; 435 436 pa_assert(c); 437 pa_assert(cause != 0); 438 439 suspend_cause = c->suspend_cause; 440 441 if (suspend) 442 suspend_cause |= cause; 443 else 444 suspend_cause &= ~cause; 445 446 if (c->suspend_cause != suspend_cause) { 447 pa_log_debug("Card suspend causes/state changed"); 448 c->suspend_cause = suspend_cause; 449 pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_SUSPEND_CHANGED], c); 450 } 451 452 PA_IDXSET_FOREACH(sink, c->sinks, idx) { 453 int r; 454 455 if ((r = pa_sink_suspend(sink, suspend, cause)) < 0) 456 ret = r; 457 } 458 459 PA_IDXSET_FOREACH(source, c->sources, idx) { 460 int r; 461 462 if ((r = pa_source_suspend(source, suspend, cause)) < 0) 463 ret = r; 464 } 465 466 return ret; 467} 468 469static int card_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) { 470 pa_card *c = userdata; 471 char *message_handler_path; 472 473 pa_assert(c); 474 pa_assert(message); 475 pa_assert(response); 476 477 message_handler_path = make_message_handler_path(c->name); 478 479 if (!object_path || !pa_streq(object_path, message_handler_path)) { 480 pa_xfree(message_handler_path); 481 return -PA_ERR_NOENTITY; 482 } 483 484 pa_xfree(message_handler_path); 485 486 if (pa_streq(message, "get-profile-sticky")) { 487 pa_json_encoder *encoder; 488 encoder = pa_json_encoder_new(); 489 490 pa_json_encoder_add_element_bool(encoder, c->profile_is_sticky); 491 492 *response = pa_json_encoder_to_string_free(encoder); 493 494 return PA_OK; 495 } else if (pa_streq(message, "set-profile-sticky")) { 496 497 if (!parameters || pa_json_object_get_type(parameters) != PA_JSON_TYPE_BOOL) { 498 pa_log_info("Card operation set-profile-sticky requires argument: \"true\" or \"false\""); 499 return -PA_ERR_INVALID; 500 } 501 502 card_set_profile_is_sticky(c, pa_json_object_get_bool(parameters)); 503 504 return PA_OK; 505 } 506 507 return -PA_ERR_NOTIMPLEMENTED; 508} 509