2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
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.
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.
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
29 #include <sys/types.h>
34 #include <pulse/xmalloc.h>
35 #include <pulse/volume.h>
36 #include <pulse/timeval.h>
37 #include <pulse/util.h>
38 #include <pulse/rtclock.h>
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/module.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/modargs.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/core-subscribe.h>
46 #include <pulsecore/card.h>
47 #include <pulsecore/namereg.h>
48 #include <pulsecore/database.h>
49 #include <pulsecore/tagstruct.h>
51 #include "module-card-restore-symdef.h"
53 PA_MODULE_AUTHOR("Lennart Poettering");
54 PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
55 PA_MODULE_VERSION(PACKAGE_VERSION
);
56 PA_MODULE_LOAD_ONCE(TRUE
);
58 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
60 static const char* const valid_modargs
[] = {
67 pa_subscription
*subscription
;
68 pa_hook_slot
*card_new_hook_slot
;
69 pa_time_event
*save_time_event
;
70 pa_database
*database
;
73 #define ENTRY_VERSION 2
80 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
81 struct userdata
*u
= userdata
;
87 pa_assert(e
== u
->save_time_event
);
88 u
->core
->mainloop
->time_free(u
->save_time_event
);
89 u
->save_time_event
= NULL
;
91 pa_database_sync(u
->database
);
92 pa_log_info("Synced.");
95 static struct entry
* entry_new(void) {
96 struct entry
*r
= pa_xnew0(struct entry
, 1);
97 r
->version
= ENTRY_VERSION
;
101 static void entry_free(struct entry
* e
) {
104 pa_xfree(e
->profile
);
108 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
110 struct entry
*e
= NULL
;
111 pa_tagstruct
*t
= NULL
;
117 key
.data
= (char*) name
;
118 key
.size
= strlen(name
);
122 if (!pa_database_get(u
->database
, &key
, &data
))
125 t
= pa_tagstruct_new(data
.data
, data
.size
);
128 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
129 e
->version
> ENTRY_VERSION
||
130 pa_tagstruct_gets(t
, &profile
) < 0) {
135 e
->profile
= pa_xstrdup(profile
);
137 if (!pa_tagstruct_eof(t
))
140 pa_tagstruct_free(t
);
141 pa_datum_free(&data
);
147 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
152 pa_tagstruct_free(t
);
153 pa_datum_free(&data
);
157 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
166 t
= pa_tagstruct_new(NULL
, 0);
167 pa_tagstruct_putu8(t
, e
->version
);
168 pa_tagstruct_puts(t
, e
->profile
);
170 key
.data
= (char *) name
;
171 key
.size
= strlen(name
);
173 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
175 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
177 pa_tagstruct_free(t
);
182 static void trigger_save(struct userdata
*u
) {
183 if (u
->save_time_event
)
186 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
189 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
190 struct userdata
*u
= userdata
;
191 struct entry
*entry
, *old
;
197 if (t
!= (PA_SUBSCRIPTION_EVENT_CARD
|PA_SUBSCRIPTION_EVENT_NEW
) &&
198 t
!= (PA_SUBSCRIPTION_EVENT_CARD
|PA_SUBSCRIPTION_EVENT_CHANGE
))
203 if (!(card
= pa_idxset_get_by_index(c
->cards
, idx
)))
206 if (!card
->save_profile
)
209 entry
->profile
= pa_xstrdup(card
->active_profile
? card
->active_profile
->name
: "");
211 if ((old
= entry_read(u
, card
->name
))) {
213 if (pa_streq(old
->profile
, entry
->profile
)) {
222 pa_log_info("Storing profile for card %s.", card
->name
);
224 if (entry_write(u
, card
->name
, entry
))
230 static pa_hook_result_t
card_new_hook_callback(pa_core
*c
, pa_card_new_data
*new_data
, struct userdata
*u
) {
235 if ((e
= entry_read(u
, new_data
->name
)) && e
->profile
[0]) {
237 if (!new_data
->active_profile
) {
238 pa_log_info("Restoring profile for card %s.", new_data
->name
);
239 pa_card_new_data_set_profile(new_data
, e
->profile
);
240 new_data
->save_profile
= TRUE
;
242 pa_log_debug("Not restoring profile for card %s, because already set.", new_data
->name
);
250 int pa__init(pa_module
*m
) {
251 pa_modargs
*ma
= NULL
;
259 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
260 pa_log("Failed to parse module arguments");
264 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
268 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_CARD
, subscribe_callback
, u
);
270 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
);
272 if (!(fname
= pa_state_path("card-database", TRUE
)))
275 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
276 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
281 pa_log_info("Successfully opened database file '%s'.", fname
);
284 for (card
= pa_idxset_first(m
->core
->cards
, &idx
); card
; card
= pa_idxset_next(m
->core
->cards
, &idx
))
285 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_CARD
|PA_SUBSCRIPTION_EVENT_NEW
, card
->index
, u
);
299 void pa__done(pa_module
*m
) {
304 if (!(u
= m
->userdata
))
308 pa_subscription_free(u
->subscription
);
310 if (u
->card_new_hook_slot
)
311 pa_hook_slot_free(u
->card_new_hook_slot
);
313 if (u
->save_time_event
)
314 u
->core
->mainloop
->time_free(u
->save_time_event
);
317 pa_database_close(u
->database
);