2010-04-09 03:04:30 +04:00
/* ir-rc5-decoder.c - handle RC5(x) IR Pulse/Space protocol
2010-04-04 17:27:20 +04:00
*
* Copyright ( C ) 2010 by Mauro Carvalho Chehab < mchehab @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License .
*
* This program 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 .
*/
/*
2010-04-09 03:04:30 +04:00
* This code handles 14 bits RC5 protocols and 20 bits RC5x protocols .
* There are other variants that use a different number of bits .
* This is currently unsupported .
* It considers a carrier of 36 kHz , with a total of 14 / 20 bits , where
2010-04-06 09:29:42 +04:00
* the first two bits are start bits , and a third one is a filing bit
2010-04-04 17:27:20 +04:00
*/
2010-04-08 22:10:27 +04:00
# include "ir-core-priv.h"
2010-04-04 17:27:20 +04:00
2010-04-06 09:29:42 +04:00
# define RC5_NBITS 14
2010-04-09 03:04:30 +04:00
# define RC5X_NBITS 20
# define CHECK_RC5X_NBITS 8
2010-04-08 20:10:00 +04:00
# define RC5_UNIT 888888 /* ns */
2010-04-16 01:46:00 +04:00
# define RC5_BIT_START (1 * RC5_UNIT)
# define RC5_BIT_END (1 * RC5_UNIT)
# define RC5X_SPACE (4 * RC5_UNIT)
2010-04-04 17:27:20 +04:00
enum rc5_state {
STATE_INACTIVE ,
2010-04-08 20:10:00 +04:00
STATE_BIT_START ,
STATE_BIT_END ,
2010-04-09 03:04:30 +04:00
STATE_CHECK_RC5X ,
2010-04-08 20:10:00 +04:00
STATE_FINISHED ,
2010-04-04 17:27:20 +04:00
} ;
/**
2010-04-08 20:10:00 +04:00
* ir_rc5_decode ( ) - Decode one RC - 5 pulse or space
2010-04-04 17:27:20 +04:00
* @ input_dev : the struct input_dev descriptor of the device
2010-04-16 01:46:00 +04:00
* @ ev : the struct ir_raw_event descriptor of the pulse / space
2010-04-04 17:27:20 +04:00
*
* This function returns - EINVAL if the pulse violates the state machine
*/
2010-04-16 01:46:00 +04:00
static int ir_rc5_decode ( struct input_dev * input_dev , struct ir_raw_event ev )
2010-04-04 17:27:20 +04:00
{
struct ir_input_dev * ir_dev = input_get_drvdata ( input_dev ) ;
2010-06-14 00:29:36 +04:00
struct rc5_dec * data = & ir_dev - > raw - > rc5 ;
2010-04-09 03:04:30 +04:00
u8 toggle ;
2010-04-08 20:10:00 +04:00
u32 scancode ;
2010-04-04 17:27:20 +04:00
2010-06-14 00:29:31 +04:00
if ( ! ( ir_dev - > raw - > enabled_protocols & IR_TYPE_RC5 ) )
return 0 ;
2010-04-04 21:45:04 +04:00
2010-04-16 01:46:00 +04:00
if ( IS_RESET ( ev ) ) {
2010-04-04 17:27:20 +04:00
data - > state = STATE_INACTIVE ;
return 0 ;
2010-04-08 20:10:00 +04:00
}
2010-04-04 17:27:20 +04:00
2010-04-16 01:46:00 +04:00
if ( ! geq_margin ( ev . duration , RC5_UNIT , RC5_UNIT / 2 ) )
2010-04-08 20:10:00 +04:00
goto out ;
2010-04-04 17:27:20 +04:00
2010-04-08 20:10:00 +04:00
again :
2010-04-16 01:46:00 +04:00
IR_dprintk ( 2 , " RC5(x) decode started at state %i (%uus %s) \n " ,
data - > state , TO_US ( ev . duration ) , TO_STR ( ev . pulse ) ) ;
2010-04-04 17:27:20 +04:00
2010-04-16 01:46:00 +04:00
if ( ! geq_margin ( ev . duration , RC5_UNIT , RC5_UNIT / 2 ) )
2010-04-08 20:10:00 +04:00
return 0 ;
2010-04-04 17:27:20 +04:00
2010-04-08 20:10:00 +04:00
switch ( data - > state ) {
2010-04-04 17:27:20 +04:00
2010-04-08 20:10:00 +04:00
case STATE_INACTIVE :
2010-04-16 01:46:00 +04:00
if ( ! ev . pulse )
break ;
data - > state = STATE_BIT_START ;
data - > count = 1 ;
/* We just need enough bits to get to STATE_CHECK_RC5X */
data - > wanted_bits = RC5X_NBITS ;
decrease_duration ( & ev , RC5_BIT_START ) ;
goto again ;
2010-04-08 20:10:00 +04:00
case STATE_BIT_START :
2010-04-16 01:46:00 +04:00
if ( ! eq_margin ( ev . duration , RC5_BIT_START , RC5_UNIT / 2 ) )
break ;
2010-06-14 00:29:36 +04:00
data - > bits < < = 1 ;
2010-04-16 01:46:00 +04:00
if ( ! ev . pulse )
2010-06-14 00:29:36 +04:00
data - > bits | = 1 ;
2010-04-16 01:46:00 +04:00
data - > count + + ;
data - > state = STATE_BIT_END ;
return 0 ;
2010-04-06 09:29:42 +04:00
2010-04-08 20:10:00 +04:00
case STATE_BIT_END :
2010-06-14 00:29:36 +04:00
if ( ! is_transition ( & ev , & ir_dev - > raw - > prev_ev ) )
2010-04-16 01:46:00 +04:00
break ;
if ( data - > count = = data - > wanted_bits )
data - > state = STATE_FINISHED ;
else if ( data - > count = = CHECK_RC5X_NBITS )
data - > state = STATE_CHECK_RC5X ;
else
data - > state = STATE_BIT_START ;
decrease_duration ( & ev , RC5_BIT_END ) ;
goto again ;
2010-04-08 20:10:00 +04:00
2010-04-09 03:04:30 +04:00
case STATE_CHECK_RC5X :
2010-04-16 01:46:00 +04:00
if ( ! ev . pulse & & geq_margin ( ev . duration , RC5X_SPACE , RC5_UNIT / 2 ) ) {
2010-04-09 03:04:30 +04:00
/* RC5X */
data - > wanted_bits = RC5X_NBITS ;
2010-04-16 01:46:00 +04:00
decrease_duration ( & ev , RC5X_SPACE ) ;
2010-04-09 03:04:30 +04:00
} else {
/* RC5 */
data - > wanted_bits = RC5_NBITS ;
}
data - > state = STATE_BIT_START ;
goto again ;
2010-04-08 20:10:00 +04:00
case STATE_FINISHED :
2010-04-16 01:46:00 +04:00
if ( ev . pulse )
break ;
2010-04-09 03:04:30 +04:00
if ( data - > wanted_bits = = RC5X_NBITS ) {
/* RC5X */
u8 xdata , command , system ;
2010-06-14 00:29:36 +04:00
xdata = ( data - > bits & 0x0003F ) > > 0 ;
command = ( data - > bits & 0x00FC0 ) > > 6 ;
system = ( data - > bits & 0x1F000 ) > > 12 ;
toggle = ( data - > bits & 0x20000 ) ? 1 : 0 ;
command + = ( data - > bits & 0x01000 ) ? 0 : 0x40 ;
2010-04-09 03:04:30 +04:00
scancode = system < < 16 | command < < 8 | xdata ;
IR_dprintk ( 1 , " RC5X scancode 0x%06x (toggle: %u) \n " ,
scancode , toggle ) ;
} else {
/* RC5 */
u8 command , system ;
2010-06-14 00:29:36 +04:00
command = ( data - > bits & 0x0003F ) > > 0 ;
system = ( data - > bits & 0x007C0 ) > > 6 ;
toggle = ( data - > bits & 0x00800 ) ? 1 : 0 ;
command + = ( data - > bits & 0x01000 ) ? 0 : 0x40 ;
2010-04-09 03:04:30 +04:00
scancode = system < < 8 | command ;
IR_dprintk ( 1 , " RC5 scancode 0x%04x (toggle: %u) \n " ,
scancode , toggle ) ;
}
2010-04-08 20:10:00 +04:00
ir_keydown ( input_dev , scancode , toggle ) ;
2010-04-04 17:27:20 +04:00
data - > state = STATE_INACTIVE ;
return 0 ;
}
2010-04-08 20:10:00 +04:00
out :
2010-04-16 01:46:00 +04:00
IR_dprintk ( 1 , " RC5(x) decode failed at state %i (%uus %s) \n " ,
data - > state , TO_US ( ev . duration ) , TO_STR ( ev . pulse ) ) ;
2010-04-04 17:27:20 +04:00
data - > state = STATE_INACTIVE ;
return - EINVAL ;
}
static struct ir_raw_handler rc5_handler = {
2010-06-14 00:29:31 +04:00
. protocols = IR_TYPE_RC5 ,
2010-04-04 17:27:20 +04:00
. decode = ir_rc5_decode ,
} ;
static int __init ir_rc5_decode_init ( void )
{
ir_raw_handler_register ( & rc5_handler ) ;
2010-04-09 03:04:30 +04:00
printk ( KERN_INFO " IR RC5(x) protocol handler initialized \n " ) ;
2010-04-04 17:27:20 +04:00
return 0 ;
}
static void __exit ir_rc5_decode_exit ( void )
{
ir_raw_handler_unregister ( & rc5_handler ) ;
}
module_init ( ir_rc5_decode_init ) ;
module_exit ( ir_rc5_decode_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@redhat.com> " ) ;
MODULE_AUTHOR ( " Red Hat Inc. (http://www.redhat.com) " ) ;
2010-04-09 03:04:30 +04:00
MODULE_DESCRIPTION ( " RC5(x) IR protocol decoder " ) ;