module-card-restore.c 20 KB
Newer Older
1
2
3
4
5
6
7
/***
  This file is part of PulseAudio.

  Copyright 2006-2008 Lennart Poettering

  PulseAudio is free software; you can redistribute it and/or modify
  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
10
11
12
13
14
15
16
  or (at your option) any later version.

  PulseAudio is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  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
21
22
23
24
25
26
27
28
29
30
***/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

Maarten Bosmans's avatar
Maarten Bosmans committed
31
#include <pulse/gccmacro.h>
32
33
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
34
#include <pulse/rtclock.h>
35
36
37
38
39
40
41
42
43

#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/card.h>
#include <pulsecore/namereg.h>
44
#include <pulsecore/database.h>
45
#include <pulsecore/tagstruct.h>
46
47
48
49

PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
PA_MODULE_VERSION(PACKAGE_VERSION);
50
PA_MODULE_LOAD_ONCE(true);
51
52
53
PA_MODULE_USAGE(
    "restore_bluetooth_profile=<boolean>"
);
54

55
#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
56
57

static const char* const valid_modargs[] = {
58
59
    "restore_bluetooth_profile",
    NULL
60
61
62
63
64
65
};

struct userdata {
    pa_core *core;
    pa_module *module;
    pa_time_event *save_time_event;
66
    pa_database *database;
67
    bool restore_bluetooth_profile;
68
69
};

70
#define ENTRY_VERSION 5
71
72
73
74

struct port_info {
    char *name;
    int64_t offset;
75
    char *profile;
76
};
77

78
struct entry {
79
    char *profile;
80
    pa_hashmap *ports; /* Port name -> struct port_info */
81
82
    char *preferred_input_port;
    char *preferred_output_port;
83
    bool profile_is_sticky; /* since version 5; must be restored together with profile name */
84
};
85

86
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
87
88
89
90
91
92
93
94
95
96
    struct userdata *u = userdata;

    pa_assert(a);
    pa_assert(e);
    pa_assert(u);

    pa_assert(e == u->save_time_event);
    u->core->mainloop->time_free(u->save_time_event);
    u->save_time_event = NULL;

97
    pa_database_sync(u->database);
98
99
100
    pa_log_info("Synced.");
}

101
102
103
104
105
106
107
static void trigger_save(struct userdata *u) {
    if (u->save_time_event)
        return;

    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
}

108
109
110
static void port_info_free(struct port_info *p_info) {
    pa_assert(p_info);

111
    pa_xfree(p_info->profile);
112
113
114
115
    pa_xfree(p_info->name);
    pa_xfree(p_info);
}

116
117
static struct entry* entry_new(void) {
    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
120
121
    return r;
}

122
123
124
125
static struct port_info *port_info_new(pa_device_port *port) {
    struct port_info *p_info;

    if (port) {
126
        p_info = pa_xnew0(struct port_info, 1);
127
128
        p_info->name = pa_xstrdup(port->name);
        p_info->offset = port->latency_offset;
129
130
        if (port->preferred_profile)
            p_info->profile = pa_xstrdup(port->preferred_profile);
131
132
133
134
135
136
    } else
        p_info = pa_xnew0(struct port_info, 1);

    return p_info;
}

137
138
139
static void entry_free(struct entry* e) {
    pa_assert(e);

140
141
    pa_xfree(e->preferred_output_port);
    pa_xfree(e->preferred_input_port);
142
    pa_xfree(e->profile);
143
    pa_hashmap_free(e->ports);
144

145
146
147
    pa_xfree(e);
}

148
149
150
151
152
153
154
155
156
static struct entry *entry_from_card(pa_card *card) {
    struct port_info *p_info;
    struct entry *entry;
    pa_device_port *port;
    void *state;

    pa_assert(card);

    entry = entry_new();
157
158
    entry->profile_is_sticky = card->profile_is_sticky;
    if (card->save_profile || entry->profile_is_sticky)
159
160
        entry->profile = pa_xstrdup(card->active_profile->name);

161
162
163
164
165
    if (card->preferred_input_port)
        entry->preferred_input_port = pa_xstrdup(card->preferred_input_port->name);
    if (card->preferred_output_port)
        entry->preferred_output_port = pa_xstrdup(card->preferred_output_port->name);

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    PA_HASHMAP_FOREACH(port, card->ports, state) {
        p_info = port_info_new(port);
        pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
    }

