• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
47 PA_MODULE_AUTHOR("Lennart Poettering");
48 PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
49 PA_MODULE_VERSION(PACKAGE_VERSION);
50 PA_MODULE_LOAD_ONCE(true);
51 PA_MODULE_USAGE(
52     "restore_bluetooth_profile=<boolean>"
53 );
54 
55 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
56 
57 static const char* const valid_modargs[] = {
58     "restore_bluetooth_profile",
59     NULL
60 };
61 
62 struct 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 
72 struct port_info {
73     char *name;
74     int64_t offset;
75     char *profile;
76 };
77 
78 struct 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 
save_time_callback(pa_mainloop_api * a,pa_time_event * e,const struct timeval * t,void * userdata)86 static 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 
trigger_save(struct userdata * u)101 static 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 
port_info_free(struct port_info * p_info)108 static 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 
entry_new(void)116 static 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 
port_info_new(pa_device_port * port)122 static 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 
entry_free(struct entry * e)137 static 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 
entry_from_card(pa_card * card)148 static 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 
entrys_equal(struct entry * a,struct entry * b)174 static 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 
entry_write(struct userdata * u,const char * name,const struct entry * e)205 static 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
legacy_entry_read(struct userdata * u,pa_datum * data)247 static 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 
entry_read(struct userdata * u,const char * name)281 static 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 
373 fail:
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 
show_full_info(pa_card * card)398 static 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 
card_put_hook_callback(pa_core * c,pa_card * card,struct userdata * u)407 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     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 
426 finish:
427     entry_free(entry);
428     if (old)
429         entry_free(old);
430 
431     return PA_HOOK_OK;
432 }
433 
update_profile_for_port(struct entry * entry,pa_card * card,pa_device_port * p)434 static 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 
card_profile_changed_callback(pa_core * c,pa_card * card,struct userdata * u)452 static 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 
card_profile_added_callback(pa_core * c,pa_card_profile * profile,struct userdata * u)485 static 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 
port_offset_change_callback(pa_core * c,pa_device_port * port,struct userdata * u)506 static 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 
card_new_hook_callback(pa_core * c,pa_card_new_data * new_data,struct userdata * u)537 static 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 
card_choose_initial_profile_callback(pa_core * core,pa_card * card,struct userdata * u)577 static 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 
612 finish:
613     entry_free(e);
614 
615     return PA_HOOK_OK;
616 }
617 
card_preferred_port_changed_callback(pa_core * core,pa_card_preferred_port_changed_hook_data * data,struct userdata * u)618 static 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 
pa__init(pa_module * m)645 int 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 
691 fail:
692     pa__done(m);
693 
694     if (ma)
695         pa_modargs_free(ma);
696 
697     return -1;
698 }
699 
pa__done(pa_module * m)700 void 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