Path: blob/master/drivers/media/rc/ir-rc6-decoder.c
15109 views
/* ir-rc6-decoder.c - A decoder for the RC6 IR protocol1*2* Copyright (C) 2010 by David Härdeman <[email protected]>3*4* This program is free software; you can redistribute it and/or modify5* it under the terms of the GNU General Public License as published by6* the Free Software Foundation version 2 of the License.7*8* This program is distributed in the hope that it will be useful,9* but WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11* GNU General Public License for more details.12*/1314#include "rc-core-priv.h"1516/*17* This decoder currently supports:18* RC6-0-16 (standard toggle bit in header)19* RC6-6A-24 (no toggle bit)20* RC6-6A-32 (MCE version with toggle bit in body)21*/2223#define RC6_UNIT 444444 /* us */24#define RC6_HEADER_NBITS 4 /* not including toggle bit */25#define RC6_0_NBITS 1626#define RC6_6A_SMALL_NBITS 2427#define RC6_6A_LARGE_NBITS 3228#define RC6_PREFIX_PULSE (6 * RC6_UNIT)29#define RC6_PREFIX_SPACE (2 * RC6_UNIT)30#define RC6_BIT_START (1 * RC6_UNIT)31#define RC6_BIT_END (1 * RC6_UNIT)32#define RC6_TOGGLE_START (2 * RC6_UNIT)33#define RC6_TOGGLE_END (2 * RC6_UNIT)34#define RC6_MODE_MASK 0x07 /* for the header bits */35#define RC6_STARTBIT_MASK 0x08 /* for the header bits */36#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */3738enum rc6_mode {39RC6_MODE_0,40RC6_MODE_6A,41RC6_MODE_UNKNOWN,42};4344enum rc6_state {45STATE_INACTIVE,46STATE_PREFIX_SPACE,47STATE_HEADER_BIT_START,48STATE_HEADER_BIT_END,49STATE_TOGGLE_START,50STATE_TOGGLE_END,51STATE_BODY_BIT_START,52STATE_BODY_BIT_END,53STATE_FINISHED,54};5556static enum rc6_mode rc6_mode(struct rc6_dec *data)57{58switch (data->header & RC6_MODE_MASK) {59case 0:60return RC6_MODE_0;61case 6:62if (!data->toggle)63return RC6_MODE_6A;64/* fall through */65default:66return RC6_MODE_UNKNOWN;67}68}6970/**71* ir_rc6_decode() - Decode one RC6 pulse or space72* @dev: the struct rc_dev descriptor of the device73* @ev: the struct ir_raw_event descriptor of the pulse/space74*75* This function returns -EINVAL if the pulse violates the state machine76*/77static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)78{79struct rc6_dec *data = &dev->raw->rc6;80u32 scancode;81u8 toggle;8283if (!(dev->raw->enabled_protocols & RC_TYPE_RC6))84return 0;8586if (!is_timing_event(ev)) {87if (ev.reset)88data->state = STATE_INACTIVE;89return 0;90}9192if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))93goto out;9495again:96IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n",97data->state, TO_US(ev.duration), TO_STR(ev.pulse));9899if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))100return 0;101102switch (data->state) {103104case STATE_INACTIVE:105if (!ev.pulse)106break;107108/* Note: larger margin on first pulse since each RC6_UNIT109is quite short and some hardware takes some time to110adjust to the signal */111if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT))112break;113114data->state = STATE_PREFIX_SPACE;115data->count = 0;116return 0;117118case STATE_PREFIX_SPACE:119if (ev.pulse)120break;121122if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2))123break;124125data->state = STATE_HEADER_BIT_START;126return 0;127128case STATE_HEADER_BIT_START:129if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2))130break;131132data->header <<= 1;133if (ev.pulse)134data->header |= 1;135data->count++;136data->state = STATE_HEADER_BIT_END;137return 0;138139case STATE_HEADER_BIT_END:140if (!is_transition(&ev, &dev->raw->prev_ev))141break;142143if (data->count == RC6_HEADER_NBITS)144data->state = STATE_TOGGLE_START;145else146data->state = STATE_HEADER_BIT_START;147148decrease_duration(&ev, RC6_BIT_END);149goto again;150151case STATE_TOGGLE_START:152if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2))153break;154155data->toggle = ev.pulse;156data->state = STATE_TOGGLE_END;157return 0;158159case STATE_TOGGLE_END:160if (!is_transition(&ev, &dev->raw->prev_ev) ||161!geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2))162break;163164if (!(data->header & RC6_STARTBIT_MASK)) {165IR_dprintk(1, "RC6 invalid start bit\n");166break;167}168169data->state = STATE_BODY_BIT_START;170decrease_duration(&ev, RC6_TOGGLE_END);171data->count = 0;172173switch (rc6_mode(data)) {174case RC6_MODE_0:175data->wanted_bits = RC6_0_NBITS;176break;177case RC6_MODE_6A:178/* This might look weird, but we basically179check the value of the first body bit to180determine the number of bits in mode 6A */181if ((!ev.pulse && !geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) ||182geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))183data->wanted_bits = RC6_6A_LARGE_NBITS;184else185data->wanted_bits = RC6_6A_SMALL_NBITS;186break;187default:188IR_dprintk(1, "RC6 unknown mode\n");189goto out;190}191goto again;192193case STATE_BODY_BIT_START:194if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2))195break;196197data->body <<= 1;198if (ev.pulse)199data->body |= 1;200data->count++;201data->state = STATE_BODY_BIT_END;202return 0;203204case STATE_BODY_BIT_END:205if (!is_transition(&ev, &dev->raw->prev_ev))206break;207208if (data->count == data->wanted_bits)209data->state = STATE_FINISHED;210else211data->state = STATE_BODY_BIT_START;212213decrease_duration(&ev, RC6_BIT_END);214goto again;215216case STATE_FINISHED:217if (ev.pulse)218break;219220switch (rc6_mode(data)) {221case RC6_MODE_0:222scancode = data->body & 0xffff;223toggle = data->toggle;224IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n",225scancode, toggle);226break;227case RC6_MODE_6A:228if (data->wanted_bits == RC6_6A_LARGE_NBITS) {229toggle = data->body & RC6_6A_MCE_TOGGLE_MASK ? 1 : 0;230scancode = data->body & ~RC6_6A_MCE_TOGGLE_MASK;231} else {232toggle = 0;233scancode = data->body & 0xffffff;234}235236IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n",237scancode, toggle);238break;239default:240IR_dprintk(1, "RC6 unknown mode\n");241goto out;242}243244rc_keydown(dev, scancode, toggle);245data->state = STATE_INACTIVE;246return 0;247}248249out:250IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n",251data->state, TO_US(ev.duration), TO_STR(ev.pulse));252data->state = STATE_INACTIVE;253return -EINVAL;254}255256static struct ir_raw_handler rc6_handler = {257.protocols = RC_TYPE_RC6,258.decode = ir_rc6_decode,259};260261static int __init ir_rc6_decode_init(void)262{263ir_raw_handler_register(&rc6_handler);264265printk(KERN_INFO "IR RC6 protocol handler initialized\n");266return 0;267}268269static void __exit ir_rc6_decode_exit(void)270{271ir_raw_handler_unregister(&rc6_handler);272}273274module_init(ir_rc6_decode_init);275module_exit(ir_rc6_decode_exit);276277MODULE_LICENSE("GPL");278MODULE_AUTHOR("David Härdeman <[email protected]>");279MODULE_DESCRIPTION("RC6 IR protocol decoder");280281282