2005-04-17 02:20:36 +04:00
/*
2011-09-22 00:47:55 +04:00
* Functions for saving / restoring console .
2005-04-17 02:20:36 +04:00
*
* Originally from swsusp .
*/
2013-02-04 17:37:20 +04:00
# include <linux/console.h>
2005-04-17 02:20:36 +04:00
# include <linux/vt_kern.h>
# include <linux/kbd_kern.h>
2009-12-15 05:00:43 +03:00
# include <linux/vt.h>
2008-04-28 13:15:03 +04:00
# include <linux/module.h>
2014-01-29 03:10:37 +04:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include "power.h"
2006-02-07 23:58:50 +03:00
# define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
2005-04-17 02:20:36 +04:00
static int orig_fgconsole , orig_kmsg ;
2013-02-04 17:37:20 +04:00
static DEFINE_MUTEX ( vt_switch_mutex ) ;
struct pm_vt_switch {
struct list_head head ;
struct device * dev ;
bool required ;
} ;
static LIST_HEAD ( pm_vt_switch_list ) ;
/**
* pm_vt_switch_required - indicate VT switch at suspend requirements
* @ dev : device
* @ required : if true , caller needs VT switch at suspend / resume time
*
* The different console drivers may or may not require VT switches across
* suspend / resume , depending on how they handle restoring video state and
* what may be running .
*
* Drivers can indicate support for switchless suspend / resume , which can
* save time and flicker , by using this routine and passing ' false ' as
* the argument . If any loaded driver needs VT switching , or the
* no_console_suspend argument has been passed on the command line , VT
* switches will occur .
*/
void pm_vt_switch_required ( struct device * dev , bool required )
{
struct pm_vt_switch * entry , * tmp ;
mutex_lock ( & vt_switch_mutex ) ;
list_for_each_entry ( tmp , & pm_vt_switch_list , head ) {
if ( tmp - > dev = = dev ) {
/* already registered, update requirement */
tmp - > required = required ;
goto out ;
}
}
entry = kmalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! entry )
goto out ;
entry - > required = required ;
entry - > dev = dev ;
list_add ( & entry - > head , & pm_vt_switch_list ) ;
out :
mutex_unlock ( & vt_switch_mutex ) ;
}
EXPORT_SYMBOL ( pm_vt_switch_required ) ;
/**
* pm_vt_switch_unregister - stop tracking a device ' s VT switching needs
* @ dev : device
*
* Remove @ dev from the vt switch list .
*/
void pm_vt_switch_unregister ( struct device * dev )
{
struct pm_vt_switch * tmp ;
mutex_lock ( & vt_switch_mutex ) ;
list_for_each_entry ( tmp , & pm_vt_switch_list , head ) {
if ( tmp - > dev = = dev ) {
list_del ( & tmp - > head ) ;
2013-12-19 15:00:47 +04:00
kfree ( tmp ) ;
2013-02-04 17:37:20 +04:00
break ;
}
}
mutex_unlock ( & vt_switch_mutex ) ;
}
EXPORT_SYMBOL ( pm_vt_switch_unregister ) ;
/*
* There are three cases when a VT switch on suspend / resume are required :
* 1 ) no driver has indicated a requirement one way or another , so preserve
* the old behavior
* 2 ) console suspend is disabled , we want to see debug messages across
* suspend / resume
* 3 ) any registered driver indicates it needs a VT switch
*
* If none of these conditions is present , meaning we have at least one driver
* that doesn ' t need the switch , and none that do , we can avoid it to make
* resume look a little prettier ( and suspend too , but that ' s usually hidden ,
* e . g . when closing the lid on a laptop ) .
*/
static bool pm_vt_switch ( void )
{
struct pm_vt_switch * entry ;
bool ret = true ;
mutex_lock ( & vt_switch_mutex ) ;
if ( list_empty ( & pm_vt_switch_list ) )
goto out ;
if ( ! console_suspend_enabled )
goto out ;
list_for_each_entry ( entry , & pm_vt_switch_list , head ) {
if ( entry - > required )
goto out ;
}
ret = false ;
out :
mutex_unlock ( & vt_switch_mutex ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
int pm_prepare_console ( void )
{
2013-02-04 17:37:20 +04:00
if ( ! pm_vt_switch ( ) )
return 0 ;
2009-09-20 00:13:25 +04:00
orig_fgconsole = vt_move_to_console ( SUSPEND_CONSOLE , 1 ) ;
if ( orig_fgconsole < 0 )
2005-04-17 02:20:36 +04:00
return 1 ;
2009-12-15 05:00:43 +03:00
orig_kmsg = vt_kmsg_redirect ( SUSPEND_CONSOLE ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
void pm_restore_console ( void )
{
2013-02-04 17:37:20 +04:00
if ( ! pm_vt_switch ( ) )
return ;
2009-09-20 00:13:25 +04:00
if ( orig_fgconsole > = 0 ) {
vt_move_to_console ( orig_fgconsole , 0 ) ;
2009-12-15 05:00:43 +03:00
vt_kmsg_redirect ( orig_kmsg ) ;
2009-02-14 04:06:17 +03:00
}
2005-04-17 02:20:36 +04:00
}