    return entry;
}

static bool entrys_equal(struct entry *a, struct entry *b) {
    struct port_info *Ap_info, *Bp_info;
    void *state;

    pa_assert(a);
    pa_assert(b);

    if (!pa_streq(a->profile, b->profile) ||
            pa_hashmap_size(a->ports) != pa_hashmap_size(b->ports))
        return false;

    PA_HASHMAP_FOREACH(Ap_info, a->ports, state) {
        if ((Bp_info = pa_hashmap_get(b->ports, Ap_info->name))) {
            if (Ap_info->offset != Bp_info->offset)
                return false;
        } else
            return false;
    }

193
194
195
196
197
198
    if (!pa_safe_streq(a->preferred_input_port, b->preferred_input_port))
        return false;

    if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port))
        return false;

199
200
201
    if (a->profile_is_sticky != b->profile_is_sticky)
        return false;

202
203
204
    return true;
}

205
static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
206
207
    pa_tagstruct *t;
    pa_datum key, data;
208
    bool r;
209
210
    void *state;
    struct port_info *p_info;
211
212
213
214
215

    pa_assert(u);
    pa_assert(name);
    pa_assert(e);

216
    t = pa_tagstruct_new();
217
    pa_tagstruct_putu8(t, ENTRY_VERSION);
218
    pa_tagstruct_puts(t, e->profile);
219
220
221
222
223
    pa_tagstruct_putu32(t, pa_hashmap_size(e->ports));

    PA_HASHMAP_FOREACH(p_info, e->ports, state) {
        pa_tagstruct_puts(t, p_info->name);
        pa_tagstruct_puts64(t, p_info->offset);
224
        pa_tagstruct_puts(t, p_info->profile);
225
    }
226

227
228
229
    pa_tagstruct_puts(t, e->preferred_input_port);
    pa_tagstruct_puts(t, e->preferred_output_port);

230
231
    pa_tagstruct_put_boolean(t, e->profile_is_sticky);

232
233
234
235
236
    key.data = (char *) name;
    key.size = strlen(name);

    data.data = (void*)pa_tagstruct_data(t, &data.size);

237
    r = (pa_database_set(u->database, &key, &data, true) == 0);
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

    pa_tagstruct_free(t);

    return r;
}

#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT

#define LEGACY_ENTRY_VERSION 1
static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
    struct legacy_entry {
        uint8_t version;
        char profile[PA_NAME_MAX];
    } PA_GCC_PACKED ;
    struct legacy_entry *le;
    struct entry *e;

    pa_assert(u);
    pa_assert(data);

    if (data->size != sizeof(struct legacy_entry)) {
        pa_log_debug("Size does not match.");
        return NULL;
    }

    le = (struct legacy_entry*)data->data;

    if (le->version != LEGACY_ENTRY_VERSION) {
        pa_log_debug("Version mismatch.");
        return NULL;
    }

    if (!memchr(le->profile, 0, sizeof(le->profile))) {
        pa_log_warn("Profile has missing NUL byte.");
        return NULL;
    }

    e = entry_new();
    e->profile = pa_xstrdup(le->profile);
    return e;
}
#endif

281
static struct entry* entry_read(struct userdata *u, const char *name) {
282
    pa_datum key, data;
283
284
285
    struct entry *e = NULL;
    pa_tagstruct *t = NULL;
    const char* profile;
286
    uint8_t version;
287
288
289
290

