153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006-2008 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <unistd.h>
2553a5a1b3Sopenharmony_ci#include <string.h>
2653a5a1b3Sopenharmony_ci#include <errno.h>
2753a5a1b3Sopenharmony_ci#include <sys/types.h>
2853a5a1b3Sopenharmony_ci#include <stdio.h>
2953a5a1b3Sopenharmony_ci#include <stdlib.h>
3053a5a1b3Sopenharmony_ci
3153a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h>
3253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3353a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3453a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3553a5a1b3Sopenharmony_ci
3653a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/card.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/database.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/tagstruct.h>
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
4853a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Automatically restore profile of cards");
4953a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
5053a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true);
5153a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
5253a5a1b3Sopenharmony_ci    "restore_bluetooth_profile=<boolean>"
5353a5a1b3Sopenharmony_ci);
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
5653a5a1b3Sopenharmony_ci
5753a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
5853a5a1b3Sopenharmony_ci    "restore_bluetooth_profile",
5953a5a1b3Sopenharmony_ci    NULL
6053a5a1b3Sopenharmony_ci};
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_cistruct userdata {
6353a5a1b3Sopenharmony_ci    pa_core *core;
6453a5a1b3Sopenharmony_ci    pa_module *module;
6553a5a1b3Sopenharmony_ci    pa_time_event *save_time_event;
6653a5a1b3Sopenharmony_ci    pa_database *database;
6753a5a1b3Sopenharmony_ci    bool restore_bluetooth_profile;
6853a5a1b3Sopenharmony_ci};
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci#define ENTRY_VERSION 5
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_cistruct port_info {
7353a5a1b3Sopenharmony_ci    char *name;
7453a5a1b3Sopenharmony_ci    int64_t offset;
7553a5a1b3Sopenharmony_ci    char *profile;
7653a5a1b3Sopenharmony_ci};
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_cistruct entry {
7953a5a1b3Sopenharmony_ci    char *profile;
8053a5a1b3Sopenharmony_ci    pa_hashmap *ports; /* Port name -> struct port_info */
8153a5a1b3Sopenharmony_ci    char *preferred_input_port;
8253a5a1b3Sopenharmony_ci    char *preferred_output_port;
8353a5a1b3Sopenharmony_ci    bool profile_is_sticky; /* since version 5; must be restored together with profile name */
8453a5a1b3Sopenharmony_ci};
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_cistatic void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
8753a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_ci    pa_assert(a);
9053a5a1b3Sopenharmony_ci    pa_assert(e);
9153a5a1b3Sopenharmony_ci    pa_assert(u);
9253a5a1b3Sopenharmony_ci
9353a5a1b3Sopenharmony_ci    pa_assert(e == u->save_time_event);
9453a5a1b3Sopenharmony_ci    u->core->mainloop->time_free(u->save_time_event);
9553a5a1b3Sopenharmony_ci    u->save_time_event = NULL;
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_ci    pa_database_sync(u->database);
9853a5a1b3Sopenharmony_ci    pa_log_info("Synced.");
9953a5a1b3Sopenharmony_ci}
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_cistatic void trigger_save(struct userdata *u) {
10253a5a1b3Sopenharmony_ci    if (u->save_time_event)
10353a5a1b3Sopenharmony_ci        return;
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
10653a5a1b3Sopenharmony_ci}
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_cistatic void port_info_free(struct port_info *p_info) {
10953a5a1b3Sopenharmony_ci    pa_assert(p_info);
11053a5a1b3Sopenharmony_ci
11153a5a1b3Sopenharmony_ci    pa_xfree(p_info->profile);
11253a5a1b3Sopenharmony_ci    pa_xfree(p_info->name);
11353a5a1b3Sopenharmony_ci    pa_xfree(p_info);
11453a5a1b3Sopenharmony_ci}
11553a5a1b3Sopenharmony_ci
11653a5a1b3Sopenharmony_cistatic struct entry* entry_new(void) {
11753a5a1b3Sopenharmony_ci    struct entry *r = pa_xnew0(struct entry, 1);
11853a5a1b3Sopenharmony_ci    r->ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) port_info_free);
11953a5a1b3Sopenharmony_ci    return r;
12053a5a1b3Sopenharmony_ci}
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_cistatic struct port_info *port_info_new(pa_device_port *port) {
12353a5a1b3Sopenharmony_ci    struct port_info *p_info;
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    if (port) {
12653a5a1b3Sopenharmony_ci        p_info = pa_xnew0(struct port_info, 1);
12753a5a1b3Sopenharmony_ci        p_info->name = pa_xstrdup(port->name);
12853a5a1b3Sopenharmony_ci        p_info->offset = port->latency_offset;
12953a5a1b3Sopenharmony_ci        if (port->preferred_profile)
13053a5a1b3Sopenharmony_ci            p_info->profile = pa_xstrdup(port->preferred_profile);
13153a5a1b3Sopenharmony_ci    } else
13253a5a1b3Sopenharmony_ci        p_info = pa_xnew0(struct port_info, 1);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    return p_info;
13553a5a1b3Sopenharmony_ci}
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_cistatic void entry_free(struct entry* e) {
13853a5a1b3Sopenharmony_ci    pa_assert(e);
13953a5a1b3Sopenharmony_ci
14053a5a1b3Sopenharmony_ci    pa_xfree(e->preferred_output_port);
14153a5a1b3Sopenharmony_ci    pa_xfree(e->preferred_input_port);
14253a5a1b3Sopenharmony_ci    pa_xfree(e->profile);
14353a5a1b3Sopenharmony_ci    pa_hashmap_free(e->ports);
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci    pa_xfree(e);
14653a5a1b3Sopenharmony_ci}
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_cistatic struct entry *entry_from_card(pa_card *card) {
14953a5a1b3Sopenharmony_ci    struct port_info *p_info;
15053a5a1b3Sopenharmony_ci    struct entry *entry;
15153a5a1b3Sopenharmony_ci    pa_device_port *port;
15253a5a1b3Sopenharmony_ci    void *state;
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ci    pa_assert(card);
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci    entry = entry_new();
15753a5a1b3Sopenharmony_ci    entry->profile_is_sticky = card->profile_is_sticky;
15853a5a1b3Sopenharmony_ci    if (card->save_profile || entry->profile_is_sticky)
15953a5a1b3Sopenharmony_ci        entry->profile = pa_xstrdup(card->active_profile->name);
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci    if (card->preferred_input_port)
16253a5a1b3Sopenharmony_ci        entry->preferred_input_port = pa_xstrdup(card->preferred_input_port->name);
16353a5a1b3Sopenharmony_ci    if (card->preferred_output_port)
16453a5a1b3Sopenharmony_ci        entry->preferred_output_port = pa_xstrdup(card->preferred_output_port->name);
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(port, card->ports, state) {
16753a5a1b3Sopenharmony_ci        p_info = port_info_new(port);
16853a5a1b3Sopenharmony_ci        pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
16953a5a1b3Sopenharmony_ci    }
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_ci    return entry;
17253a5a1b3Sopenharmony_ci}
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_cistatic bool entrys_equal(struct entry *a, struct entry *b) {
17553a5a1b3Sopenharmony_ci    struct port_info *Ap_info, *Bp_info;
17653a5a1b3Sopenharmony_ci    void *state;
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci    pa_assert(a);
17953a5a1b3Sopenharmony_ci    pa_assert(b);
18053a5a1b3Sopenharmony_ci
18153a5a1b3Sopenharmony_ci    if (!pa_streq(a->profile, b->profile) ||
18253a5a1b3Sopenharmony_ci            pa_hashmap_size(a->ports) != pa_hashmap_size(b->ports))
18353a5a1b3Sopenharmony_ci        return false;
18453a5a1b3Sopenharmony_ci
18553a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(Ap_info, a->ports, state) {
18653a5a1b3Sopenharmony_ci        if ((Bp_info = pa_hashmap_get(b->ports, Ap_info->name))) {
18753a5a1b3Sopenharmony_ci            if (Ap_info->offset != Bp_info->offset)
18853a5a1b3Sopenharmony_ci                return false;
18953a5a1b3Sopenharmony_ci        } else
19053a5a1b3Sopenharmony_ci            return false;
19153a5a1b3Sopenharmony_ci    }
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci    if (!pa_safe_streq(a->preferred_input_port, b->preferred_input_port))
19453a5a1b3Sopenharmony_ci        return false;
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_ci    if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port))
19753a5a1b3Sopenharmony_ci        return false;
19853a5a1b3Sopenharmony_ci
19953a5a1b3Sopenharmony_ci    if (a->profile_is_sticky != b->profile_is_sticky)
20053a5a1b3Sopenharmony_ci        return false;
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_ci    return true;
20353a5a1b3Sopenharmony_ci}
20453a5a1b3Sopenharmony_ci
20553a5a1b3Sopenharmony_cistatic bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
20653a5a1b3Sopenharmony_ci    pa_tagstruct *t;
20753a5a1b3Sopenharmony_ci    pa_datum key, data;
20853a5a1b3Sopenharmony_ci    bool r;
20953a5a1b3Sopenharmony_ci    void *state;
21053a5a1b3Sopenharmony_ci    struct port_info *p_info;
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci    pa_assert(u);
21353a5a1b3Sopenharmony_ci    pa_assert(name);
21453a5a1b3Sopenharmony_ci    pa_assert(e);
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
21753a5a1b3Sopenharmony_ci    pa_tagstruct_putu8(t, ENTRY_VERSION);
21853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, e->profile);
21953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, pa_hashmap_size(e->ports));
22053a5a1b3Sopenharmony_ci
22153a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(p_info, e->ports, state) {
22253a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, p_info->name);
22353a5a1b3Sopenharmony_ci        pa_tagstruct_puts64(t, p_info->offset);
22453a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, p_info->profile);
22553a5a1b3Sopenharmony_ci    }
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, e->preferred_input_port);
22853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, e->preferred_output_port);
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, e->profile_is_sticky);
23153a5a1b3Sopenharmony_ci
23253a5a1b3Sopenharmony_ci    key.data = (char *) name;
23353a5a1b3Sopenharmony_ci    key.size = strlen(name);
23453a5a1b3Sopenharmony_ci
23553a5a1b3Sopenharmony_ci    data.data = (void*)pa_tagstruct_data(t, &data.size);
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ci    r = (pa_database_set(u->database, &key, &data, true) == 0);
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci    pa_tagstruct_free(t);
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    return r;
24253a5a1b3Sopenharmony_ci}
24353a5a1b3Sopenharmony_ci
24453a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
24553a5a1b3Sopenharmony_ci
24653a5a1b3Sopenharmony_ci#define LEGACY_ENTRY_VERSION 1
24753a5a1b3Sopenharmony_cistatic struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
24853a5a1b3Sopenharmony_ci    struct legacy_entry {
24953a5a1b3Sopenharmony_ci        uint8_t version;
25053a5a1b3Sopenharmony_ci        char profile[PA_NAME_MAX];
25153a5a1b3Sopenharmony_ci    } PA_GCC_PACKED ;
25253a5a1b3Sopenharmony_ci    struct legacy_entry *le;
25353a5a1b3Sopenharmony_ci    struct entry *e;
25453a5a1b3Sopenharmony_ci
25553a5a1b3Sopenharmony_ci    pa_assert(u);
25653a5a1b3Sopenharmony_ci    pa_assert(data);
25753a5a1b3Sopenharmony_ci
25853a5a1b3Sopenharmony_ci    if (data->size != sizeof(struct legacy_entry)) {
25953a5a1b3Sopenharmony_ci        pa_log_debug("Size does not match.");
26053a5a1b3Sopenharmony_ci        return NULL;
26153a5a1b3Sopenharmony_ci    }
26253a5a1b3Sopenharmony_ci
26353a5a1b3Sopenharmony_ci    le = (struct legacy_entry*)data->data;
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci    if (le->version != LEGACY_ENTRY_VERSION) {
26653a5a1b3Sopenharmony_ci        pa_log_debug("Version mismatch.");
26753a5a1b3Sopenharmony_ci        return NULL;
26853a5a1b3Sopenharmony_ci    }
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_ci    if (!memchr(le->profile, 0, sizeof(le->profile))) {
27153a5a1b3Sopenharmony_ci        pa_log_warn("Profile has missing NUL byte.");
27253a5a1b3Sopenharmony_ci        return NULL;
27353a5a1b3Sopenharmony_ci    }
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci    e = entry_new();
27653a5a1b3Sopenharmony_ci    e->profile = pa_xstrdup(le->profile);
27753a5a1b3Sopenharmony_ci    return e;
27853a5a1b3Sopenharmony_ci}
27953a5a1b3Sopenharmony_ci#endif
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_cistatic struct entry* entry_read(struct userdata *u, const char *name) {
28253a5a1b3Sopenharmony_ci    pa_datum key, data;
28353a5a1b3Sopenharmony_ci    struct entry *e = NULL;
28453a5a1b3Sopenharmony_ci    pa_tagstruct *t = NULL;
28553a5a1b3Sopenharmony_ci    const char* profile;
28653a5a1b3Sopenharmony_ci    uint8_t version;
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci    pa_assert(u);
28953a5a1b3Sopenharmony_ci    pa_assert(name);
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    key.data = (char*) name;
29253a5a1b3Sopenharmony_ci    key.size = strlen(name);
29353a5a1b3Sopenharmony_ci
29453a5a1b3Sopenharmony_ci    pa_zero(data);
29553a5a1b3Sopenharmony_ci
29653a5a1b3Sopenharmony_ci    if (!pa_database_get(u->database, &key, &data)) {
29753a5a1b3Sopenharmony_ci        pa_log_debug("Database contains no data for key: %s", name);
29853a5a1b3Sopenharmony_ci        return NULL;
29953a5a1b3Sopenharmony_ci    }
30053a5a1b3Sopenharmony_ci
30153a5a1b3Sopenharmony_ci    t = pa_tagstruct_new_fixed(data.data, data.size);
30253a5a1b3Sopenharmony_ci    e = entry_new();
30353a5a1b3Sopenharmony_ci
30453a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu8(t, &version) < 0 ||
30553a5a1b3Sopenharmony_ci        version > ENTRY_VERSION ||
30653a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &profile) < 0) {
30753a5a1b3Sopenharmony_ci
30853a5a1b3Sopenharmony_ci        goto fail;
30953a5a1b3Sopenharmony_ci    }
31053a5a1b3Sopenharmony_ci
31153a5a1b3Sopenharmony_ci    if (!profile)
31253a5a1b3Sopenharmony_ci        profile = "";
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_ci    e->profile = pa_xstrdup(profile);
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci    if (version >= 2) {
31753a5a1b3Sopenharmony_ci        uint32_t port_count = 0;
31853a5a1b3Sopenharmony_ci        const char *port_name = NULL, *profile_name = NULL;
31953a5a1b3Sopenharmony_ci        int64_t port_offset = 0;
32053a5a1b3Sopenharmony_ci        struct port_info *p_info;
32153a5a1b3Sopenharmony_ci        unsigned i;
32253a5a1b3Sopenharmony_ci
32353a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &port_count) < 0)
32453a5a1b3Sopenharmony_ci            goto fail;
32553a5a1b3Sopenharmony_ci
32653a5a1b3Sopenharmony_ci        for (i = 0; i < port_count; i++) {
32753a5a1b3Sopenharmony_ci            if (pa_tagstruct_gets(t, &port_name) < 0 ||
32853a5a1b3Sopenharmony_ci                !port_name ||
32953a5a1b3Sopenharmony_ci                pa_hashmap_get(e->ports, port_name) ||
33053a5a1b3Sopenharmony_ci                pa_tagstruct_gets64(t, &port_offset) < 0)
33153a5a1b3Sopenharmony_ci                goto fail;
33253a5a1b3Sopenharmony_ci            if (version >= 3 && pa_tagstruct_gets(t, &profile_name) < 0)
33353a5a1b3Sopenharmony_ci                goto fail;
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_ci            p_info = port_info_new(NULL);
33653a5a1b3Sopenharmony_ci            p_info->name = pa_xstrdup(port_name);
33753a5a1b3Sopenharmony_ci            p_info->offset = port_offset;
33853a5a1b3Sopenharmony_ci            if (profile_name)
33953a5a1b3Sopenharmony_ci                p_info->profile = pa_xstrdup(profile_name);
34053a5a1b3Sopenharmony_ci
34153a5a1b3Sopenharmony_ci            pa_assert_se(pa_hashmap_put(e->ports, p_info->name, p_info) >= 0);
34253a5a1b3Sopenharmony_ci        }
34353a5a1b3Sopenharmony_ci    }
34453a5a1b3Sopenharmony_ci
34553a5a1b3Sopenharmony_ci    if (version >= 4) {
34653a5a1b3Sopenharmony_ci        const char *preferred_input_port;
34753a5a1b3Sopenharmony_ci        const char *preferred_output_port;
34853a5a1b3Sopenharmony_ci
34953a5a1b3Sopenharmony_ci        if (pa_tagstruct_gets(t, &preferred_input_port) < 0
35053a5a1b3Sopenharmony_ci                || pa_tagstruct_gets(t, &preferred_output_port) < 0)
35153a5a1b3Sopenharmony_ci            goto fail;
35253a5a1b3Sopenharmony_ci
35353a5a1b3Sopenharmony_ci        e->preferred_input_port = pa_xstrdup(preferred_input_port);
35453a5a1b3Sopenharmony_ci        e->preferred_output_port = pa_xstrdup(preferred_output_port);
35553a5a1b3Sopenharmony_ci    }
35653a5a1b3Sopenharmony_ci
35753a5a1b3Sopenharmony_ci    if (version >= 5) {
35853a5a1b3Sopenharmony_ci        bool profile_is_sticky;
35953a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &profile_is_sticky) < 0)
36053a5a1b3Sopenharmony_ci            goto fail;
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_ci        e->profile_is_sticky = profile_is_sticky;
36353a5a1b3Sopenharmony_ci    }
36453a5a1b3Sopenharmony_ci
36553a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t))
36653a5a1b3Sopenharmony_ci        goto fail;
36753a5a1b3Sopenharmony_ci
36853a5a1b3Sopenharmony_ci    pa_tagstruct_free(t);
36953a5a1b3Sopenharmony_ci    pa_datum_free(&data);
37053a5a1b3Sopenharmony_ci
37153a5a1b3Sopenharmony_ci    return e;
37253a5a1b3Sopenharmony_ci
37353a5a1b3Sopenharmony_cifail:
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
37653a5a1b3Sopenharmony_ci
37753a5a1b3Sopenharmony_ci    if (e)
37853a5a1b3Sopenharmony_ci        entry_free(e);
37953a5a1b3Sopenharmony_ci    if (t)
38053a5a1b3Sopenharmony_ci        pa_tagstruct_free(t);
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
38353a5a1b3Sopenharmony_ci    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
38453a5a1b3Sopenharmony_ci    if ((e = legacy_entry_read(u, &data))) {
38553a5a1b3Sopenharmony_ci        pa_log_debug("Success. Saving new format for key: %s", name);
38653a5a1b3Sopenharmony_ci        if (entry_write(u, name, e))
38753a5a1b3Sopenharmony_ci            trigger_save(u);
38853a5a1b3Sopenharmony_ci        pa_datum_free(&data);
38953a5a1b3Sopenharmony_ci        return e;
39053a5a1b3Sopenharmony_ci    } else
39153a5a1b3Sopenharmony_ci        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
39253a5a1b3Sopenharmony_ci#endif
39353a5a1b3Sopenharmony_ci
39453a5a1b3Sopenharmony_ci    pa_datum_free(&data);
39553a5a1b3Sopenharmony_ci    return NULL;
39653a5a1b3Sopenharmony_ci}
39753a5a1b3Sopenharmony_ci
39853a5a1b3Sopenharmony_cistatic void show_full_info(pa_card *card) {
39953a5a1b3Sopenharmony_ci    pa_assert(card);
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci    if (card->save_profile)
40253a5a1b3Sopenharmony_ci        pa_log_info("Storing profile and port latency offsets for card %s.", card->name);
40353a5a1b3Sopenharmony_ci    else
40453a5a1b3Sopenharmony_ci        pa_log_info("Storing port latency offsets for card %s.", card->name);
40553a5a1b3Sopenharmony_ci}
40653a5a1b3Sopenharmony_ci
40753a5a1b3Sopenharmony_cistatic pa_hook_result_t card_put_hook_callback(pa_core *c, pa_card *card, struct userdata *u) {
40853a5a1b3Sopenharmony_ci    struct entry *entry, *old;
40953a5a1b3Sopenharmony_ci
41053a5a1b3Sopenharmony_ci    pa_assert(card);
41153a5a1b3Sopenharmony_ci
41253a5a1b3Sopenharmony_ci    entry = entry_from_card(card);
41353a5a1b3Sopenharmony_ci
41453a5a1b3Sopenharmony_ci    if ((old = entry_read(u, card->name))) {
41553a5a1b3Sopenharmony_ci        if (!card->save_profile)
41653a5a1b3Sopenharmony_ci            entry->profile = pa_xstrdup(old->profile);
41753a5a1b3Sopenharmony_ci        if (entrys_equal(entry, old))
41853a5a1b3Sopenharmony_ci            goto finish;
41953a5a1b3Sopenharmony_ci    }
42053a5a1b3Sopenharmony_ci
42153a5a1b3Sopenharmony_ci    show_full_info(card);
42253a5a1b3Sopenharmony_ci
42353a5a1b3Sopenharmony_ci    if (entry_write(u, card->name, entry))
42453a5a1b3Sopenharmony_ci        trigger_save(u);
42553a5a1b3Sopenharmony_ci
42653a5a1b3Sopenharmony_cifinish:
42753a5a1b3Sopenharmony_ci    entry_free(entry);
42853a5a1b3Sopenharmony_ci    if (old)
42953a5a1b3Sopenharmony_ci        entry_free(old);
43053a5a1b3Sopenharmony_ci
43153a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
43253a5a1b3Sopenharmony_ci}
43353a5a1b3Sopenharmony_ci
43453a5a1b3Sopenharmony_cistatic void update_profile_for_port(struct entry *entry, pa_card *card, pa_device_port *p) {
43553a5a1b3Sopenharmony_ci    struct port_info *p_info;
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_ci    if (p == NULL)
43853a5a1b3Sopenharmony_ci        return;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci    if (!(p_info = pa_hashmap_get(entry->ports, p->name))) {
44153a5a1b3Sopenharmony_ci        p_info = port_info_new(p);
44253a5a1b3Sopenharmony_ci        pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
44353a5a1b3Sopenharmony_ci    }
44453a5a1b3Sopenharmony_ci
44553a5a1b3Sopenharmony_ci    if (!pa_safe_streq(p_info->profile, p->preferred_profile)) {
44653a5a1b3Sopenharmony_ci        pa_xfree(p_info->profile);
44753a5a1b3Sopenharmony_ci        p_info->profile = pa_xstrdup(p->preferred_profile);
44853a5a1b3Sopenharmony_ci        pa_log_info("Storing profile %s for port %s on card %s.", p_info->profile, p->name, card->name);
44953a5a1b3Sopenharmony_ci    }
45053a5a1b3Sopenharmony_ci}
45153a5a1b3Sopenharmony_ci
45253a5a1b3Sopenharmony_cistatic pa_hook_result_t card_profile_changed_callback(pa_core *c, pa_card *card, struct userdata *u) {
45353a5a1b3Sopenharmony_ci    struct entry *entry;
45453a5a1b3Sopenharmony_ci    pa_sink *sink;
45553a5a1b3Sopenharmony_ci    pa_source *source;
45653a5a1b3Sopenharmony_ci    uint32_t state;
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci    pa_assert(card);
45953a5a1b3Sopenharmony_ci
46053a5a1b3Sopenharmony_ci    if (!card->save_profile && !card->profile_is_sticky)
46153a5a1b3Sopenharmony_ci        return PA_HOOK_OK;
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci    if ((entry = entry_read(u, card->name))) {
46453a5a1b3Sopenharmony_ci        pa_xfree(entry->profile);
46553a5a1b3Sopenharmony_ci        entry->profile_is_sticky = card->profile_is_sticky;
46653a5a1b3Sopenharmony_ci        entry->profile = pa_xstrdup(card->active_profile->name);
46753a5a1b3Sopenharmony_ci        pa_log_info("Storing card profile for card %s.", card->name);
46853a5a1b3Sopenharmony_ci    } else {
46953a5a1b3Sopenharmony_ci        entry = entry_from_card(card);
47053a5a1b3Sopenharmony_ci        show_full_info(card);
47153a5a1b3Sopenharmony_ci    }
47253a5a1b3Sopenharmony_ci
47353a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(sink, card->sinks, state)
47453a5a1b3Sopenharmony_ci        update_profile_for_port(entry, card, sink->active_port);
47553a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(source, card->sources, state)
47653a5a1b3Sopenharmony_ci        update_profile_for_port(entry, card, source->active_port);
47753a5a1b3Sopenharmony_ci
47853a5a1b3Sopenharmony_ci    if (entry_write(u, card->name, entry))
47953a5a1b3Sopenharmony_ci        trigger_save(u);
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    entry_free(entry);
48253a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
48353a5a1b3Sopenharmony_ci}
48453a5a1b3Sopenharmony_ci
48553a5a1b3Sopenharmony_cistatic pa_hook_result_t card_profile_added_callback(pa_core *c, pa_card_profile *profile, struct userdata *u) {
48653a5a1b3Sopenharmony_ci    struct entry *entry;
48753a5a1b3Sopenharmony_ci
48853a5a1b3Sopenharmony_ci    pa_assert(profile);
48953a5a1b3Sopenharmony_ci
49053a5a1b3Sopenharmony_ci    if (profile->available == PA_AVAILABLE_NO)
49153a5a1b3Sopenharmony_ci        return PA_HOOK_OK;
49253a5a1b3Sopenharmony_ci
49353a5a1b3Sopenharmony_ci    if (!(entry = entry_read(u, profile->card->name)))
49453a5a1b3Sopenharmony_ci        return PA_HOOK_OK;
49553a5a1b3Sopenharmony_ci
49653a5a1b3Sopenharmony_ci    if (pa_safe_streq(entry->profile, profile->name)) {
49753a5a1b3Sopenharmony_ci        if (pa_card_set_profile(profile->card, profile, true) >= 0)
49853a5a1b3Sopenharmony_ci            pa_log_info("Restored profile '%s' for card %s.", profile->name, profile->card->name);
49953a5a1b3Sopenharmony_ci    }
50053a5a1b3Sopenharmony_ci
50153a5a1b3Sopenharmony_ci    entry_free(entry);
50253a5a1b3Sopenharmony_ci
50353a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
50453a5a1b3Sopenharmony_ci}
50553a5a1b3Sopenharmony_ci
50653a5a1b3Sopenharmony_cistatic pa_hook_result_t port_offset_change_callback(pa_core *c, pa_device_port *port, struct userdata *u) {
50753a5a1b3Sopenharmony_ci    struct entry *entry;
50853a5a1b3Sopenharmony_ci    pa_card *card;
50953a5a1b3Sopenharmony_ci
51053a5a1b3Sopenharmony_ci    pa_assert(port);
51153a5a1b3Sopenharmony_ci    card = port->card;
51253a5a1b3Sopenharmony_ci
51353a5a1b3Sopenharmony_ci    if ((entry = entry_read(u, card->name))) {
51453a5a1b3Sopenharmony_ci        struct port_info *p_info;
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci        if ((p_info = pa_hashmap_get(entry->ports, port->name)))
51753a5a1b3Sopenharmony_ci            p_info->offset = port->latency_offset;
51853a5a1b3Sopenharmony_ci        else {
51953a5a1b3Sopenharmony_ci            p_info = port_info_new(port);
52053a5a1b3Sopenharmony_ci            pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
52153a5a1b3Sopenharmony_ci        }
52253a5a1b3Sopenharmony_ci
52353a5a1b3Sopenharmony_ci        pa_log_info("Storing latency offset for port %s on card %s.", port->name, card->name);
52453a5a1b3Sopenharmony_ci
52553a5a1b3Sopenharmony_ci    } else {
52653a5a1b3Sopenharmony_ci        entry = entry_from_card(card);
52753a5a1b3Sopenharmony_ci        show_full_info(card);
52853a5a1b3Sopenharmony_ci    }
52953a5a1b3Sopenharmony_ci
53053a5a1b3Sopenharmony_ci    if (entry_write(u, card->name, entry))
53153a5a1b3Sopenharmony_ci        trigger_save(u);
53253a5a1b3Sopenharmony_ci
53353a5a1b3Sopenharmony_ci    entry_free(entry);
53453a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
53553a5a1b3Sopenharmony_ci}
53653a5a1b3Sopenharmony_ci
53753a5a1b3Sopenharmony_cistatic pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
53853a5a1b3Sopenharmony_ci    struct entry *e;
53953a5a1b3Sopenharmony_ci    void *state;
54053a5a1b3Sopenharmony_ci    pa_device_port *p;
54153a5a1b3Sopenharmony_ci    struct port_info *p_info;
54253a5a1b3Sopenharmony_ci
54353a5a1b3Sopenharmony_ci    pa_assert(new_data);
54453a5a1b3Sopenharmony_ci
54553a5a1b3Sopenharmony_ci    if (!(e = entry_read(u, new_data->name)))
54653a5a1b3Sopenharmony_ci        return PA_HOOK_OK;
54753a5a1b3Sopenharmony_ci
54853a5a1b3Sopenharmony_ci    /* Always restore the latency offsets because their
54953a5a1b3Sopenharmony_ci     * initial value is always 0 */
55053a5a1b3Sopenharmony_ci
55153a5a1b3Sopenharmony_ci    pa_log_info("Restoring port latency offsets for card %s.", new_data->name);
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(p_info, e->ports, state)
55453a5a1b3Sopenharmony_ci        if ((p = pa_hashmap_get(new_data->ports, p_info->name))) {
55553a5a1b3Sopenharmony_ci            p->latency_offset = p_info->offset;
55653a5a1b3Sopenharmony_ci            if (!p->preferred_profile && p_info->profile)
55753a5a1b3Sopenharmony_ci                pa_device_port_set_preferred_profile(p, p_info->profile);
55853a5a1b3Sopenharmony_ci        }
55953a5a1b3Sopenharmony_ci
56053a5a1b3Sopenharmony_ci    if (e->preferred_input_port) {
56153a5a1b3Sopenharmony_ci        p = pa_hashmap_get(new_data->ports, e->preferred_input_port);
56253a5a1b3Sopenharmony_ci        if (p)
56353a5a1b3Sopenharmony_ci            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_INPUT, p);
56453a5a1b3Sopenharmony_ci    }
56553a5a1b3Sopenharmony_ci
56653a5a1b3Sopenharmony_ci    if (e->preferred_output_port) {
56753a5a1b3Sopenharmony_ci        p = pa_hashmap_get(new_data->ports, e->preferred_output_port);
56853a5a1b3Sopenharmony_ci        if (p)
56953a5a1b3Sopenharmony_ci            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_OUTPUT, p);
57053a5a1b3Sopenharmony_ci    }
57153a5a1b3Sopenharmony_ci
57253a5a1b3Sopenharmony_ci    entry_free(e);
57353a5a1b3Sopenharmony_ci
57453a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
57553a5a1b3Sopenharmony_ci}
57653a5a1b3Sopenharmony_ci
57753a5a1b3Sopenharmony_cistatic pa_hook_result_t card_choose_initial_profile_callback(pa_core *core, pa_card *card, struct userdata *u) {
57853a5a1b3Sopenharmony_ci    struct entry *e;
57953a5a1b3Sopenharmony_ci
58053a5a1b3Sopenharmony_ci    if (!(e = entry_read(u, card->name)))
58153a5a1b3Sopenharmony_ci        return PA_HOOK_OK;
58253a5a1b3Sopenharmony_ci
58353a5a1b3Sopenharmony_ci    if (!u->restore_bluetooth_profile) {
58453a5a1b3Sopenharmony_ci        const char *s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
58553a5a1b3Sopenharmony_ci        if (pa_safe_streq(s, "bluetooth"))
58653a5a1b3Sopenharmony_ci            goto finish;
58753a5a1b3Sopenharmony_ci    }
58853a5a1b3Sopenharmony_ci
58953a5a1b3Sopenharmony_ci    card->profile_is_sticky = e->profile_is_sticky;
59053a5a1b3Sopenharmony_ci    pa_log_info("Profile '%s' was previously %s for card %s.",
59153a5a1b3Sopenharmony_ci            e->profile,
59253a5a1b3Sopenharmony_ci            card->profile_is_sticky ? "sticky" : "automatically selected",
59353a5a1b3Sopenharmony_ci            card->name);
59453a5a1b3Sopenharmony_ci
59553a5a1b3Sopenharmony_ci    if (e->profile[0]) {
59653a5a1b3Sopenharmony_ci        pa_card_profile *profile;
59753a5a1b3Sopenharmony_ci
59853a5a1b3Sopenharmony_ci        profile = pa_hashmap_get(card->profiles, e->profile);
59953a5a1b3Sopenharmony_ci        if (profile) {
60053a5a1b3Sopenharmony_ci            if (profile->available != PA_AVAILABLE_NO || card->profile_is_sticky) {
60153a5a1b3Sopenharmony_ci                pa_log_info("Restoring profile '%s' for card %s.", profile->name, card->name);
60253a5a1b3Sopenharmony_ci                pa_card_set_profile(card, profile, true);
60353a5a1b3Sopenharmony_ci            } else
60453a5a1b3Sopenharmony_ci                pa_log_debug("Not restoring profile %s for card %s, because the profile is currently unavailable.",
60553a5a1b3Sopenharmony_ci                             profile->name, card->name);
60653a5a1b3Sopenharmony_ci        } else {
60753a5a1b3Sopenharmony_ci            pa_log_debug("Tried to restore profile %s for card %s, but the card doesn't have such profile.",
60853a5a1b3Sopenharmony_ci                         e->profile, card->name);
60953a5a1b3Sopenharmony_ci        }
61053a5a1b3Sopenharmony_ci    }
61153a5a1b3Sopenharmony_ci
61253a5a1b3Sopenharmony_cifinish:
61353a5a1b3Sopenharmony_ci    entry_free(e);
61453a5a1b3Sopenharmony_ci
61553a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
61653a5a1b3Sopenharmony_ci}
61753a5a1b3Sopenharmony_ci
61853a5a1b3Sopenharmony_cistatic pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_card_preferred_port_changed_hook_data *data,
61953a5a1b3Sopenharmony_ci                                                             struct userdata *u) {
62053a5a1b3Sopenharmony_ci    struct entry *e;
62153a5a1b3Sopenharmony_ci    pa_card *card;
62253a5a1b3Sopenharmony_ci
62353a5a1b3Sopenharmony_ci    card = data->card;
62453a5a1b3Sopenharmony_ci
62553a5a1b3Sopenharmony_ci    e = entry_read(u, card->name);
62653a5a1b3Sopenharmony_ci    if (!e)
62753a5a1b3Sopenharmony_ci        e = entry_from_card(card);
62853a5a1b3Sopenharmony_ci
62953a5a1b3Sopenharmony_ci    if (data->direction == PA_DIRECTION_INPUT) {
63053a5a1b3Sopenharmony_ci        pa_xfree(e->preferred_input_port);
63153a5a1b3Sopenharmony_ci        e->preferred_input_port = pa_xstrdup(card->preferred_input_port ? card->preferred_input_port->name : NULL);
63253a5a1b3Sopenharmony_ci    } else {
63353a5a1b3Sopenharmony_ci        pa_xfree(e->preferred_output_port);
63453a5a1b3Sopenharmony_ci        e->preferred_output_port = pa_xstrdup(card->preferred_output_port ? card->preferred_output_port->name : NULL);
63553a5a1b3Sopenharmony_ci    }
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci    if (entry_write(u, card->name, e))
63853a5a1b3Sopenharmony_ci        trigger_save(u);
63953a5a1b3Sopenharmony_ci
64053a5a1b3Sopenharmony_ci    entry_free(e);
64153a5a1b3Sopenharmony_ci
64253a5a1b3Sopenharmony_ci    return PA_HOOK_OK;
64353a5a1b3Sopenharmony_ci}
64453a5a1b3Sopenharmony_ci
64553a5a1b3Sopenharmony_ciint pa__init(pa_module*m) {
64653a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
64753a5a1b3Sopenharmony_ci    struct userdata *u;
64853a5a1b3Sopenharmony_ci    char *state_path;
64953a5a1b3Sopenharmony_ci    bool restore_bluetooth_profile;
65053a5a1b3Sopenharmony_ci
65153a5a1b3Sopenharmony_ci    pa_assert(m);
65253a5a1b3Sopenharmony_ci
65353a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
65453a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments");
65553a5a1b3Sopenharmony_ci        goto fail;
65653a5a1b3Sopenharmony_ci    }
65753a5a1b3Sopenharmony_ci
65853a5a1b3Sopenharmony_ci    restore_bluetooth_profile = false;
65953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "restore_bluetooth_profile", &restore_bluetooth_profile) < 0) {
66053a5a1b3Sopenharmony_ci        pa_log("Invalid boolean value for restore_bluetooth_profile parameter");
66153a5a1b3Sopenharmony_ci        goto fail;
66253a5a1b3Sopenharmony_ci    }
66353a5a1b3Sopenharmony_ci
66453a5a1b3Sopenharmony_ci    m->userdata = u = pa_xnew0(struct userdata, 1);
66553a5a1b3Sopenharmony_ci    u->core = m->core;
66653a5a1b3Sopenharmony_ci    u->module = m;
66753a5a1b3Sopenharmony_ci    u->restore_bluetooth_profile = restore_bluetooth_profile;
66853a5a1b3Sopenharmony_ci
66953a5a1b3Sopenharmony_ci    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);
67053a5a1b3Sopenharmony_ci    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL,
67153a5a1b3Sopenharmony_ci                           (pa_hook_cb_t) card_choose_initial_profile_callback, u);
67253a5a1b3Sopenharmony_ci    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);
67353a5a1b3Sopenharmony_ci    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);
67453a5a1b3Sopenharmony_ci    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);
67553a5a1b3Sopenharmony_ci    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);
67653a5a1b3Sopenharmony_ci    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);
67753a5a1b3Sopenharmony_ci
67853a5a1b3Sopenharmony_ci    if (!(state_path = pa_state_path(NULL, true)))
67953a5a1b3Sopenharmony_ci        goto fail;
68053a5a1b3Sopenharmony_ci
68153a5a1b3Sopenharmony_ci    if (!(u->database = pa_database_open(state_path, "card-database", true, true))) {
68253a5a1b3Sopenharmony_ci        pa_xfree(state_path);
68353a5a1b3Sopenharmony_ci        goto fail;
68453a5a1b3Sopenharmony_ci    }
68553a5a1b3Sopenharmony_ci
68653a5a1b3Sopenharmony_ci    pa_xfree(state_path);
68753a5a1b3Sopenharmony_ci
68853a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
68953a5a1b3Sopenharmony_ci    return 0;
69053a5a1b3Sopenharmony_ci
69153a5a1b3Sopenharmony_cifail:
69253a5a1b3Sopenharmony_ci    pa__done(m);
69353a5a1b3Sopenharmony_ci
69453a5a1b3Sopenharmony_ci    if (ma)
69553a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
69653a5a1b3Sopenharmony_ci
69753a5a1b3Sopenharmony_ci    return -1;
69853a5a1b3Sopenharmony_ci}
69953a5a1b3Sopenharmony_ci
70053a5a1b3Sopenharmony_civoid pa__done(pa_module*m) {
70153a5a1b3Sopenharmony_ci    struct userdata* u;
70253a5a1b3Sopenharmony_ci
70353a5a1b3Sopenharmony_ci    pa_assert(m);
70453a5a1b3Sopenharmony_ci
70553a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
70653a5a1b3Sopenharmony_ci        return;
70753a5a1b3Sopenharmony_ci
70853a5a1b3Sopenharmony_ci    if (u->save_time_event) {
70953a5a1b3Sopenharmony_ci        u->core->mainloop->time_free(u->save_time_event);
71053a5a1b3Sopenharmony_ci        pa_database_sync(u->database);
71153a5a1b3Sopenharmony_ci    }
71253a5a1b3Sopenharmony_ci
71353a5a1b3Sopenharmony_ci    if (u->database)
71453a5a1b3Sopenharmony_ci        pa_database_close(u->database);
71553a5a1b3Sopenharmony_ci
71653a5a1b3Sopenharmony_ci    pa_xfree(u);
71753a5a1b3Sopenharmony_ci}
718