2017-12-01 08:47:08 -05:00
// SPDX-License-Identifier: GPL-2.0
// ir-rc5-decoder.c - decoder for RC5(x) and StreamZap protocols
//
// Copyright (C) 2010 by Mauro Carvalho Chehab
// Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
2010-04-04 10:27:20 -03:00
/*
2014-04-03 20:32:31 -03:00
* This decoder handles the 14 bit RC5 protocol , 15 bit " StreamZap " protocol
* and 20 bit RC5x protocol .
2010-04-04 10:27:20 -03:00
*/
2010-11-09 23:09:57 -03:00
# include "rc-core-priv.h"
2011-07-03 14:03:12 -04:00
# include <linux/module.h>
2010-04-04 10:27:20 -03:00
2010-04-06 02:29:42 -03:00
# define RC5_NBITS 14
2014-04-03 20:32:31 -03:00
# define RC5_SZ_NBITS 15
2010-04-08 20:04:30 -03:00
# define RC5X_NBITS 20
# define CHECK_RC5X_NBITS 8
2010-04-08 13:10:00 -03:00
# define RC5_UNIT 888888 /* ns */
2010-04-15 18:46:00 -03:00
# define RC5_BIT_START (1 * RC5_UNIT)
# define RC5_BIT_END (1 * RC5_UNIT)
# define RC5X_SPACE (4 * RC5_UNIT)
2016-05-14 14:01:26 -03:00
# define RC5_TRAILER (6 * RC5_UNIT) /* In reality, approx 100 */
2010-04-04 10:27:20 -03:00
enum rc5_state {
STATE_INACTIVE ,
2010-04-08 13:10:00 -03:00
STATE_BIT_START ,
STATE_BIT_END ,
2010-04-08 20:04:30 -03:00
STATE_CHECK_RC5X ,
2010-04-08 13:10:00 -03:00
STATE_FINISHED ,
2010-04-04 10:27:20 -03:00
} ;
/**
2010-04-08 13:10:00 -03:00
* ir_rc5_decode ( ) - Decode one RC - 5 pulse or space
2010-10-29 16:08:23 -03:00
* @ dev : the struct rc_dev descriptor of the device
2010-04-15 18:46:00 -03:00
* @ ev : the struct ir_raw_event descriptor of the pulse / space
2010-04-04 10:27:20 -03:00
*
* This function returns - EINVAL if the pulse violates the state machine
*/
2010-10-29 16:08:23 -03:00
static int ir_rc5_decode ( struct rc_dev * dev , struct ir_raw_event ev )
2010-04-04 10:27:20 -03:00
{
2010-10-29 16:08:23 -03:00
struct rc5_dec * data = & dev - > raw - > rc5 ;
2010-04-08 20:04:30 -03:00
u8 toggle ;
2010-04-08 13:10:00 -03:00
u32 scancode ;
2017-08-07 16:20:58 -04:00
enum rc_proto protocol ;
2010-04-04 10:27:20 -03:00
2010-10-16 19:56:28 -03:00
if ( ! is_timing_event ( ev ) ) {
if ( ev . reset )
data - > state = STATE_INACTIVE ;
2010-04-04 10:27:20 -03:00
return 0 ;
2010-04-08 13:10:00 -03:00
}
2010-04-04 10:27:20 -03:00
2010-04-15 18:46:00 -03:00
if ( ! geq_margin ( ev . duration , RC5_UNIT , RC5_UNIT / 2 ) )
2010-04-08 13:10:00 -03:00
goto out ;
2010-04-04 10:27:20 -03:00
2010-04-08 13:10:00 -03:00
again :
2018-02-12 07:20:52 -05:00
dev_dbg ( & dev - > dev , " RC5(x/sz) decode started at state %i (%uus %s) \n " ,
data - > state , TO_US ( ev . duration ) , TO_STR ( ev . pulse ) ) ;
2010-04-04 10:27:20 -03:00
2010-04-15 18:46:00 -03:00
if ( ! geq_margin ( ev . duration , RC5_UNIT , RC5_UNIT / 2 ) )
2010-04-08 13:10:00 -03:00
return 0 ;
2010-04-04 10:27:20 -03:00
2010-04-08 13:10:00 -03:00
switch ( data - > state ) {
2010-04-04 10:27:20 -03:00
2010-04-08 13:10:00 -03:00
case STATE_INACTIVE :
2010-04-15 18:46:00 -03:00
if ( ! ev . pulse )
break ;
data - > state = STATE_BIT_START ;
data - > count = 1 ;
decrease_duration ( & ev , RC5_BIT_START ) ;
goto again ;
2010-04-08 13:10:00 -03:00
case STATE_BIT_START :
2014-04-03 20:32:31 -03:00
if ( ! ev . pulse & & geq_margin ( ev . duration , RC5_TRAILER , RC5_UNIT / 2 ) ) {
data - > state = STATE_FINISHED ;
goto again ;
}
2010-04-15 18:46:00 -03:00
if ( ! eq_margin ( ev . duration , RC5_BIT_START , RC5_UNIT / 2 ) )
break ;
2010-06-13 17:29:36 -03:00
data - > bits < < = 1 ;
2010-04-15 18:46:00 -03:00
if ( ! ev . pulse )
2010-06-13 17:29:36 -03:00
data - > bits | = 1 ;
2010-04-15 18:46:00 -03:00
data - > count + + ;
data - > state = STATE_BIT_END ;
return 0 ;
2010-04-06 02:29:42 -03:00
2010-04-08 13:10:00 -03:00
case STATE_BIT_END :
2010-10-29 16:08:23 -03:00
if ( ! is_transition ( & ev , & dev - > raw - > prev_ev ) )
2010-04-15 18:46:00 -03:00
break ;
2014-04-03 20:32:31 -03:00
if ( data - > count = = CHECK_RC5X_NBITS )
2010-04-15 18:46:00 -03:00
data - > state = STATE_CHECK_RC5X ;
else
data - > state = STATE_BIT_START ;
decrease_duration ( & ev , RC5_BIT_END ) ;
goto again ;
2010-04-08 13:10:00 -03:00
2010-04-08 20:04:30 -03:00
case STATE_CHECK_RC5X :
2010-04-15 18:46:00 -03:00
if ( ! ev . pulse & & geq_margin ( ev . duration , RC5X_SPACE , RC5_UNIT / 2 ) ) {
2014-04-03 20:32:31 -03:00
data - > is_rc5x = true ;
2010-04-15 18:46:00 -03:00
decrease_duration ( & ev , RC5X_SPACE ) ;
2014-04-03 20:32:31 -03:00
} else
data - > is_rc5x = false ;
2010-04-08 20:04:30 -03:00
data - > state = STATE_BIT_START ;
goto again ;
2010-04-08 13:10:00 -03:00
case STATE_FINISHED :
2010-04-15 18:46:00 -03:00
if ( ev . pulse )
break ;
2014-04-03 20:32:31 -03:00
if ( data - > is_rc5x & & data - > count = = RC5X_NBITS ) {
2010-04-08 20:04:30 -03:00
/* RC5X */
u8 xdata , command , system ;
2017-08-07 16:20:58 -04:00
if ( ! ( dev - > enabled_protocols & RC_PROTO_BIT_RC5X_20 ) ) {
2012-10-11 19:11:54 -03:00
data - > state = STATE_INACTIVE ;
return 0 ;
}
2010-06-13 17:29:36 -03:00
xdata = ( data - > bits & 0x0003F ) > > 0 ;
command = ( data - > bits & 0x00FC0 ) > > 6 ;
system = ( data - > bits & 0x1F000 ) > > 12 ;
toggle = ( data - > bits & 0x20000 ) ? 1 : 0 ;
2016-12-02 15:16:13 -02:00
command + = ( data - > bits & 0x40000 ) ? 0 : 0x40 ;
2010-04-08 20:04:30 -03:00
scancode = system < < 16 | command < < 8 | xdata ;
2017-08-07 16:20:58 -04:00
protocol = RC_PROTO_RC5X_20 ;
2010-04-08 20:04:30 -03:00
2014-04-03 20:32:31 -03:00
} else if ( ! data - > is_rc5x & & data - > count = = RC5_NBITS ) {
2010-04-08 20:04:30 -03:00
/* RC5 */
u8 command , system ;
2017-08-07 16:20:58 -04:00
if ( ! ( dev - > enabled_protocols & RC_PROTO_BIT_RC5 ) ) {
2012-10-11 19:11:54 -03:00
data - > state = STATE_INACTIVE ;
return 0 ;
}
2010-06-13 17:29:36 -03: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-08 20:04:30 -03:00
scancode = system < < 8 | command ;
2017-08-07 16:20:58 -04:00
protocol = RC_PROTO_RC5 ;
2010-04-08 20:04:30 -03:00
2014-04-03 20:32:31 -03:00
} else if ( ! data - > is_rc5x & & data - > count = = RC5_SZ_NBITS ) {
/* RC5 StreamZap */
u8 command , system ;
2017-08-07 16:20:58 -04:00
if ( ! ( dev - > enabled_protocols & RC_PROTO_BIT_RC5_SZ ) ) {
2014-04-03 20:32:31 -03:00
data - > state = STATE_INACTIVE ;
return 0 ;
}
command = ( data - > bits & 0x0003F ) > > 0 ;
system = ( data - > bits & 0x02FC0 ) > > 6 ;
toggle = ( data - > bits & 0x01000 ) ? 1 : 0 ;
scancode = system < < 6 | command ;
2017-08-07 16:20:58 -04:00
protocol = RC_PROTO_RC5_SZ ;
2014-04-03 20:32:31 -03:00
} else
break ;
2018-02-12 07:20:52 -05:00
dev_dbg ( & dev - > dev , " RC5(x/sz) scancode 0x%06x (p: %u, t: %u) \n " ,
scancode , protocol , toggle ) ;
2010-04-08 20:04:30 -03:00
2014-04-03 20:31:30 -03:00
rc_keydown ( dev , protocol , scancode , toggle ) ;
2010-04-04 10:27:20 -03:00
data - > state = STATE_INACTIVE ;
return 0 ;
}
2010-04-08 13:10:00 -03:00
out :
2018-02-12 07:20:52 -05:00
dev_dbg ( & dev - > dev , " RC5(x/sz) decode failed at state %i count %d (%uus %s) \n " ,
data - > state , data - > count , TO_US ( ev . duration ) , TO_STR ( ev . pulse ) ) ;
2010-04-04 10:27:20 -03:00
data - > state = STATE_INACTIVE ;
return - EINVAL ;
}
2015-03-31 14:48:08 -03:00
static const struct ir_raw_timings_manchester ir_rc5_timings = {
2018-01-05 08:26:51 -05:00
. leader_pulse = RC5_UNIT ,
2015-03-31 14:48:08 -03:00
. clock = RC5_UNIT ,
. trailer_space = RC5_UNIT * 10 ,
} ;
static const struct ir_raw_timings_manchester ir_rc5x_timings [ 2 ] = {
{
2018-01-05 08:26:51 -05:00
. leader_pulse = RC5_UNIT ,
2015-03-31 14:48:08 -03:00
. clock = RC5_UNIT ,
. trailer_space = RC5X_SPACE ,
} ,
{
. clock = RC5_UNIT ,
. trailer_space = RC5_UNIT * 10 ,
} ,
} ;
static const struct ir_raw_timings_manchester ir_rc5_sz_timings = {
2018-01-05 08:26:51 -05:00
. leader_pulse = RC5_UNIT ,
2015-03-31 14:48:08 -03:00
. clock = RC5_UNIT ,
. trailer_space = RC5_UNIT * 10 ,
} ;
/**
* ir_rc5_encode ( ) - Encode a scancode as a stream of raw events
*
* @ protocol : protocol variant to encode
* @ scancode : scancode to encode
* @ events : array of raw ir events to write into
* @ max : maximum size of @ events
*
* Returns : The number of events written .
* - ENOBUFS if there isn ' t enough space in the array to fit the
* encoding . In this case all @ max events will have been written .
* - EINVAL if the scancode is ambiguous or invalid .
*/
2017-08-07 16:20:58 -04:00
static int ir_rc5_encode ( enum rc_proto protocol , u32 scancode ,
2015-03-31 14:48:08 -03:00
struct ir_raw_event * events , unsigned int max )
{
int ret ;
struct ir_raw_event * e = events ;
unsigned int data , xdata , command , commandx , system , pre_space_data ;
/* Detect protocol and convert scancode to raw data */
2017-08-07 16:20:58 -04:00
if ( protocol = = RC_PROTO_RC5 ) {
2015-03-31 14:48:08 -03:00
/* decode scancode */
command = ( scancode & 0x003f ) > > 0 ;
commandx = ( scancode & 0x0040 ) > > 6 ;
system = ( scancode & 0x1f00 ) > > 8 ;
/* encode data */
data = ! commandx < < 12 | system < < 6 | command ;
2018-01-05 08:38:43 -05:00
/* First bit is encoded by leader_pulse */
2015-03-31 14:48:08 -03:00
ret = ir_raw_gen_manchester ( & e , max , & ir_rc5_timings ,
2018-01-05 08:38:43 -05:00
RC5_NBITS - 1 , data ) ;
2015-03-31 14:48:08 -03:00
if ( ret < 0 )
return ret ;
2017-08-07 16:20:58 -04:00
} else if ( protocol = = RC_PROTO_RC5X_20 ) {
2015-03-31 14:48:08 -03:00
/* decode scancode */
xdata = ( scancode & 0x00003f ) > > 0 ;
command = ( scancode & 0x003f00 ) > > 8 ;
commandx = ! ( scancode & 0x004000 ) ;
system = ( scancode & 0x1f0000 ) > > 16 ;
/* encode data */
data = commandx < < 18 | system < < 12 | command < < 6 | xdata ;
2018-01-05 08:38:43 -05:00
/* First bit is encoded by leader_pulse */
2015-03-31 14:48:08 -03:00
pre_space_data = data > > ( RC5X_NBITS - CHECK_RC5X_NBITS ) ;
ret = ir_raw_gen_manchester ( & e , max , & ir_rc5x_timings [ 0 ] ,
2018-01-05 08:38:43 -05:00
CHECK_RC5X_NBITS - 1 ,
pre_space_data ) ;
2015-03-31 14:48:08 -03:00
if ( ret < 0 )
return ret ;
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
& ir_rc5x_timings [ 1 ] ,
RC5X_NBITS - CHECK_RC5X_NBITS ,
data ) ;
if ( ret < 0 )
return ret ;
2017-08-07 16:20:58 -04:00
} else if ( protocol = = RC_PROTO_RC5_SZ ) {
2015-03-31 14:48:08 -03:00
/* RC5-SZ scancode is raw enough for Manchester as it is */
2018-01-05 08:38:43 -05:00
/* First bit is encoded by leader_pulse */
2015-03-31 14:48:08 -03:00
ret = ir_raw_gen_manchester ( & e , max , & ir_rc5_sz_timings ,
2018-01-05 08:38:43 -05:00
RC5_SZ_NBITS - 1 ,
scancode & 0x2fff ) ;
2015-03-31 14:48:08 -03:00
if ( ret < 0 )
return ret ;
} else {
return - EINVAL ;
}
return e - events ;
}
2010-04-04 10:27:20 -03:00
static struct ir_raw_handler rc5_handler = {
2017-08-07 16:20:58 -04:00
. protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 |
RC_PROTO_BIT_RC5_SZ ,
2010-04-04 10:27:20 -03:00
. decode = ir_rc5_decode ,
2015-03-31 14:48:08 -03:00
. encode = ir_rc5_encode ,
2017-02-25 06:51:30 -05:00
. carrier = 36000 ,
2010-04-04 10:27:20 -03:00
} ;
static int __init ir_rc5_decode_init ( void )
{
ir_raw_handler_register ( & rc5_handler ) ;
2014-04-03 20:32:31 -03:00
printk ( KERN_INFO " IR RC5(x/sz) protocol handler initialized \n " ) ;
2010-04-04 10:27:20 -03: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 ) ;
2017-12-01 08:47:08 -05:00
MODULE_LICENSE ( " GPL v2 " ) ;
2014-04-03 20:32:31 -03:00
MODULE_AUTHOR ( " Mauro Carvalho Chehab and Jarod Wilson " ) ;
2010-04-04 10:27:20 -03:00
MODULE_AUTHOR ( " Red Hat Inc. (http://www.redhat.com) " ) ;
2014-04-03 20:32:31 -03:00
MODULE_DESCRIPTION ( " RC5(x/sz) IR protocol decoder " ) ;