    pa_assert(u);
    pa_assert(name);

291
292
    key.data = (char*) name;
    key.size = strlen(name);
293

294
    pa_zero(data);
295

296
297
298
299
    if (!pa_database_get(u->database, &key, &data)) {
        pa_log_debug("Database contains no data for key: %s", name);
        return NULL;
    }
300

301
    t = pa_tagstruct_new_fixed(data.data, data.size);
302
    e = entry_new();
303

304
305
    if (pa_tagstruct_getu8(t, &version) < 0 ||
        version > ENTRY_VERSION ||
306
        pa_tagstruct_gets(t, &profile) < 0) {
307

308
309
310
        goto fail;
    }

311
312
313
    if (!profile)
        profile = "";

314
315
    e->profile = pa_xstrdup(profile);

316
    if (version >= 2) {
317
        uint32_t port_count = 0;
318
        const char *port_name = NULL, *profile_name = NULL;
319
320
321
322
323
324
325
326
327
328
329
330
331
        int64_t port_offset = 0;
        struct port_info *p_info;
        unsigned i;

        if (pa_tagstruct_getu32(t, &port_count) < 0)
            goto fail;

        for (i = 0; i < port_count; i++) {
            if (pa_tagstruct_gets(t, &port_name) < 0 ||
                !port_name ||
                pa_hashmap_get(e->ports, port_name) ||
                pa_tagstruct_gets64(t, &port_offset) < 0)
                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
337
            p_info->name = pa_xstrdup(port_name);
            p_info->offset = port_offset;
338
339
            if (profile_name)
                p_info->profile = pa_xstrdup(profile_name);
340
341
342
343
344

            pa_assert_se(pa_hashmap_put(e->ports, p_info->name, p_info) >= 0);
        }
    }

345
346
347
348
349
350
351
352
353
354
355
356
    if (version >= 4) {
        const char *preferred_input_port;
        const char *preferred_output_port;

        if (pa_tagstruct_gets(t, &preferred_input_port) < 0
                || pa_tagstruct_gets(t, &preferred_output_port) < 0)
            goto fail;

        e->preferred_input_port = pa_xstrdup(preferred_input_port);
        e->preferred_output_port = pa_xstrdup(preferred_output_port);
    }

357
358
359
360
361
362
363
364
    if (version >= 5) {
        bool profile_is_sticky;
        if (pa_tagstruct_get_boolean(t, &profile_is_sticky) < 0)
            goto fail;

        e->profile_is_sticky = profile_is_sticky;
    }

365
    if (!pa_tagstruct_eof(t))
366
        goto fail;
367
368
369

    pa_tagstruct_free(t);
    pa_datum_free(&data);
370
371
372
373
374

    return e;

fail:

375
376
377
378
379
380
    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);

    if (e)
        entry_free(e);
    if (t)
        pa_tagstruct_free(t);
381

382
383
384
385
386
387
388
389
390
391
392
#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
    if ((e = legacy_entry_read(u, &data))) {
        pa_log_debug("Success. Saving new format for key: %s", name);
        if (entry_write(u, name, e))
            trigger_save(u);
        pa_datum_free(&data);
        return e;
    } else
        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
#endif
393

394
395
    pa_datum_free(&data);
    return NULL;
396
397
}

398
399
400
401
402
403
404
405
406
407
static void show_full_info(pa_card *card) {
    pa_assert(card);

    if (card->save_profile)
        pa_log_info("Storing profile and port latency offsets for card %s.", card->name);
    else
        pa_log_info("Storing port latency offsets for card %s.", card->name);
}

static 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
415
416
417
418
419
    if ((old = entry_read(u, card->name))) {
        if (!card->save_profile)
            entry->profile = pa_xstrdup(old->profile);
        if (entrys_equal(entry, old))
            goto finish;
    }
420

421
    show_full_info(card);
422

423
424
    if (entry_write(u, card->name, entry))
        trigger_save(u);
425

426
427
428
429
finish:
    entry_free(entry);
    if (old)
        entry_free(old);
430

431
432
433
    return PA_HOOK_OK;
}

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
static void update_profile_for_port(struct entry *entry, pa_card *card, pa_device_port *p) {
    struct port_info *p_info;

    if (p == NULL)
        return;

    if (!(p_info = pa_hashmap_get(entry->ports, p->name))) {
        p_info = port_info_new(p);
        pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
    }

    if (!pa_safe_streq(p_info->profile, p->preferred_profile)) {
        pa_xfree(p_info->profile);
        p_info->profile = pa_xstrdup(p->preferred_profile);
        pa_log_info("Storing profile %s for port %s on card %s.", p_info->profile, p->name, card->name);
    }
}

