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>
50 #include "module-card-restore-symdef.h"
52 PA_MODULE_AUTHOR("Lennart Poettering");
53 PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
54 PA_MODULE_VERSION(PACKAGE_VERSION
);
55 PA_MODULE_LOAD_ONCE(TRUE
);
57 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
59 static const char* const valid_modargs
[] = {
66 pa_subscription
*subscription
;
67 pa_hook_slot
*card_new_hook_slot
;
68 pa_time_event
*save_time_event
;
69 pa_database
*database
;
72 #define ENTRY_VERSION 1
76 char profile
[PA_NAME_MAX
];
79 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
80 struct userdata
*u
= userdata
;
86 pa_assert(e
== u
->save_time_event
);
87 u
->core
->mainloop
->time_free(u
->save_time_event
);
88 u
->save_time_event
= NULL
;
90 pa_database_sync(u
->database
);
91 pa_log_info("Synced.");
94 static struct entry
* read_entry(struct userdata
*u
, const char *name
) {
101 key
.data
= (char*) name
;
102 key
.size
= strlen(name
);
106 if (!pa_database_get(u
->database
, &key
, &data
))
109 if (data
.size
!= sizeof(struct entry
)) {
110 pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name
, (unsigned long) data
.size
, (unsigned long) sizeof(struct entry
));
114 e
= (struct entry
*) data
.data
;
116 if (e
->version
!= ENTRY_VERSION
) {
117 pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name
);
121 if (!memchr(e
->profile
, 0, sizeof(e
->profile
))) {
122 pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name
);
130 pa_datum_free(&data
);
134 static void trigger_save(struct userdata
*u
) {
135 if (u
->save_time_event
)
138 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
141 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
142 struct userdata
*u
= userdata
;
143 struct entry entry
, *old
;
150 if (t
!= (PA_SUBSCRIPTION_EVENT_CARD
|PA_SUBSCRIPTION_EVENT_NEW
) &&
151 t
!= (PA_SUBSCRIPTION_EVENT_CARD
|PA_SUBSCRIPTION_EVENT_CHANGE
))
155 entry
.version
= ENTRY_VERSION
;
157 if (!(card
= pa_idxset_get_by_index(c
->cards
, idx
)))
160 if (!card
->save_profile
)
163 pa_strlcpy(entry
.profile
, card
->active_profile
? card
->active_profile
->name
: "", sizeof(entry
.profile
));
165 if ((old
= read_entry(u
, card
->name
))) {
167 if (strncmp(old
->profile
, entry
.profile
, sizeof(entry
.profile
)) == 0) {
175 key
.data
= card
->name
;
176 key
.size
= strlen(card
->name
);
179 data
.size
= sizeof(entry
);
181 pa_log_info("Storing profile for card %s.", card
->name
);
183 pa_database_set(u
->database
, &key
, &data
, TRUE
);
188 static pa_hook_result_t
card_new_hook_callback(pa_core
*c
, pa_card_new_data
*new_data
, struct userdata
*u
) {
193 if ((e
= read_entry(u
, new_data
->name
)) && e
->profile
[0]) {
195 if (!new_data
->active_profile
) {
196 pa_log_info("Restoring profile for card %s.", new_data
->name
);
197 pa_card_new_data_set_profile(new_data
, e
->profile
);
198 new_data
->save_profile
= TRUE
;
200 pa_log_debug("Not restoring profile for card %s, because already set.", new_data
->name
);
208 int pa__init(pa_module
*m
) {
209 pa_modargs
*ma
= NULL
;
217 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
218 pa_log("Failed to parse module arguments");
222 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
226 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_CARD
, subscribe_callback
, u
);
228 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
);
230 if (!(fname
= pa_state_path("card-database", TRUE
)))
233 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
234 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
239 pa_log_info("Sucessfully opened database file '%s'.", fname
);
242 for (card
= pa_idxset_first(m
->core
->cards
, &idx
); card
; card
= pa_idxset_next(m
->core
->cards
, &idx
))
243 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_CARD
|PA_SUBSCRIPTION_EVENT_NEW
, card
->index
, u
);
257 void pa__done(pa_module
*m
) {
262 if (!(u
= m
->userdata
))
266 pa_subscription_free(u
->subscription
);
268 if (u
->card_new_hook_slot
)
269 pa_hook_slot_free(u
->card_new_hook_slot
);
271 if (u
->save_time_event
)
272 u
->core
->mainloop
->time_free(u
->save_time_event
);
275 pa_database_close(u
->database
);