1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2006-2008 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 <unistd.h> 25#include <string.h> 26#include <errno.h> 27#include <sys/types.h> 28#include <stdio.h> 29#include <stdlib.h> 30 31#include <pulse/gccmacro.h> 32#include <pulse/xmalloc.h> 33#include <pulse/timeval.h> 34#include <pulse/rtclock.h> 35 36#include <pulsecore/core-error.h> 37#include <pulsecore/module.h> 38#include <pulsecore/core-util.h> 39#include <pulsecore/modargs.h> 40#include <pulsecore/log.h> 41#include <pulsecore/core-subscribe.h> 42#include <pulsecore/card.h> 43#include <pulsecore/namereg.h> 44#include <pulsecore/database.h> 45#include <pulsecore/tagstruct.h> 46 47PA_MODULE_AUTHOR("Lennart Poettering"); 48PA_MODULE_DESCRIPTION("Automatically restore profile of cards"); 49PA_MODULE_VERSION(PACKAGE_VERSION); 50PA_MODULE_LOAD_ONCE(true); 51PA_MODULE_USAGE( 52 "restore_bluetooth_profile=<boolean>" 53); 54 55#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) 56 57static const char* const valid_modargs[] = { 58 "restore_bluetooth_profile", 59 NULL 60}; 61 62struct userdata { 63 pa_core *core; 64 pa_module *module; 65 pa_time_event *save_time_event; 66 pa_database *database; 67 bool restore_bluetooth_profile; 68}; 69 70#define ENTRY_VERSION 5 71 72struct port_info { 73 char *name; 74 int64_t offset; 75 char *profile; 76}; 77 78struct entry { 79 char *profile; 80 pa_hashmap *ports; /* Port name -> struct port_info */ 81 char *preferred_input_port; 82 char *preferred_output_port; 83 bool profile_is_sticky; /* since version 5; must be restored together with profile name */ 84}; 85 86static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { 87 struct userdata *u = userdata; 88 89 pa_assert(a); 90 pa_assert(e); 91 pa_assert(u); 92 93 pa_assert(e == u->save_time_event); 94 u->core->mainloop->time_free(u->save_time_event); 95 u->save_time_event = NULL; 96 97 pa_database_sync(u->database); 98 pa_log_info("Synced."); 99} 100 101static void trigger_save(struct userdata *u) { 102 if (u->save_time_event) 103 return; 104 105 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); 106} 107 108static void port_info_free(struct port_info *p_info) { 109 pa_assert(p_info); 110 111 pa_xfree(p_info->profile); 112 pa_xfree(p_info->name); 113 pa_xfree(p_info); 114} 115 116static struct entry* entry_new(void) { 117 struct entry *r = pa_xnew0(struct entry, 1); 118 r->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) port_info_free); 119 return r; 120} 121 122static struct port_info *port_info_new(pa_device_port *port) { 123 struct port_info *p_info; 124 125 if (port) { 126 p_info = pa_xnew0(struct port_info, 1); 127 p_info->name = pa_xstrdup(port->name); 128 p_info->offset = port->latency_offset; 129 if (port->preferred_profile) 130 p_info->profile = pa_xstrdup(port->preferred_profile); 131 } else 132 p_info = pa_xnew0(struct port_info, 1); 133 134 return p_info; 135} 136 137static void entry_free(struct entry* e) { 138 pa_assert(e); 139 140 pa_xfree(e->preferred_output_port); 141 pa_xfree(e->preferred_input_port); 142 pa_xfree(e->profile); 143 pa_hashmap_free(e->ports); 144 145 pa_xfree(e); 146} 147 148static struct entry *entry_from_card(pa_card *card) { 149 struct port_info *p_info; 150 struct entry *entry; 151 pa_device_port *port; 152 void *state; 153 154 pa_assert(card); 155 156 entry = entry_new(); 157 entry->profile_is_sticky = card->profile_is_sticky; 158 if (card->save_profile || entry->profile_is_sticky) 159 entry->profile = pa_xstrdup(card->active_profile->name); 160 161 if (card->preferred_input_port) 162 entry->preferred_input_port = pa_xstrdup(card->preferred_input_port->name); 163 if (card->preferred_output_port) 164 entry->preferred_output_port = pa_xstrdup(card->preferred_output_port->name); 165 166 PA_HASHMAP_FOREACH(port, card->ports, state) { 167 p_info = port_info_new(port); 168 pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0); 169 } 170 171 return entry; 172} 173 174static bool entrys_equal(struct entry *a, struct entry *b) { 175 struct port_info *Ap_info, *Bp_info; 176 void *state; 177 178 pa_assert(a); 179 pa_assert(b); 180 181 if (!pa_streq(a->profile, b->profile) || 182 pa_hashmap_size(a->ports) != pa_hashmap_size(b->ports)) 183 return false; 184 185 PA_HASHMAP_FOREACH(Ap_info, a->ports, state) { 186 if ((Bp_info = pa_hashmap_get(b->ports, Ap_info->name))) { 187 if (Ap_info->offset != Bp_info->offset) 188 return false; 189 } else 190 return false; 191 } 192 193 if (!pa_safe_streq(a->preferred_input_port, b->preferred_input_port)) 194 return false; 195 196 if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port)) 197 return false; 198 199 if (a->profile_is_sticky != b->profile_is_sticky) 200 return false; 201 202 return true; 203} 204 205static bool entry_write(struct userdata *u, const char *name, const struct entry *e) { 206 pa_tagstruct *t; 207 pa_datum key, data; 208 bool r; 209 void *state; 210 struct port_info *p_info; 211 212 pa_assert(u); 213 pa_assert(name); 214 pa_assert(e); 215 216 t = pa_tagstruct_new(); 217 pa_tagstruct_putu8(t, ENTRY_VERSION); 218 pa_tagstruct_puts(t, e->profile); 219 pa_tagstruct_putu32(t, pa_hashmap_size(e->ports)); 220 221 PA_HASHMAP_FOREACH(p_info, e->ports, state) { 222 pa_tagstruct_puts(t, p_info->name); 223 pa_tagstruct_puts64(t, p_info->offset); 224 pa_tagstruct_puts(t, p_info->profile); 225 } 226 227 pa_tagstruct_puts(t, e->preferred_input_port); 228 pa_tagstruct_puts(t, e->preferred_output_port); 229 230 pa_tagstruct_put_boolean(t, e->profile_is_sticky); 231 232 key.data = (char *) name; 233 key.size = strlen(name); 234 235 data.data = (void*)pa_tagstruct_data(t, &data.size); 236 237 r = (pa_database_set(u->database, &key, &data, true) == 0); 238 239 pa_tagstruct_free(t); 240 241 return r; 242} 243 244#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 245 246#define LEGACY_ENTRY_VERSION 1 247static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) { 248 struct legacy_entry { 249 uint8_t version; 250 char profile[PA_NAME_MAX]; 251 } PA_GCC_PACKED ; 252 struct legacy_entry *le; 253 struct entry *e; 254 255 pa_assert(u); 256 pa_assert(data); 257 258 if (data->size != sizeof(struct legacy_entry)) { 259 pa_log_debug("Size does not match."); 260 return NULL; 261 } 262 263 le = (struct legacy_entry*)data->data; 264 265 if (le->version != LEGACY_ENTRY_VERSION) { 266 pa_log_debug("Version mismatch."); 267 return NULL; 268 } 269 270 if (!memchr(le->profile, 0, sizeof(le->profile))) { 271 pa_log_warn("Profile has missing NUL byte."); 272 return NULL; 273 } 274 275 e = entry_new(); 276 e->profile = pa_xstrdup(le->profile); 277 return e; 278} 279#endif 280 281static struct entry* entry_read(struct userdata *u, const char *name) { 282 pa_datum key, data; 283 struct entry *e = NULL; 284 pa_tagstruct *t = NULL; 285 const char* profile; 286 uint8_t version; 287 288 pa_assert(u); 289 pa_assert(name); 290 291 key.data = (char*) name; 292 key.size = strlen(name); 293 294 pa_zero(data); 295 296 if (!pa_database_get(u->database, &key, &data)) { 297 pa_log_debug("Database contains no data for key: %s", name); 298 return NULL; 299 } 300 301 t = pa_tagstruct_new_fixed(data.data, data.size); 302 e = entry_new(); 303 304 if (pa_tagstruct_getu8(t, &version) < 0 || 305 version > ENTRY_VERSION || 306 pa_tagstruct_gets(t, &profile) < 0) { 307 308 goto fail; 309 } 310 311 if (!profile) 312 profile = ""; 313 314 e->profile = pa_xstrdup(profile); 315 316 if (version >= 2) { 317 uint32_t port_count = 0; 318 const char *port_name = NULL, *profile_name = NULL; 319 int64_t port_offset = 0; 320 struct port_info *p_info; 321 unsigned i; 322 323 if (pa_tagstruct_getu32(t, &port_count) < 0) 324 goto fail; 325 326 for (i = 0; i < port_count; i++) { 327 if (pa_tagstruct_gets(t, &port_name) < 0 || 328 !port_name || 329 pa_hashmap_get(e->ports, port_name) || 330 pa_tagstruct_gets64(t, &port_offset) < 0) 331 goto fail; 332 if (version >= 3 && pa_tagstruct_gets(t, &profile_name) < 0) 333 goto fail; 334 335 p_info = port_info_new(NULL); 336 p_info->name = pa_xstrdup(port_name); 337 p_info->offset = port_offset; 338 if (profile_name) 339 p_info->profile = pa_xstrdup(profile_name); 340 341 pa_assert_se(pa_hashmap_put(e->ports, p_info->name, p_info) >= 0); 342 } 343 } 344 345 if (version >= 4) { 346 const char *preferred_input_port; 347 const char *preferred_output_port; 348 349 if (pa_tagstruct_gets(t, &preferred_input_port) < 0 350 || pa_tagstruct_gets(t, &preferred_output_port) < 0) 351 goto fail; 352 353 e->preferred_input_port = pa_xstrdup(preferred_input_port); 354 e->preferred_output_port = pa_xstrdup(preferred_output_port); 355 } 356 357 if (version >= 5) { 358 bool profile_is_sticky; 359 if (pa_tagstruct_get_boolean(t, &profile_is_sticky) < 0) 360 goto fail; 361 362 e->profile_is_sticky = profile_is_sticky; 363 } 364 365 if (!pa_tagstruct_eof(t)) 366 goto fail; 367 368 pa_tagstruct_free(t); 369 pa_datum_free(&data); 370 371 return e; 372 373fail: 374 375 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name); 376 377 if (e) 378 entry_free(e); 379 if (t) 380 pa_tagstruct_free(t); 381 382#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 383 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name); 384 if ((e = legacy_entry_read(u, &data))) { 385 pa_log_debug("Success. Saving new format for key: %s", name); 386 if (entry_write(u, name, e)) 387 trigger_save(u); 388 pa_datum_free(&data); 389 return e; 390 } else 391 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name); 392#endif 393 394 pa_datum_free(&data); 395 return NULL; 396} 397 398static void show_full_info(pa_card *card) { 399 pa_assert(card); 400 401 if (card->save_profile) 402 pa_log_info("Storing profile and port latency offsets for card %s.", card->name); 403 else 404 pa_log_info("Storing port latency offsets for card %s.", card->name); 405} 406 407static pa_hook_result_t card_put_hook_callback(pa_core *c, pa_card *card, struct userdata *u) { 408 struct entry *entry, *old; 409 410 pa_assert(card); 411 412 entry = entry_from_card(card); 413 414 if ((old = entry_read(u, card->name))) { 415 if (!card->save_profile) 416 entry->profile = pa_xstrdup(old->profile); 417 if (entrys_equal(entry, old)) 418 goto finish; 419 } 420 421 show_full_info(card); 422 423 if (entry_write(u, card->name, entry)) 424 trigger_save(u); 425 426finish: 427 entry_free(entry); 428 if (old) 429 entry_free(old); 430 431 return PA_HOOK_OK; 432} 433 434static void update_profile_for_port(struct entry *entry, pa_card *card, pa_device_port *p) { 435 struct port_info *p_info; 436 437 if (p == NULL) 438 return; 439 440 if (!(p_info = pa_hashmap_get(entry->ports, p->name))) { 441 p_info = port_info_new(p); 442 pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0); 443 } 444 445 if (!pa_safe_streq(p_info->profile, p->preferred_profile)) { 446 pa_xfree(p_info->profile); 447 p_info->profile = pa_xstrdup(p->preferred_profile); 448 pa_log_info("Storing profile %s for port %s on card %s.", p_info->profile, p->name, card->name); 449 } 450} 451 452static pa_hook_result_t card_profile_changed_callback(pa_core *c, pa_card *card, struct userdata *u) { 453 struct entry *entry; 454 pa_sink *sink; 455 pa_source *source; 456 uint32_t state; 457 458 pa_assert(card); 459 460 if (!card->save_profile && !card->profile_is_sticky) 461 return PA_HOOK_OK; 462 463 if ((entry = entry_read(u, card->name))) { 464 pa_xfree(entry->profile); 465 entry->profile_is_sticky = card->profile_is_sticky; 466 entry->profile = pa_xstrdup(card->active_profile->name); 467 pa_log_info("Storing card profile for card %s.", card->name); 468 } else { 469 entry = entry_from_card(card); 470 show_full_info(card); 471 } 472 473 PA_IDXSET_FOREACH(sink, card->sinks, state) 474 update_profile_for_port(entry, card, sink->active_port); 475 PA_IDXSET_FOREACH(source, card->sources, state) 476 update_profile_for_port(entry, card, source->active_port); 477 478 if (entry_write(u, card->name, entry)) 479 trigger_save(u); 480 481 entry_free(entry); 482 return PA_HOOK_OK; 483} 484 485static pa_hook_result_t card_profile_added_callback(pa_core *c, pa_card_profile *profile, struct userdata *u) { 486 struct entry *entry; 487 488 pa_assert(profile); 489 490 if (profile->available == PA_AVAILABLE_NO) 491 return PA_HOOK_OK; 492 493 if (!(entry = entry_read(u, profile->card->name))) 494 return PA_HOOK_OK; 495 496 if (pa_safe_streq(entry->profile, profile->name)) { 497 if (pa_card_set_profile(profile->card, profile, true) >= 0) 498 pa_log_info("Restored profile '%s' for card %s.", profile->name, profile->card->name); 499 } 500 501 entry_free(entry); 502 503 return PA_HOOK_OK; 504} 505 506static pa_hook_result_t port_offset_change_callback(pa_core *c, pa_device_port *port, struct userdata *u) { 507 struct entry *entry; 508 pa_card *card; 509 510 pa_assert(port); 511 card = port->card; 512 513 if ((entry = entry_read(u, card->name))) { 514 struct port_info *p_info; 515 516 if ((p_info = pa_hashmap_get(entry->ports, port->name))) 517 p_info->offset = port->latency_offset; 518 else { 519 p_info = port_info_new(port); 520 pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0); 521 } 522 523 pa_log_info("Storing latency offset for port %s on card %s.", port->name, card->name); 524 525 } else { 526 entry = entry_from_card(card); 527 show_full_info(card); 528 } 529 530 if (entry_write(u, card->name, entry)) 531 trigger_save(u); 532 533 entry_free(entry); 534 return PA_HOOK_OK; 535} 536 537static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) { 538 struct entry *e; 539 void *state; 540 pa_device_port *p; 541 struct port_info *p_info; 542 543 pa_assert(new_data); 544 545 if (!(e = entry_read(u, new_data->name))) 546 return PA_HOOK_OK; 547 548 /* Always restore the latency offsets because their 549 * initial value is always 0 */ 550 551 pa_log_info("Restoring port latency offsets for card %s.", new_data->name); 552 553 PA_HASHMAP_FOREACH(p_info, e->ports, state) 554 if ((p = pa_hashmap_get(new_data->ports, p_info->name))) { 555 p->latency_offset = p_info->offset; 556 if (!p->preferred_profile && p_info->profile) 557 pa_device_port_set_preferred_profile(p, p_info->profile); 558 } 559 560 if (e->preferred_input_port) { 561 p = pa_hashmap_get(new_data->ports, e->preferred_input_port); 562 if (p) 563 pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_INPUT, p); 564 } 565 566 if (e->preferred_output_port) { 567 p = pa_hashmap_get(new_data->ports, e->preferred_output_port); 568 if (p) 569 pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_OUTPUT, p); 570 } 571 572 entry_free(e); 573 574 return PA_HOOK_OK; 575} 576 577static pa_hook_result_t card_choose_initial_profile_callback(pa_core *core, pa_card *card, struct userdata *u) { 578 struct entry *e; 579 580 if (!(e = entry_read(u, card->name))) 581 return PA_HOOK_OK; 582 583 if (!u->restore_bluetooth_profile) { 584 const char *s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS); 585 if (pa_safe_streq(s, "bluetooth")) 586 goto finish; 587 } 588 589 card->profile_is_sticky = e->profile_is_sticky; 590 pa_log_info("Profile '%s' was previously %s for card %s.", 591 e->profile, 592 card->profile_is_sticky ? "sticky" : "automatically selected", 593 card->name); 594 595 if (e->profile[0]) { 596 pa_card_profile *profile; 597 598 profile = pa_hashmap_get(card->profiles, e->profile); 599 if (profile) { 600 if (profile->available != PA_AVAILABLE_NO || card->profile_is_sticky) { 601 pa_log_info("Restoring profile '%s' for card %s.", profile->name, card->name); 602 pa_card_set_profile(card, profile, true); 603 } else 604 pa_log_debug("Not restoring profile %s for card %s, because the profile is currently unavailable.", 605 profile->name, card->name); 606 } else { 607 pa_log_debug("Tried to restore profile %s for card %s, but the card doesn't have such profile.", 608 e->profile, card->name); 609 } 610 } 611 612finish: 613 entry_free(e); 614 615 return PA_HOOK_OK; 616} 617 618static pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_card_preferred_port_changed_hook_data *data, 619 struct userdata *u) { 620 struct entry *e; 621 pa_card *card; 622 623 card = data->card; 624 625 e = entry_read(u, card->name); 626 if (!e) 627 e = entry_from_card(card); 628 629 if (data->direction == PA_DIRECTION_INPUT) { 630 pa_xfree(e->preferred_input_port); 631 e->preferred_input_port = pa_xstrdup(card->preferred_input_port ? card->preferred_input_port->name : NULL); 632 } else { 633 pa_xfree(e->preferred_output_port); 634 e->preferred_output_port = pa_xstrdup(card->preferred_output_port ? card->preferred_output_port->name : NULL); 635 } 636 637 if (entry_write(u, card->name, e)) 638 trigger_save(u); 639 640 entry_free(e); 641 642 return PA_HOOK_OK; 643} 644 645int pa__init(pa_module*m) { 646 pa_modargs *ma = NULL; 647 struct userdata *u; 648 char *state_path; 649 bool restore_bluetooth_profile; 650 651 pa_assert(m); 652 653 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 654 pa_log("Failed to parse module arguments"); 655 goto fail; 656 } 657 658 restore_bluetooth_profile = false; 659 if (pa_modargs_get_value_boolean(ma, "restore_bluetooth_profile", &restore_bluetooth_profile) < 0) { 660 pa_log("Invalid boolean value for restore_bluetooth_profile parameter"); 661 goto fail; 662 } 663 664 m->userdata = u = pa_xnew0(struct userdata, 1); 665 u->core = m->core; 666 u->module = m; 667 u->restore_bluetooth_profile = restore_bluetooth_profile; 668 669 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u); 670 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL, 671 (pa_hook_cb_t) card_choose_initial_profile_callback, u); 672 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u); 673 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_preferred_port_changed_callback, u); 674 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u); 675 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_added_callback, u); 676 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) port_offset_change_callback, u); 677 678 if (!(state_path = pa_state_path(NULL, true))) 679 goto fail; 680 681 if (!(u->database = pa_database_open(state_path, "card-database", true, true))) { 682 pa_xfree(state_path); 683 goto fail; 684 } 685 686 pa_xfree(state_path); 687 688 pa_modargs_free(ma); 689 return 0; 690 691fail: 692 pa__done(m); 693 694 if (ma) 695 pa_modargs_free(ma); 696 697 return -1; 698} 699 700void pa__done(pa_module*m) { 701 struct userdata* u; 702 703 pa_assert(m); 704 705 if (!(u = m->userdata)) 706 return; 707 708 if (u->save_time_event) { 709 u->core->mainloop->time_free(u->save_time_event); 710 pa_database_sync(u->database); 711 } 712 713 if (u->database) 714 pa_database_close(u->database); 715 716 pa_xfree(u); 717} 718