2009-01-21 02:49:42 +01:00
/***
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
2009-03-03 20:23:02 +00:00
by the Free Software Foundation ; either version 2.1 of the License ,
2009-01-21 02:49:42 +01:00
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
along with PulseAudio ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
# 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>
# include <ctype.h>
# include <pulse/xmalloc.h>
# include <pulse/volume.h>
# include <pulse/timeval.h>
# include <pulse/util.h>
# 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>
2009-05-14 01:24:26 +02:00
# include <pulsecore/database.h>
2009-01-21 02:49:42 +01:00
# include "module-card-restore-symdef.h"
PA_MODULE_AUTHOR ( " Lennart Poettering " ) ;
PA_MODULE_DESCRIPTION ( " Automatically restore profile of cards " ) ;
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( TRUE ) ;
# define SAVE_INTERVAL 10
static const char * const valid_modargs [ ] = {
NULL
} ;
struct userdata {
pa_core * core ;
pa_module * module ;
pa_subscription * subscription ;
pa_hook_slot * card_new_hook_slot ;
pa_time_event * save_time_event ;
2009-05-14 01:24:26 +02:00
pa_database * database ;
2009-01-21 02:49:42 +01:00
} ;
2009-02-04 18:31:24 +01:00
# define ENTRY_VERSION 1
2009-02-13 18:02:47 +01:00
struct entry {
2009-02-04 18:31:24 +01:00
uint8_t version ;
2009-01-21 02:49:42 +01:00
char profile [ PA_NAME_MAX ] ;
2009-02-13 18:02:47 +01:00
} PA_GCC_PACKED ;
2009-01-21 02:49:42 +01:00
static void save_time_callback ( pa_mainloop_api * a , pa_time_event * e , const struct timeval * tv , void * userdata ) {
struct userdata * u = userdata ;
pa_assert ( a ) ;
pa_assert ( e ) ;
pa_assert ( tv ) ;
pa_assert ( u ) ;
pa_assert ( e = = u - > save_time_event ) ;
u - > core - > mainloop - > time_free ( u - > save_time_event ) ;
u - > save_time_event = NULL ;
2009-05-14 01:24:26 +02:00
pa_database_sync ( u - > database ) ;
2009-01-21 02:49:42 +01:00
pa_log_info ( " Synced. " ) ;
}
static struct entry * read_entry ( struct userdata * u , const char * name ) {
2009-05-14 01:24:26 +02:00
pa_datum key , data ;
2009-01-21 02:49:42 +01:00
struct entry * e ;
pa_assert ( u ) ;
pa_assert ( name ) ;
2009-05-14 01:24:26 +02:00
key . data = ( char * ) name ;
key . size = strlen ( name ) ;
2009-01-21 02:49:42 +01:00
2009-05-14 01:24:26 +02:00
pa_zero ( data ) ;
2009-01-21 02:49:42 +01:00
2009-05-14 01:24:26 +02:00
if ( ! pa_database_get ( u - > database , & key , & data ) )
2009-01-21 02:49:42 +01:00
goto fail ;
2009-05-14 01:24:26 +02:00
if ( data . size ! = sizeof ( struct entry ) ) {
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 ) ) ;
2009-01-21 02:49:42 +01:00
goto fail ;
}
2009-05-14 01:24:26 +02:00
e = ( struct entry * ) data . data ;
2009-01-21 02:49:42 +01:00
2009-02-04 18:31:24 +01:00
if ( e - > version ! = ENTRY_VERSION ) {
pa_log_debug ( " Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring. " , name ) ;
goto fail ;
}
2009-01-21 02:49:42 +01:00
if ( ! memchr ( e - > profile , 0 , sizeof ( e - > profile ) ) ) {
pa_log_warn ( " Database contains entry for card %s with missing NUL byte in profile name " , name ) ;
goto fail ;
}
return e ;
fail :
2009-05-14 01:24:26 +02:00
pa_datum_free ( & data ) ;
2009-01-21 02:49:42 +01:00
return NULL ;
}
static void trigger_save ( struct userdata * u ) {
struct timeval tv ;
if ( u - > save_time_event )
return ;
pa_gettimeofday ( & tv ) ;
tv . tv_sec + = SAVE_INTERVAL ;
u - > save_time_event = u - > core - > mainloop - > time_new ( u - > core - > mainloop , & tv , save_time_callback , u ) ;
}
static void subscribe_callback ( pa_core * c , pa_subscription_event_type_t t , uint32_t idx , void * userdata ) {
struct userdata * u = userdata ;
struct entry entry , * old ;
2009-05-14 01:24:26 +02:00
pa_datum key , data ;
2009-01-21 02:49:42 +01:00
pa_card * card ;
pa_assert ( c ) ;
pa_assert ( u ) ;
if ( t ! = ( PA_SUBSCRIPTION_EVENT_CARD | PA_SUBSCRIPTION_EVENT_NEW ) & &
t ! = ( PA_SUBSCRIPTION_EVENT_CARD | PA_SUBSCRIPTION_EVENT_CHANGE ) )
return ;
2009-05-14 01:24:26 +02:00
pa_zero ( entry ) ;
2009-02-04 18:31:24 +01:00
entry . version = ENTRY_VERSION ;
2009-01-21 02:49:42 +01:00
if ( ! ( card = pa_idxset_get_by_index ( c - > cards , idx ) ) )
return ;
2009-03-23 19:31:36 +01:00
if ( ! card - > save_profile )
return ;
2009-01-21 02:49:42 +01:00
pa_strlcpy ( entry . profile , card - > active_profile ? card - > active_profile - > name : " " , sizeof ( entry . profile ) ) ;
if ( ( old = read_entry ( u , card - > name ) ) ) {
if ( strncmp ( old - > profile , entry . profile , sizeof ( entry . profile ) ) = = 0 ) {
pa_xfree ( old ) ;
return ;
}
pa_xfree ( old ) ;
}
2009-05-14 01:24:26 +02:00
key . data = card - > name ;
key . size = strlen ( card - > name ) ;
2009-01-21 02:49:42 +01:00
2009-05-14 01:24:26 +02:00
data . data = & entry ;
data . size = sizeof ( entry ) ;
2009-01-21 02:49:42 +01:00
pa_log_info ( " Storing profile for card %s. " , card - > name ) ;
2009-05-14 01:24:26 +02:00
pa_database_set ( u - > database , & key , & data , TRUE ) ;
2009-01-21 02:49:42 +01:00
trigger_save ( u ) ;
}
static pa_hook_result_t card_new_hook_callback ( pa_core * c , pa_card_new_data * new_data , struct userdata * u ) {
struct entry * e ;
pa_assert ( new_data ) ;
2009-02-19 04:49:58 +01:00
if ( ( e = read_entry ( u , new_data - > name ) ) & & e - > profile [ 0 ] ) {
2009-01-21 02:49:42 +01:00
if ( ! new_data - > active_profile ) {
pa_log_info ( " Restoring profile for card %s. " , new_data - > name ) ;
2009-06-18 00:58:19 +02:00
pa_card_new_data_set_profile ( new_data , e - > profile ) ;
new_data - > save_profile = FALSE ;
2009-01-21 02:49:42 +01:00
} else
pa_log_debug ( " Not restoring profile for card %s, because already set. " , new_data - > name ) ;
pa_xfree ( e ) ;
}
return PA_HOOK_OK ;
}
int pa__init ( pa_module * m ) {
pa_modargs * ma = NULL ;
struct userdata * u ;
2009-05-14 01:24:26 +02:00
char * fname ;
2009-01-21 02:49:42 +01:00
pa_card * card ;
uint32_t idx ;
pa_assert ( m ) ;
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
pa_log ( " Failed to parse module arguments " ) ;
goto fail ;
}
2009-06-18 00:59:33 +02:00
m - > userdata = u = pa_xnew0 ( struct userdata , 1 ) ;
2009-01-21 02:49:42 +01:00
u - > core = m - > core ;
u - > module = m ;
u - > subscription = pa_subscription_new ( m - > core , PA_SUBSCRIPTION_MASK_CARD , subscribe_callback , u ) ;
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 ) ;
2009-05-14 01:24:26 +02:00
if ( ! ( fname = pa_state_path ( " card-database " , TRUE ) ) )
2009-01-21 02:49:42 +01:00
goto fail ;
2009-05-14 01:24:26 +02:00
if ( ! ( u - > database = pa_database_open ( fname , TRUE ) ) ) {
pa_log ( " Failed to open volume database '%s': %s " , fname , pa_cstrerror ( errno ) ) ;
2009-01-21 02:49:42 +01:00
pa_xfree ( fname ) ;
goto fail ;
}
pa_log_info ( " Sucessfully opened database file '%s'. " , fname ) ;
pa_xfree ( fname ) ;
for ( card = pa_idxset_first ( m - > core - > cards , & idx ) ; card ; card = pa_idxset_next ( m - > core - > cards , & idx ) )
subscribe_callback ( m - > core , PA_SUBSCRIPTION_EVENT_CARD | PA_SUBSCRIPTION_EVENT_NEW , card - > index , u ) ;
pa_modargs_free ( ma ) ;
return 0 ;
fail :
pa__done ( m ) ;
if ( ma )
pa_modargs_free ( ma ) ;
return - 1 ;
}
void pa__done ( pa_module * m ) {
struct userdata * u ;
pa_assert ( m ) ;
if ( ! ( u = m - > userdata ) )
return ;
if ( u - > subscription )
pa_subscription_free ( u - > subscription ) ;
if ( u - > card_new_hook_slot )
pa_hook_slot_free ( u - > card_new_hook_slot ) ;
if ( u - > save_time_event )
u - > core - > mainloop - > time_free ( u - > save_time_event ) ;
2009-05-14 01:24:26 +02:00
if ( u - > database )
pa_database_close ( u - > database ) ;
2009-01-21 02:49:42 +01:00
pa_xfree ( u ) ;
}