2019-05-29 17:17:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-04-09 03:04:40 +04:00
/* ir-rc6-decoder.c - A decoder for the RC6 IR protocol
*
* Copyright ( C ) 2010 by David Härdeman < david @ hardeman . nu >
*/
2010-11-10 05:09:57 +03:00
# include "rc-core-priv.h"
2011-07-03 22:03:12 +04:00
# include <linux/module.h>
2010-04-09 03:04:40 +04:00
/*
* This decoder currently supports :
* RC6 - 0 - 16 ( standard toggle bit in header )
2011-10-31 15:39:32 +04:00
* RC6 - 6 A - 20 ( no toggle bit )
2010-04-09 03:04:40 +04:00
* RC6 - 6 A - 24 ( no toggle bit )
* RC6 - 6 A - 32 ( MCE version with toggle bit in body )
*/
2020-08-23 20:23:05 +03:00
# define RC6_UNIT 444 /* microseconds */
2010-04-09 03:04:40 +04:00
# define RC6_HEADER_NBITS 4 /* not including toggle bit */
# define RC6_0_NBITS 16
2011-10-31 15:39:32 +04:00
# define RC6_6A_32_NBITS 32
# define RC6_6A_NBITS 128 /* Variable 8..128 */
2010-04-16 01:46:00 +04:00
# define RC6_PREFIX_PULSE (6 * RC6_UNIT)
# define RC6_PREFIX_SPACE (2 * RC6_UNIT)
# define RC6_BIT_START (1 * RC6_UNIT)
# define RC6_BIT_END (1 * RC6_UNIT)
# define RC6_TOGGLE_START (2 * RC6_UNIT)
# define RC6_TOGGLE_END (2 * RC6_UNIT)
2011-10-31 15:39:32 +04:00
# define RC6_SUFFIX_SPACE (6 * RC6_UNIT)
2010-04-09 03:04:40 +04:00
# define RC6_MODE_MASK 0x07 /* for the header bits */
# define RC6_STARTBIT_MASK 0x08 /* for the header bits */
# define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */
2011-10-31 15:39:32 +04:00
# define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */
# define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */
2019-02-07 12:29:12 +03:00
# define RC6_6A_ZOTAC_CC 0x80340000 /* Zotac customer code */
2018-08-28 16:49:42 +03:00
# define RC6_6A_KATHREIN_CC 0x80460000 /* Kathrein RCU-676 customer code */
2011-10-31 15:39:32 +04:00
# ifndef CHAR_BIT
# define CHAR_BIT 8 /* Normally in <limits.h> */
# endif
2010-04-09 03:04:40 +04:00
enum rc6_mode {
RC6_MODE_0 ,
RC6_MODE_6A ,
RC6_MODE_UNKNOWN ,
} ;
enum rc6_state {
STATE_INACTIVE ,
STATE_PREFIX_SPACE ,
STATE_HEADER_BIT_START ,
STATE_HEADER_BIT_END ,
STATE_TOGGLE_START ,
STATE_TOGGLE_END ,
STATE_BODY_BIT_START ,
STATE_BODY_BIT_END ,
STATE_FINISHED ,
} ;
2010-06-14 00:29:36 +04:00
static enum rc6_mode rc6_mode ( struct rc6_dec * data )
2010-04-09 03:04:40 +04:00
{
switch ( data - > header & RC6_MODE_MASK ) {
case 0 :
return RC6_MODE_0 ;
case 6 :
if ( ! data - > toggle )
return RC6_MODE_6A ;
2020-08-24 01:36:59 +03:00
fallthrough ;
2010-04-09 03:04:40 +04:00
default :
return RC6_MODE_UNKNOWN ;
}
}
/**
* ir_rc6_decode ( ) - Decode one RC6 pulse or space
2010-10-29 23:08:23 +04:00
* @ dev : the struct rc_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-09 03:04:40 +04:00
*
* This function returns - EINVAL if the pulse violates the state machine
*/
2010-10-29 23:08:23 +04:00
static int ir_rc6_decode ( struct rc_dev * dev , struct ir_raw_event ev )
2010-04-09 03:04:40 +04:00
{
2010-10-29 23:08:23 +04:00
struct rc6_dec * data = & dev - > raw - > rc6 ;
2010-04-09 03:04:40 +04:00
u32 scancode ;
u8 toggle ;
2017-08-07 23:20:58 +03:00
enum rc_proto protocol ;
2010-04-09 03:04:40 +04:00
2010-10-17 02:56:28 +04:00
if ( ! is_timing_event ( ev ) ) {
2022-01-15 13:12:35 +03:00
if ( ev . overflow )
2010-10-17 02:56:28 +04:00
data - > state = STATE_INACTIVE ;
2010-04-09 03:04:40 +04:00
return 0 ;
}
2010-04-16 01:46:00 +04:00
if ( ! geq_margin ( ev . duration , RC6_UNIT , RC6_UNIT / 2 ) )
2010-04-09 03:04:40 +04:00
goto out ;
again :
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6 decode started at state %i (%uus %s) \n " ,
2020-08-23 20:23:05 +03:00
data - > state , ev . duration , TO_STR ( ev . pulse ) ) ;
2010-04-09 03:04:40 +04:00
2010-04-16 01:46:00 +04:00
if ( ! geq_margin ( ev . duration , RC6_UNIT , RC6_UNIT / 2 ) )
2010-04-09 03:04:40 +04:00
return 0 ;
switch ( data - > state ) {
case STATE_INACTIVE :
2010-04-16 01:46:00 +04:00
if ( ! ev . pulse )
break ;
/* Note: larger margin on first pulse since each RC6_UNIT
is quite short and some hardware takes some time to
adjust to the signal */
if ( ! eq_margin ( ev . duration , RC6_PREFIX_PULSE , RC6_UNIT ) )
break ;
data - > state = STATE_PREFIX_SPACE ;
data - > count = 0 ;
return 0 ;
2010-04-09 03:04:40 +04:00
case STATE_PREFIX_SPACE :
2010-04-16 01:46:00 +04:00
if ( ev . pulse )
break ;
if ( ! eq_margin ( ev . duration , RC6_PREFIX_SPACE , RC6_UNIT / 2 ) )
break ;
data - > state = STATE_HEADER_BIT_START ;
2011-10-31 15:39:32 +04:00
data - > header = 0 ;
2010-04-16 01:46:00 +04:00
return 0 ;
2010-04-09 03:04:40 +04:00
case STATE_HEADER_BIT_START :
2010-04-16 01:46:00 +04:00
if ( ! eq_margin ( ev . duration , RC6_BIT_START , RC6_UNIT / 2 ) )
break ;
data - > header < < = 1 ;
if ( ev . pulse )
data - > header | = 1 ;
data - > count + + ;
data - > state = STATE_HEADER_BIT_END ;
return 0 ;
2010-04-09 03:04:40 +04:00
case STATE_HEADER_BIT_END :
2010-04-16 01:46:00 +04:00
if ( data - > count = = RC6_HEADER_NBITS )
data - > state = STATE_TOGGLE_START ;
else
data - > state = STATE_HEADER_BIT_START ;
decrease_duration ( & ev , RC6_BIT_END ) ;
goto again ;
2010-04-09 03:04:40 +04:00
case STATE_TOGGLE_START :
2010-04-16 01:46:00 +04:00
if ( ! eq_margin ( ev . duration , RC6_TOGGLE_START , RC6_UNIT / 2 ) )
break ;
data - > toggle = ev . pulse ;
data - > state = STATE_TOGGLE_END ;
return 0 ;
2010-04-09 03:04:40 +04:00
case STATE_TOGGLE_END :
2010-04-16 01:46:00 +04:00
if ( ! ( data - > header & RC6_STARTBIT_MASK ) ) {
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6 invalid start bit \n " ) ;
2010-04-16 01:46:00 +04:00
break ;
}
2010-04-09 03:04:40 +04:00
2010-04-16 01:46:00 +04:00
data - > state = STATE_BODY_BIT_START ;
decrease_duration ( & ev , RC6_TOGGLE_END ) ;
data - > count = 0 ;
2011-10-31 15:39:32 +04:00
data - > body = 0 ;
2010-04-16 01:46:00 +04:00
switch ( rc6_mode ( data ) ) {
case RC6_MODE_0 :
data - > wanted_bits = RC6_0_NBITS ;
break ;
case RC6_MODE_6A :
2011-10-31 15:39:32 +04:00
data - > wanted_bits = RC6_6A_NBITS ;
2010-04-16 01:46:00 +04:00
break ;
default :
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6 unknown mode \n " ) ;
2010-04-16 01:46:00 +04:00
goto out ;
2010-04-09 03:04:40 +04:00
}
2010-04-16 01:46:00 +04:00
goto again ;
2010-04-09 03:04:40 +04:00
case STATE_BODY_BIT_START :
2011-10-31 15:39:32 +04:00
if ( eq_margin ( ev . duration , RC6_BIT_START , RC6_UNIT / 2 ) ) {
/* Discard LSB's that won't fit in data->body */
if ( data - > count + + < CHAR_BIT * sizeof data - > body ) {
data - > body < < = 1 ;
if ( ev . pulse )
data - > body | = 1 ;
}
data - > state = STATE_BODY_BIT_END ;
return 0 ;
} else if ( RC6_MODE_6A = = rc6_mode ( data ) & & ! ev . pulse & &
geq_margin ( ev . duration , RC6_SUFFIX_SPACE , RC6_UNIT / 2 ) ) {
data - > state = STATE_FINISHED ;
goto again ;
}
break ;
2010-04-09 03:04:40 +04:00
case STATE_BODY_BIT_END :
2010-04-16 01:46:00 +04:00
if ( data - > count = = data - > wanted_bits )
data - > state = STATE_FINISHED ;
else
data - > state = STATE_BODY_BIT_START ;
decrease_duration ( & ev , RC6_BIT_END ) ;
goto again ;
2010-04-09 03:04:40 +04:00
case STATE_FINISHED :
2010-04-16 01:46:00 +04:00
if ( ev . pulse )
break ;
2010-04-09 03:04:40 +04:00
switch ( rc6_mode ( data ) ) {
case RC6_MODE_0 :
2011-10-31 15:39:32 +04:00
scancode = data - > body ;
2010-04-09 03:04:40 +04:00
toggle = data - > toggle ;
2017-08-07 23:20:58 +03:00
protocol = RC_PROTO_RC6_0 ;
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6(0) scancode 0x%04x (toggle: %u) \n " ,
scancode , toggle ) ;
2010-04-09 03:04:40 +04:00
break ;
2014-04-04 03:31:30 +04:00
2010-04-09 03:04:40 +04:00
case RC6_MODE_6A :
2011-10-31 15:39:32 +04:00
if ( data - > count > CHAR_BIT * sizeof data - > body ) {
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6 too many (%u) data bits \n " ,
2011-10-31 15:39:32 +04:00
data - > count ) ;
goto out ;
}
scancode = data - > body ;
2014-04-04 03:31:30 +04:00
switch ( data - > count ) {
case 20 :
2017-08-07 23:20:58 +03:00
protocol = RC_PROTO_RC6_6A_20 ;
2014-04-04 03:31:30 +04:00
toggle = 0 ;
break ;
case 24 :
2017-08-07 23:20:58 +03:00
protocol = RC_PROTO_RC6_6A_24 ;
2010-04-09 03:04:40 +04:00
toggle = 0 ;
2014-04-04 03:31:30 +04:00
break ;
case 32 :
2018-08-28 16:49:42 +03:00
switch ( scancode & RC6_6A_LCC_MASK ) {
case RC6_6A_MCE_CC :
case RC6_6A_KATHREIN_CC :
2019-02-07 12:29:12 +03:00
case RC6_6A_ZOTAC_CC :
2017-08-07 23:20:58 +03:00
protocol = RC_PROTO_RC6_MCE ;
2014-04-04 03:31:30 +04:00
toggle = ! ! ( scancode & RC6_6A_MCE_TOGGLE_MASK ) ;
2014-11-21 00:09:54 +03:00
scancode & = ~ RC6_6A_MCE_TOGGLE_MASK ;
2018-08-28 16:49:42 +03:00
break ;
default :
2017-08-07 23:20:58 +03:00
protocol = RC_PROTO_RC6_6A_32 ;
2014-04-04 03:31:30 +04:00
toggle = 0 ;
2018-08-28 16:49:42 +03:00
break ;
2014-04-04 03:31:30 +04:00
}
break ;
default :
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6(6A) unsupported length \n " ) ;
2014-04-04 03:31:30 +04:00
goto out ;
2010-04-09 03:04:40 +04:00
}
2014-04-04 03:31:30 +04:00
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u) \n " ,
protocol , scancode , toggle ) ;
2010-04-09 03:04:40 +04:00
break ;
default :
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6 unknown mode \n " ) ;
2010-04-09 03:04:40 +04:00
goto out ;
}
2014-04-04 03:31:30 +04:00
rc_keydown ( dev , protocol , scancode , toggle ) ;
2010-04-09 03:04:40 +04:00
data - > state = STATE_INACTIVE ;
return 0 ;
}
out :
2018-02-12 15:20:52 +03:00
dev_dbg ( & dev - > dev , " RC6 decode failed at state %i (%uus %s) \n " ,
2020-08-23 20:23:05 +03:00
data - > state , ev . duration , TO_STR ( ev . pulse ) ) ;
2010-04-09 03:04:40 +04:00
data - > state = STATE_INACTIVE ;
return - EINVAL ;
}
2015-03-31 20:48:09 +03:00
static const struct ir_raw_timings_manchester ir_rc6_timings [ 4 ] = {
{
2018-01-05 16:26:51 +03:00
. leader_pulse = RC6_PREFIX_PULSE ,
. leader_space = RC6_PREFIX_SPACE ,
2015-03-31 20:48:09 +03:00
. clock = RC6_UNIT ,
. invert = 1 ,
} ,
{
. clock = RC6_UNIT * 2 ,
. invert = 1 ,
} ,
{
. clock = RC6_UNIT ,
. invert = 1 ,
. trailer_space = RC6_SUFFIX_SPACE ,
} ,
} ;
/**
* ir_rc6_encode ( ) - Encode a scancode as a stream of raw events
*
* @ protocol : protocol 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 23:20:58 +03:00
static int ir_rc6_encode ( enum rc_proto protocol , u32 scancode ,
2015-03-31 20:48:09 +03:00
struct ir_raw_event * events , unsigned int max )
{
int ret ;
struct ir_raw_event * e = events ;
2017-08-07 23:20:58 +03:00
if ( protocol = = RC_PROTO_RC6_0 ) {
2015-03-31 20:48:09 +03:00
/* Modulate the header (Start Bit & Mode-0) */
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
2018-01-05 16:26:51 +03:00
& ir_rc6_timings [ 0 ] ,
2018-01-05 16:38:43 +03:00
RC6_HEADER_NBITS , ( 1 < < 3 ) ) ;
2015-03-31 20:48:09 +03:00
if ( ret < 0 )
return ret ;
/* Modulate Trailer Bit */
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
2018-01-05 16:26:51 +03:00
& ir_rc6_timings [ 1 ] , 1 , 0 ) ;
2015-03-31 20:48:09 +03:00
if ( ret < 0 )
return ret ;
/* Modulate rest of the data */
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
2018-01-05 16:26:51 +03:00
& ir_rc6_timings [ 2 ] , RC6_0_NBITS ,
2015-03-31 20:48:09 +03:00
scancode ) ;
if ( ret < 0 )
return ret ;
} else {
int bits ;
switch ( protocol ) {
2017-08-07 23:20:58 +03:00
case RC_PROTO_RC6_MCE :
case RC_PROTO_RC6_6A_32 :
2015-03-31 20:48:09 +03:00
bits = 32 ;
break ;
2017-08-07 23:20:58 +03:00
case RC_PROTO_RC6_6A_24 :
2015-03-31 20:48:09 +03:00
bits = 24 ;
break ;
2017-08-07 23:20:58 +03:00
case RC_PROTO_RC6_6A_20 :
2015-03-31 20:48:09 +03:00
bits = 20 ;
break ;
default :
return - EINVAL ;
}
/* Modulate the header (Start Bit & Header-version 6 */
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
2018-01-05 16:26:51 +03:00
& ir_rc6_timings [ 0 ] ,
2018-01-05 16:38:43 +03:00
RC6_HEADER_NBITS , ( 1 < < 3 | 6 ) ) ;
2015-03-31 20:48:09 +03:00
if ( ret < 0 )
return ret ;
/* Modulate Trailer Bit */
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
2018-01-05 16:26:51 +03:00
& ir_rc6_timings [ 1 ] , 1 , 0 ) ;
2015-03-31 20:48:09 +03:00
if ( ret < 0 )
return ret ;
/* Modulate rest of the data */
ret = ir_raw_gen_manchester ( & e , max - ( e - events ) ,
2018-01-05 16:26:51 +03:00
& ir_rc6_timings [ 2 ] ,
2015-03-31 20:48:09 +03:00
bits ,
scancode ) ;
if ( ret < 0 )
return ret ;
}
return e - events ;
}
2010-04-09 03:04:40 +04:00
static struct ir_raw_handler rc6_handler = {
2017-08-07 23:20:58 +03:00
. protocols = RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 |
RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 |
RC_PROTO_BIT_RC6_MCE ,
2010-04-09 03:04:40 +04:00
. decode = ir_rc6_decode ,
2015-03-31 20:48:09 +03:00
. encode = ir_rc6_encode ,
2017-02-25 14:51:30 +03:00
. carrier = 36000 ,
2018-03-23 23:47:37 +03:00
. min_timeout = RC6_SUFFIX_SPACE ,
2010-04-09 03:04:40 +04:00
} ;
static int __init ir_rc6_decode_init ( void )
{
ir_raw_handler_register ( & rc6_handler ) ;
printk ( KERN_INFO " IR RC6 protocol handler initialized \n " ) ;
return 0 ;
}
static void __exit ir_rc6_decode_exit ( void )
{
ir_raw_handler_unregister ( & rc6_handler ) ;
}
module_init ( ir_rc6_decode_init ) ;
module_exit ( ir_rc6_decode_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Härdeman <david@hardeman.nu> " ) ;
MODULE_DESCRIPTION ( " RC6 IR protocol decoder " ) ;