hashmap: Add pa_hashmap_remove_all()
[platform/upstream/pulseaudio.git] / src / modules / module-card-restore.c
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, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include <pulse/gccmacro.h>
34 #include <pulse/xmalloc.h>
35 #include <pulse/timeval.h>
36 #include <pulse/rtclock.h>
37
38 #include <pulsecore/core-error.h>
39 #include <pulsecore/module.h>
40 #include <pulsecore/core-util.h>
41 #include <pulsecore/modargs.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/core-subscribe.h>
44 #include <pulsecore/card.h>
45 #include <pulsecore/namereg.h>
46 #include <pulsecore/database.h>
47 #include <pulsecore/tagstruct.h>
48
49 #include "module-card-restore-symdef.h"
50
51 PA_MODULE_AUTHOR("Lennart Poettering");
52 PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
53 PA_MODULE_VERSION(PACKAGE_VERSION);
54 PA_MODULE_LOAD_ONCE(TRUE);
55
56 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
57
58 static const char* const valid_modargs[] = {
59     NULL
60 };
61
62 struct userdata {
63     pa_core *core;
64     pa_module *module;
65     pa_hook_slot *card_new_hook_slot;
66     pa_hook_slot *card_put_hook_slot;
67     pa_hook_slot *card_profile_hook_slot;
68     pa_hook_slot *port_new_hook_slot;
69     pa_hook_slot *port_offset_hook_slot;
70     pa_time_event *save_time_event;
71     pa_database *database;
72     bool hooks_connected;
73 };
74
75 #define ENTRY_VERSION 2
76
77 struct port_info {
78     char *name;
79     int64_t offset;
80 };
81
82 struct entry {
83     uint8_t version;
84     char *profile;
85     pa_hashmap *ports; /* Port name -> struct port_info */
86 };
87
88 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
89     struct userdata *u = userdata;
90
91     pa_assert(a);
92     pa_assert(e);
93     pa_assert(u);
94
95     pa_assert(e == u->save_time_event);
96     u->core->mainloop->time_free(u->save_time_event);
97     u->save_time_event = NULL;
98
99     pa_database_sync(u->database);
100     pa_log_info("Synced.");
101 }
102
103 static void trigger_save(struct userdata *u) {
104     if (u->save_time_event)
105         return;
106
107     u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
108 }
109
110 static struct entry* entry_new(void) {
111     struct entry *r = pa_xnew0(struct entry, 1);
112     r->version = ENTRY_VERSION;
113     r->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
114     return r;
115 }
116
117 static struct port_info *port_info_new(pa_device_port *port) {
118     struct port_info *p_info;
119
120     if (port) {
121         p_info = pa_xnew(struct port_info, 1);
122         p_info->name = pa_xstrdup(port->name);
123         p_info->offset = port->latency_offset;
124     } else
125         p_info = pa_xnew0(struct port_info, 1);
126
127     return p_info;
128 }
129
130 static void port_info_update(struct port_info *p_info, pa_device_port *port) {
131     pa_assert(p_info);
132     pa_assert(port);
133
134     pa_xfree(p_info->name);
135     p_info->name = pa_xstrdup(port->name);
136     p_info->offset = port->latency_offset;
137 }
138
139 static void port_info_free(struct port_info *p_info) {
140     pa_assert(p_info);
141
142     pa_xfree(p_info->name);
143     pa_xfree(p_info);
144 }
145
146 static void entry_free(struct entry* e) {
147     pa_assert(e);
148
149     pa_xfree(e->profile);
150     pa_hashmap_free(e->ports, (pa_free_cb_t) port_info_free);
151
152     pa_xfree(e);
153 }
154
155 static struct entry *entry_from_card(pa_card *card) {
156     struct port_info *p_info;
157     struct entry *entry;
158     pa_device_port *port;
159     void *state;
160
161     pa_assert(card);
162
163     entry = entry_new();
164     if (card->save_profile)
165         entry->profile = pa_xstrdup(card->active_profile->name);
166
167     PA_HASHMAP_FOREACH(port, card->ports, state) {
168         p_info = port_info_new(port);
169         pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
170     }
171
172     return entry;
173 }
174
175 static bool entrys_equal(struct entry *a, struct entry *b) {
176     struct port_info *Ap_info, *Bp_info;
177     void *state;
178
179     pa_assert(a);
180     pa_assert(b);
181
182     if (!pa_streq(a->profile, b->profile) ||
183             pa_hashmap_size(a->ports) != pa_hashmap_size(b->ports))
184         return false;
185
186     PA_HASHMAP_FOREACH(Ap_info, a->ports, state) {
187         if ((Bp_info = pa_hashmap_get(b->ports, Ap_info->name))) {
188             if (Ap_info->offset != Bp_info->offset)
189                 return false;
190         } else
191             return false;
192     }
193
194     return true;
195 }
196
197 static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
198     pa_tagstruct *t;
199     pa_datum key, data;
200     pa_bool_t r;
201     void *state;
202     struct port_info *p_info;
203
204     pa_assert(u);
205     pa_assert(name);
206     pa_assert(e);
207
208     t = pa_tagstruct_new(NULL, 0);
209     pa_tagstruct_putu8(t, e->version);
210     pa_tagstruct_puts(t, e->profile);
211     pa_tagstruct_putu32(t, pa_hashmap_size(e->ports));
212
213     PA_HASHMAP_FOREACH(p_info, e->ports, state) {
214         pa_tagstruct_puts(t, p_info->name);
215         pa_tagstruct_puts64(t, p_info->offset);
216     }
217
218     key.data = (char *) name;
219     key.size = strlen(name);
220
221     data.data = (void*)pa_tagstruct_data(t, &data.size);
222
223     r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
224
225     pa_tagstruct_free(t);
226
227     return r;
228 }
229
230 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
231
232 #define LEGACY_ENTRY_VERSION 1
233 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
234     struct legacy_entry {
235         uint8_t version;
236         char profile[PA_NAME_MAX];
237     } PA_GCC_PACKED ;
238     struct legacy_entry *le;
239     struct entry *e;
240
241     pa_assert(u);
242     pa_assert(data);
243
244     if (data->size != sizeof(struct legacy_entry)) {
245         pa_log_debug("Size does not match.");
246         return NULL;
247     }
248
249     le = (struct legacy_entry*)data->data;
250
251     if (le->version != LEGACY_ENTRY_VERSION) {
252         pa_log_debug("Version mismatch.");
253         return NULL;
254     }
255
256     if (!memchr(le->profile, 0, sizeof(le->profile))) {
257         pa_log_warn("Profile has missing NUL byte.");
258         return NULL;
259     }
260
261     e = entry_new();
262     e->profile = pa_xstrdup(le->profile);
263     return e;
264 }
265 #endif
266
267 static struct entry* entry_read(struct userdata *u, const char *name) {
268     pa_datum key, data;
269     struct entry *e = NULL;
270     pa_tagstruct *t = NULL;
271     const char* profile;
272
273     pa_assert(u);
274     pa_assert(name);
275
276     key.data = (char*) name;
277     key.size = strlen(name);
278
279     pa_zero(data);
280
281     if (!pa_database_get(u->database, &key, &data))
282         goto fail;
283
284     t = pa_tagstruct_new(data.data, data.size);
285     e = entry_new();
286
287     if (pa_tagstruct_getu8(t, &e->version) < 0 ||
288         e->version > ENTRY_VERSION ||
289         pa_tagstruct_gets(t, &profile) < 0) {
290
291         goto fail;
292     }
293
294     if (!profile)
295         profile = "";
296
297     e->profile = pa_xstrdup(profile);
298
299     if (e->version >= 2) {
300         uint32_t port_count = 0;
301         const char *port_name = NULL;
302         int64_t port_offset = 0;
303         struct port_info *p_info;
304         unsigned i;
305
306         if (pa_tagstruct_getu32(t, &port_count) < 0)
307             goto fail;
308
309         for (i = 0; i < port_count; i++) {
310             if (pa_tagstruct_gets(t, &port_name) < 0 ||
311                 !port_name ||
312                 pa_hashmap_get(e->ports, port_name) ||
313                 pa_tagstruct_gets64(t, &port_offset) < 0)
314                 goto fail;
315
316             p_info = port_info_new(NULL);
317             p_info->name = pa_xstrdup(port_name);
318             p_info->offset = port_offset;
319
320             pa_assert_se(pa_hashmap_put(e->ports, p_info->name, p_info) >= 0);
321         }
322     }
323
324     if (!pa_tagstruct_eof(t))
325         goto fail;
326
327     pa_tagstruct_free(t);
328     pa_datum_free(&data);
329
330     return e;
331
332 fail:
333
334     pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
335
336     if (e)
337         entry_free(e);
338     if (t)
339         pa_tagstruct_free(t);
340
341 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
342     pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
343     if ((e = legacy_entry_read(u, &data))) {
344         pa_log_debug("Success. Saving new format for key: %s", name);
345         if (entry_write(u, name, e))
346             trigger_save(u);
347         pa_datum_free(&data);
348         return e;
349     } else
350         pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
351 #endif
352
353     pa_datum_free(&data);
354     return NULL;
355 }
356
357 static void show_full_info(pa_card *card) {
358     pa_assert(card);
359
360     if (card->save_profile)
361         pa_log_info("Storing profile and port latency offsets for card %s.", card->name);
362     else
363         pa_log_info("Storing port latency offsets for card %s.", card->name);
364 }
365
366 static pa_hook_result_t card_put_hook_callback(pa_core *c, pa_card *card, struct userdata *u) {
367     struct entry *entry, *old;
368
369     pa_assert(card);
370
371     entry = entry_from_card(card);
372
373     if ((old = entry_read(u, card->name))) {
374         if (!card->save_profile)
375             entry->profile = pa_xstrdup(old->profile);
376         if (entrys_equal(entry, old))
377             goto finish;
378     }
379
380     show_full_info(card);
381
382     if (entry_write(u, card->name, entry))
383         trigger_save(u);
384
385 finish:
386     entry_free(entry);
387     if (old)
388         entry_free(old);
389
390     return PA_HOOK_OK;
391 }
392
393 static pa_hook_result_t card_profile_change_callback(pa_core *c, pa_card *card, struct userdata *u) {
394     struct entry *entry;
395
396     pa_assert(card);
397
398     if (!card->save_profile)
399         return PA_HOOK_OK;
400
401     if ((entry = entry_read(u, card->name))) {
402         entry->profile = pa_xstrdup(card->active_profile->name);
403         pa_log_info("Storing card profile for card %s.", card->name);
404     } else {
405         entry = entry_from_card(card);
406         show_full_info(card);
407     }
408
409     if (entry_write(u, card->name, entry))
410         trigger_save(u);
411
412     entry_free(entry);
413     return PA_HOOK_OK;
414 }
415
416 static pa_hook_result_t card_port_add_callback(pa_core *c, pa_device_port *port, struct userdata *u) {
417     struct entry *entry;
418     pa_card *card;
419
420     pa_assert(port);
421     card = port->card;
422
423     if ((entry = entry_read(u, card->name))) {
424         struct port_info *p_info;
425
426         if ((p_info = pa_hashmap_get(entry->ports, port->name)))
427             port_info_update(p_info, port);
428         else {
429             p_info = port_info_new(port);
430             pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
431         }
432
433         pa_log_info("Storing port info for port %s on card %s.", port->name, card->name);
434
435     } else {
436         entry = entry_from_card(card);
437         show_full_info(card);
438     }
439
440     if (entry_write(u, card->name, entry))
441         trigger_save(u);
442
443     entry_free(entry);
444     return PA_HOOK_OK;
445 }
446
447 static pa_hook_result_t port_offset_change_callback(pa_core *c, pa_device_port *port, struct userdata *u) {
448     struct entry *entry;
449     pa_card *card;
450
451     pa_assert(port);
452     card = port->card;
453
454     if ((entry = entry_read(u, card->name))) {
455         struct port_info *p_info;
456
457         if ((p_info = pa_hashmap_get(entry->ports, port->name)))
458             p_info->offset = port->latency_offset;
459         else {
460             p_info = port_info_new(port);
461             pa_assert_se(pa_hashmap_put(entry->ports, p_info->name, p_info) >= 0);
462         }
463
464         pa_log_info("Storing latency offset for port %s on card %s.", port->name, card->name);
465
466     } else {
467         entry_from_card(card);
468         show_full_info(card);
469     }
470
471     if (entry_write(u, card->name, entry))
472         trigger_save(u);
473
474     entry_free(entry);
475     return PA_HOOK_OK;
476 }
477
478 static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
479     struct entry *e;
480     void *state;
481     pa_device_port *p;
482     struct port_info *p_info;
483
484     pa_assert(new_data);
485
486     if (!(e = entry_read(u, new_data->name)))
487         return PA_HOOK_OK;
488
489     if (e->profile[0]) {
490         if (!new_data->active_profile) {
491             pa_card_new_data_set_profile(new_data, e->profile);
492             pa_log_info("Restored profile '%s' for card %s.", new_data->active_profile, new_data->name);
493             new_data->save_profile = TRUE;
494
495         } else
496             pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
497     }
498
499     /* Always restore the latency offsets because their
500      * initial value is always 0 */
501
502     pa_log_info("Restoring port latency offsets for card %s.", new_data->name);
503
504     PA_HASHMAP_FOREACH(p_info, e->ports, state)
505         if ((p = pa_hashmap_get(new_data->ports, p_info->name)))
506             p->latency_offset = p_info->offset;
507
508     entry_free(e);
509
510     return PA_HOOK_OK;
511 }
512
513 int pa__init(pa_module*m) {
514     pa_modargs *ma = NULL;
515     struct userdata *u;
516     char *fname;
517
518     pa_assert(m);
519
520     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
521         pa_log("Failed to parse module arguments");
522         goto fail;
523     }
524
525     m->userdata = u = pa_xnew0(struct userdata, 1);
526     u->core = m->core;
527     u->module = m;
528
529     u->card_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
530     u->card_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
531     u->card_profile_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_change_callback, u);
532     u->port_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_PORT_ADDED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_port_add_callback, u);
533     u->port_offset_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) port_offset_change_callback, u);
534     u->hooks_connected = true;
535
536     if (!(fname = pa_state_path("card-database", TRUE)))
537         goto fail;
538
539     if (!(u->database = pa_database_open(fname, TRUE))) {
540         pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
541         pa_xfree(fname);
542         goto fail;
543     }
544
545     pa_log_info("Successfully opened database file '%s'.", fname);
546     pa_xfree(fname);
547
548     pa_modargs_free(ma);
549     return 0;
550
551 fail:
552     pa__done(m);
553
554     if (ma)
555         pa_modargs_free(ma);
556
557     return -1;
558 }
559
560 void pa__done(pa_module*m) {
561     struct userdata* u;
562
563     pa_assert(m);
564
565     if (!(u = m->userdata))
566         return;
567
568     if (u->hooks_connected) {
569         pa_hook_slot_free(u->card_new_hook_slot);
570         pa_hook_slot_free(u->card_put_hook_slot);
571         pa_hook_slot_free(u->card_profile_hook_slot);
572         pa_hook_slot_free(u->port_new_hook_slot);
573         pa_hook_slot_free(u->port_offset_hook_slot);
574     }
575
576     if (u->save_time_event)
577         u->core->mainloop->time_free(u->save_time_event);
578
579     if (u->database)
580         pa_database_close(u->database);
581
582     pa_xfree(u);
583 }