@ -554,7 +554,7 @@ EXPORT_SYMBOL(handle_sysrq);
# ifdef CONFIG_INPUT
/* Simple translation table for the SysRq keys */
static const unsigned char sysrq_xlate [ KEY_MAX + 1 ] =
static const unsigned char sysrq_xlate [ KEY_CNT ] =
" \000 \033 1234567890-= \177 \t " /* 0x00 - 0x0f */
" qwertyuiop[] \r \000 as " /* 0x10 - 0x1f */
" dfghjkl;'` \000 \\ zxcv " /* 0x20 - 0x2f */
@ -563,53 +563,129 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] =
" 230 \177 \000 \000 \213 \214 \000 \000 \000 \000 \000 \000 \000 \000 \000 \000 " /* 0x50 - 0x5f */
" \r \000 / " ; /* 0x60 - 0x6f */
static bool sysrq_down ;
static int sysrq_alt_use ;
static int sysrq_alt ;
static DEFINE_SPINLOCK ( sysrq_event_lock ) ;
struct sysrq_state {
struct input_handle handle ;
struct work_struct reinject_work ;
unsigned long key_down [ BITS_TO_LONGS ( KEY_CNT ) ] ;
unsigned int alt ;
unsigned int alt_use ;
bool active ;
bool need_reinject ;
} ;
static void sysrq_reinject_alt_sysrq ( struct work_struct * work )
{
struct sysrq_state * sysrq =
container_of ( work , struct sysrq_state , reinject_work ) ;
struct input_handle * handle = & sysrq - > handle ;
unsigned int alt_code = sysrq - > alt_use ;
if ( sysrq - > need_reinject ) {
/* Simulate press and release of Alt + SysRq */
input_inject_event ( handle , EV_KEY , alt_code , 1 ) ;
input_inject_event ( handle , EV_KEY , KEY_SYSRQ , 1 ) ;
input_inject_event ( handle , EV_SYN , SYN_REPORT , 1 ) ;
input_inject_event ( handle , EV_KEY , KEY_SYSRQ , 0 ) ;
input_inject_event ( handle , EV_KEY , alt_code , 0 ) ;
input_inject_event ( handle , EV_SYN , SYN_REPORT , 1 ) ;
}
}
static bool sysrq_filter ( struct input_handle * handle , unsigned int type ,
unsigned int code , int value )
static bool sysrq_filter ( struct input_handle * handle ,
unsigned int type , unsigned int code , int value )
{
struct sysrq_state * sysrq = handle - > private ;
bool was_active = sysrq - > active ;
bool suppress ;
/* We are called with interrupts disabled, just take the lock */
spin_lock ( & sysrq_event_lock ) ;
switch ( type ) {
if ( type ! = EV_KEY )
goto out ;
case EV_SYN :
suppress = false ;
break ;
switch ( code ) {
case EV_KEY :
switch ( code ) {
case KEY_LEFTALT :
case KEY_RIGHTALT :
if ( value )
sysrq_alt = code ;
else {
if ( sysrq_down & & code = = sysrq_alt_use )
sysrq_down = false ;
case KEY_LEFTALT :
case KEY_RIGHTALT :
if ( ! value ) {
/* One of ALTs is being released */
if ( sysrq - > active & & code = = sysrq - > alt_use )
sysrq - > active = false ;
sysrq_alt = 0 ;
sysrq - > alt = KEY_RESERVED ;
} else if ( value ! = 2 ) {
sysrq - > alt = code ;
sysrq - > need_reinject = false ;
}
break ;
case KEY_SYSRQ :
if ( value = = 1 & & sysrq - > alt ! = KEY_RESERVED ) {
sysrq - > active = true ;
sysrq - > alt_use = sysrq - > alt ;
/*
* If nothing else will be pressed we ' ll need
* to * re - inject Alt - SysRq keysroke .
*/
sysrq - > need_reinject = true ;
}
/*
* Pretend that sysrq was never pressed at all . This
* is needed to properly handle KGDB which will try
* to release all keys after exiting debugger . If we
* do not clear key bit it KGDB will end up sending
* release events for Alt and SysRq , potentially
* triggering print screen function .
*/
if ( sysrq - > active )
clear_bit ( KEY_SYSRQ , handle - > dev - > key ) ;
break ;
default :
if ( sysrq - > active & & value & & value ! = 2 ) {
sysrq - > need_reinject = false ;
__handle_sysrq ( sysrq_xlate [ code ] , true ) ;
}
break ;
}
break ;
case KEY_SYSRQ :
if ( value = = 1 & & sysrq_alt ) {
sysrq_down = true ;
sysrq_alt_use = sysrq_alt ;
suppress = sysrq - > active ;
if ( ! sysrq - > active ) {
/*
* If we are not suppressing key presses keep track of
* keyboard state so we can release keys that have been
* pressed before entering SysRq mode .
*/
if ( value )
set_bit ( code , sysrq - > key_down ) ;
else
clear_bit ( code , sysrq - > key_down ) ;
if ( was_active )
schedule_work ( & sysrq - > reinject_work ) ;
} else if ( value = = 0 & &
test_and_clear_bit ( code , sysrq - > key_down ) ) {
/*
* Pass on release events for keys that was pressed before
* entering SysRq mode .
*/
suppress = false ;
}
break ;
default :
if ( sysrq_down & & value & & value ! = 2 )
__handle_sysrq ( sysrq_xlate [ code ] , true ) ;
suppress = sysrq - > active ;
break ;
}
out :
suppress = sysrq_down ;
spin_unlock ( & sysrq_event_lock ) ;
return suppress ;
}
@ -617,28 +693,28 @@ static int sysrq_connect(struct input_handler *handler,
struct input_dev * dev ,
const struct input_device_id * id )
{
struct input_handle * handle ;
struct sysrq_state * sysrq ;
int error ;
sysrq_down = false ;
sysrq_alt = 0 ;
handle = kzalloc ( sizeof ( struct input_handle ) , GFP_KERNEL ) ;
if ( ! handle )
sysrq = kzalloc ( sizeof ( struct sysrq_state ) , GFP_KERNEL ) ;
if ( ! sysrq )
return - ENOMEM ;
handle - > dev = dev ;
handle - > handler = handler ;
handle - > name = " sysrq " ;
INIT_WORK ( & sysrq - > reinject_work , sysrq_reinject_alt_sysrq ) ;
sysrq - > handle . dev = dev ;
sysrq - > handle . handler = handler ;
sysrq - > handle . name = " sysrq " ;
sysrq - > handle . private = sysrq ;
error = input_register_handle ( handle ) ;
error = input_register_handle ( & sysrq - > handle ) ;
if ( error ) {
pr_err ( " Failed to register input sysrq handler, error %d \n " ,
error ) ;
goto err_free ;
}
error = input_open_device ( handle ) ;
error = input_open_device ( & sysrq - > handle ) ;
if ( error ) {
pr_err ( " Failed to open input device, error %d \n " , error ) ;
goto err_unregister ;
@ -647,17 +723,20 @@ static int sysrq_connect(struct input_handler *handler,
return 0 ;
err_unregister :
input_unregister_handle ( handle ) ;
input_unregister_handle ( & sysrq - > handle ) ;
err_free :
kfree ( handle ) ;
kfree ( sysrq ) ;
return error ;
}
static void sysrq_disconnect ( struct input_handle * handle )
{
struct sysrq_state * sysrq = handle - > private ;
input_close_device ( handle ) ;
cancel_work_sync ( & sysrq - > reinject_work ) ;
input_unregister_handle ( handle ) ;
kfree ( handle ) ;
kfree ( sysrq ) ;
}
/*