452
static pa_hook_result_t card_profile_changed_callback(pa_core *c, pa_card *card, struct userdata *u) {
453
    struct entry *entry;
454
455
456
    pa_sink *sink;
    pa_source *source;
    uint32_t state;
457
458
459

    pa_assert(card);

460
    if (!card->save_profile && !card->profile_is_sticky)
461
462
463
        return PA_HOOK_OK;

    if ((entry = entry_read(u, card->name))) {
464
        pa_xfree(entry->profile);
465
        entry->profile_is_sticky = card->profile_is_sticky;
466
467
468
469
470
        entry->profile = pa_xstrdup(card->active_profile->name);
        pa_log_info("Storing card profile for card %s.", card->name);
    } else {
        entry = entry_from_card(card);
        show_full_info(card);
471
    }
472

473
474
475
476
477
    PA_IDXSET_FOREACH(sink, card->sinks, state)
        update_profile_for_port(entry, card, sink->active_port);
    PA_IDXSET_FOREACH(source, card->sources, state)
        update_profile_for_port(entry, card, source->active_port);

478
479
    if (entry_write(u, card->name, entry))
        trigger_save(u);
480

481
482
483
484
    entry_free(entry);
    return PA_HOOK_OK;
}

485
486
487
488
489
static pa_hook_result_t card_profile_added_callback(pa_core *c, pa_card_profile *profile, struct userdata *u) {
    struct entry *entry;

    pa_assert(profile);

490
491
492
    if (profile->available == PA_AVAILABLE_NO)
        return PA_HOOK_OK;

493
494
495
496
    if (!(entry = entry_read(u, profile->card->name)))
        return PA_HOOK_OK;

    if (pa_safe_streq(entry->profile, profile->name)) {
497
        if (pa_card_set_profile(profile->card, profile, true) >= 0)
498
499
500
501
502
503
504
505
            pa_log_info("Restored profile '%s' for card %s.", profile->name, profile->card->name);
    }

    entry_free(entry);

    return PA_HOOK_OK;
}

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
static pa_hook_result_t port_offset_change_callback(pa_core *c, pa_device_port *port, struct userdata *u) {
    struct entry *entry;
    pa_card *card;

    pa_assert(port);
    card = port->card;

    if ((entry = entry_read(u, card->name))) {
        struct port_info *p_info;

        if ((p_info = pa_hashmap_get(entry->ports, port->name)))
            p_info->offset = port->latency_offset;
        else {
            p_info = port_info_new(port);
            pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
        }

        pa_log_info("Storing latency offset for port %s on card %s.", port->name, card->name);

    } else {
526
        entry = entry_from_card(card);
527
528
        show_full_info(card);
    }
529

530
531
    if (entry_write(u, card->name, entry))
        trigger_save(u);
532

533
    entry_free(entry);
534
    return PA_HOOK_OK;
535
536
537
538
}

static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
    struct entry *e;
539
540
541
    void *state;
    pa_device_port *p;
    struct port_info *p_info;
542
543
544

    pa_assert(new_data);

545
546
    if (!(e = entry_read(u, new_data->name)))
        return PA_HOOK_OK;
547

548
549
550
551
552
553
    /* Always restore the latency offsets because their
     * initial value is always 0 */

    pa_log_info("Restoring port latency offsets for card %s.", new_data->name);

    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
557
558
            if (!p->preferred_profile && p_info->profile)
                pa_device_port_set_preferred_profile(p, p_info->profile);
        }
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

    if (e->preferred_input_port) {
        p = pa_hashmap_get(new_data->ports, e->preferred_input_port);
        if (p)
            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_INPUT, p);
    }

    if (e->preferred_output_port) {
        p = pa_hashmap_get(new_data->ports, e->preferred_output_port);
        if (p)
            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_OUTPUT, p);
    }

    entry_free(e);

    return PA_HOOK_OK;
}

577
578
579
580
581
582
static pa_hook_result_t card_choose_initial_profile_callback(pa_core *core, pa_card *card, struct userdata *u) {
    struct entry *e;

    if (!(e = entry_read(u, card->name)))
        return PA_HOOK_OK;

583
584
585
    if (!u->restore_bluetooth_profile) {
        const char *s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS);
        if (pa_safe_streq(s, "bluetooth"))
586
            goto finish;
587
588
    }

589
590
591
592
593
594
    card->profile_is_sticky = e->profile_is_sticky;
    pa_log_info("Profile '%s' was previously %s for card %s.",
            e->profile,
            card->profile_is_sticky ? "sticky" : "automatically selected",
            card->name);

595
596
597
598
599
    if (e->profile[0]) {
        pa_card_profile *profile;

        profile = pa_hashmap_get(card->profiles, e->profile);
        if (profile) {
600
            if (profile->available != PA_AVAILABLE_NO || card->profile_is_sticky) {
601
602
603
604
605
                pa_log_info("Restoring profile '%s' for card %s.", profile->name, card->name);
                pa_card_set_profile(card, profile, true);
            } else
                pa_log_debug("Not restoring profile %s for card %s, because the profile is currently unavailable.",
                             profile->name, card->name);
606
607
608
609
610
611
        } else {
            pa_log_debug("Tried to restore profile %s for card %s, but the card doesn't have such profile.",
                         e->profile, card->name);
        }
    }

612
finish:
613
614
615
616
617
    entry_free(e);

    return PA_HOOK_OK;
}

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
static pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_card_preferred_port_changed_hook_data *data,
                                                             struct userdata *u) {
    struct entry *e;
    pa_card *card;

    card = data->card;

    e = entry_read(u, card->name);
    if (!e)
        e = entry_from_card(card);

    if (data->direction == PA_DIRECTION_INPUT) {
        pa_xfree(e->preferred_input_port);
        e->preferred_input_port = pa_xstrdup(card->preferred_input_port ? card->preferred_input_port->name : NULL);
    } else {
        pa_xfree(e->preferred_output_port);
        e->preferred_output_port = pa_xstrdup(card->preferred_output_port ? card->preferred_output_port->name : NULL);
    }

    if (entry_write(u, card->name, e))
        trigger_save(u);

640
641
    entry_free(e);

642
643
644
645
646
647
    return PA_HOOK_OK;
}

int pa__init(pa_module*m) {
    pa_modargs *ma = NULL;
    struct userdata *u;
648
    char *state_path;
649
    bool restore_bluetooth_profile;
650
651
652
653
654
655
656
657

    pa_assert(m);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments");
        goto fail;
    }

658
659
660
661
662
663
    restore_bluetooth_profile = false;
    if (pa_modargs_get_value_boolean(ma, "restore_bluetooth_profile", &restore_bluetooth_profile) < 0) {
        pa_log("Invalid boolean value for restore_bluetooth_profile parameter");
        goto fail;
    }

664
    m->userdata = u = pa_xnew0(struct userdata, 1);
665
666
    u->core = m->core;
    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
671
    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL,
                           (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
675
676
    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);
    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);
    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
680
        goto fail;

681
682
    if (!(u->database = pa_database_open(state_path, "card-database", true, true))) {
        pa_xfree(state_path);
683
684
685
        goto fail;
    }

686
    pa_xfree(state_path);
687
688
689
690
691
692
693
694
695
696

    pa_modargs_free(ma);
    return 0;

fail:
    pa__done(m);

    if (ma)
        pa_modargs_free(ma);

Maarten Bosmans's avatar
Maarten Bosmans committed
697
    return -1;
698
699
700
701
702
703
704
705
706
707
}

void pa__done(pa_module*m) {
    struct userdata* u;

    pa_assert(m);

    if (!(u = m->userdata))
        return;

708
    if (u->save_time_event) {
709
        u->core->mainloop->time_free(u->save_time_event);
710
711
        pa_database_sync(u->database);
    }
712

713
714
    if (u->database)
        pa_database_close(u->database);
715
716
717

    pa_xfree(u);
}