120835280SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0 220835280SMauro Carvalho Chehab // ir-nec-decoder.c - handle NEC IR Pulse/Space protocol 320835280SMauro Carvalho Chehab // 420835280SMauro Carvalho Chehab // Copyright (C) 2010 by Mauro Carvalho Chehab 532cf86f6SMauro Carvalho Chehab 632cf86f6SMauro Carvalho Chehab #include <linux/bitrev.h> 77a707b89SPaul Gortmaker #include <linux/module.h> 8f62de675SMauro Carvalho Chehab #include "rc-core-priv.h" 932cf86f6SMauro Carvalho Chehab 1032cf86f6SMauro Carvalho Chehab #define NEC_NBITS 32 11528222d8SSean Young #define NEC_UNIT 563 /* us */ 1232cf86f6SMauro Carvalho Chehab #define NEC_HEADER_PULSE (16 * NEC_UNIT) 1332cf86f6SMauro Carvalho Chehab #define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */ 1432cf86f6SMauro Carvalho Chehab #define NEC_HEADER_SPACE (8 * NEC_UNIT) 1532cf86f6SMauro Carvalho Chehab #define NEC_REPEAT_SPACE (4 * NEC_UNIT) 1632cf86f6SMauro Carvalho Chehab #define NEC_BIT_PULSE (1 * NEC_UNIT) 1732cf86f6SMauro Carvalho Chehab #define NEC_BIT_0_SPACE (1 * NEC_UNIT) 1832cf86f6SMauro Carvalho Chehab #define NEC_BIT_1_SPACE (3 * NEC_UNIT) 1932cf86f6SMauro Carvalho Chehab #define NEC_TRAILER_PULSE (1 * NEC_UNIT) 2032cf86f6SMauro Carvalho Chehab #define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */ 2132cf86f6SMauro Carvalho Chehab #define NECX_REPEAT_BITS 1 2232cf86f6SMauro Carvalho Chehab 2332cf86f6SMauro Carvalho Chehab enum nec_state { 2432cf86f6SMauro Carvalho Chehab STATE_INACTIVE, 2532cf86f6SMauro Carvalho Chehab STATE_HEADER_SPACE, 2632cf86f6SMauro Carvalho Chehab STATE_BIT_PULSE, 2732cf86f6SMauro Carvalho Chehab STATE_BIT_SPACE, 2832cf86f6SMauro Carvalho Chehab STATE_TRAILER_PULSE, 2932cf86f6SMauro Carvalho Chehab STATE_TRAILER_SPACE, 3032cf86f6SMauro Carvalho Chehab }; 3132cf86f6SMauro Carvalho Chehab 3232cf86f6SMauro Carvalho Chehab /** 3332cf86f6SMauro Carvalho Chehab * ir_nec_decode() - Decode one NEC pulse or space 34d8b4b582SDavid Härdeman * @dev: the struct rc_dev descriptor of the device 3564dc6829SMauro Carvalho Chehab * @ev: the struct ir_raw_event descriptor of the pulse/space 3632cf86f6SMauro Carvalho Chehab * 3732cf86f6SMauro Carvalho Chehab * This function returns -EINVAL if the pulse violates the state machine 3832cf86f6SMauro Carvalho Chehab */ 39d8b4b582SDavid Härdeman static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) 4032cf86f6SMauro Carvalho Chehab { 41d8b4b582SDavid Härdeman struct nec_dec *data = &dev->raw->nec; 4232cf86f6SMauro Carvalho Chehab u32 scancode; 436d741bfeSSean Young enum rc_proto rc_proto; 4432cf86f6SMauro Carvalho Chehab u8 address, not_address, command, not_command; 4532cf86f6SMauro Carvalho Chehab 4632cf86f6SMauro Carvalho Chehab if (!is_timing_event(ev)) { 47*950170d6SSean Young if (ev.overflow) 4832cf86f6SMauro Carvalho Chehab data->state = STATE_INACTIVE; 4932cf86f6SMauro Carvalho Chehab return 0; 5032cf86f6SMauro Carvalho Chehab } 5132cf86f6SMauro Carvalho Chehab 5250078a90SSean Young dev_dbg(&dev->dev, "NEC decode started at state %d (%uus %s)\n", 53528222d8SSean Young data->state, ev.duration, TO_STR(ev.pulse)); 5432cf86f6SMauro Carvalho Chehab 5532cf86f6SMauro Carvalho Chehab switch (data->state) { 5632cf86f6SMauro Carvalho Chehab 5732cf86f6SMauro Carvalho Chehab case STATE_INACTIVE: 5832cf86f6SMauro Carvalho Chehab if (!ev.pulse) 5932cf86f6SMauro Carvalho Chehab break; 6032cf86f6SMauro Carvalho Chehab 61743135e7SSean Young if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT * 2)) { 6232cf86f6SMauro Carvalho Chehab data->is_nec_x = false; 6332cf86f6SMauro Carvalho Chehab data->necx_repeat = false; 6432cf86f6SMauro Carvalho Chehab } else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2)) 6532cf86f6SMauro Carvalho Chehab data->is_nec_x = true; 6632cf86f6SMauro Carvalho Chehab else 6732cf86f6SMauro Carvalho Chehab break; 6832cf86f6SMauro Carvalho Chehab 6932cf86f6SMauro Carvalho Chehab data->count = 0; 7032cf86f6SMauro Carvalho Chehab data->state = STATE_HEADER_SPACE; 7132cf86f6SMauro Carvalho Chehab return 0; 7232cf86f6SMauro Carvalho Chehab 7332cf86f6SMauro Carvalho Chehab case STATE_HEADER_SPACE: 7432cf86f6SMauro Carvalho Chehab if (ev.pulse) 7532cf86f6SMauro Carvalho Chehab break; 7632cf86f6SMauro Carvalho Chehab 77743135e7SSean Young if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT)) { 7832cf86f6SMauro Carvalho Chehab data->state = STATE_BIT_PULSE; 7932cf86f6SMauro Carvalho Chehab return 0; 8032cf86f6SMauro Carvalho Chehab } else if (eq_margin(ev.duration, NEC_REPEAT_SPACE, NEC_UNIT / 2)) { 8132cf86f6SMauro Carvalho Chehab data->state = STATE_TRAILER_PULSE; 8232cf86f6SMauro Carvalho Chehab return 0; 8332cf86f6SMauro Carvalho Chehab } 8432cf86f6SMauro Carvalho Chehab 8532cf86f6SMauro Carvalho Chehab break; 8632cf86f6SMauro Carvalho Chehab 8732cf86f6SMauro Carvalho Chehab case STATE_BIT_PULSE: 8832cf86f6SMauro Carvalho Chehab if (!ev.pulse) 8932cf86f6SMauro Carvalho Chehab break; 9032cf86f6SMauro Carvalho Chehab 9132cf86f6SMauro Carvalho Chehab if (!eq_margin(ev.duration, NEC_BIT_PULSE, NEC_UNIT / 2)) 9232cf86f6SMauro Carvalho Chehab break; 9332cf86f6SMauro Carvalho Chehab 9432cf86f6SMauro Carvalho Chehab data->state = STATE_BIT_SPACE; 9532cf86f6SMauro Carvalho Chehab return 0; 9632cf86f6SMauro Carvalho Chehab 9732cf86f6SMauro Carvalho Chehab case STATE_BIT_SPACE: 9832cf86f6SMauro Carvalho Chehab if (ev.pulse) 9932cf86f6SMauro Carvalho Chehab break; 10032cf86f6SMauro Carvalho Chehab 10132cf86f6SMauro Carvalho Chehab if (data->necx_repeat && data->count == NECX_REPEAT_BITS && 10250078a90SSean Young geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) { 10350078a90SSean Young dev_dbg(&dev->dev, "Repeat last key\n"); 104ca86674bSMauro Carvalho Chehab rc_repeat(dev); 10532cf86f6SMauro Carvalho Chehab data->state = STATE_INACTIVE; 10632cf86f6SMauro Carvalho Chehab return 0; 10732cf86f6SMauro Carvalho Chehab } else if (data->count > NECX_REPEAT_BITS) 10832cf86f6SMauro Carvalho Chehab data->necx_repeat = false; 10932cf86f6SMauro Carvalho Chehab 11032cf86f6SMauro Carvalho Chehab data->bits <<= 1; 11132cf86f6SMauro Carvalho Chehab if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2)) 11232cf86f6SMauro Carvalho Chehab data->bits |= 1; 11332cf86f6SMauro Carvalho Chehab else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2)) 11432cf86f6SMauro Carvalho Chehab break; 11532cf86f6SMauro Carvalho Chehab data->count++; 11632cf86f6SMauro Carvalho Chehab 11732cf86f6SMauro Carvalho Chehab if (data->count == NEC_NBITS) 11832cf86f6SMauro Carvalho Chehab data->state = STATE_TRAILER_PULSE; 11932cf86f6SMauro Carvalho Chehab else 12032cf86f6SMauro Carvalho Chehab data->state = STATE_BIT_PULSE; 12132cf86f6SMauro Carvalho Chehab 12232cf86f6SMauro Carvalho Chehab return 0; 12332cf86f6SMauro Carvalho Chehab 12432cf86f6SMauro Carvalho Chehab case STATE_TRAILER_PULSE: 12532cf86f6SMauro Carvalho Chehab if (!ev.pulse) 12632cf86f6SMauro Carvalho Chehab break; 12732cf86f6SMauro Carvalho Chehab 12832cf86f6SMauro Carvalho Chehab if (!eq_margin(ev.duration, NEC_TRAILER_PULSE, NEC_UNIT / 2)) 12932cf86f6SMauro Carvalho Chehab break; 13032cf86f6SMauro Carvalho Chehab 13132cf86f6SMauro Carvalho Chehab data->state = STATE_TRAILER_SPACE; 13232cf86f6SMauro Carvalho Chehab return 0; 13332cf86f6SMauro Carvalho Chehab 13432cf86f6SMauro Carvalho Chehab case STATE_TRAILER_SPACE: 13532cf86f6SMauro Carvalho Chehab if (ev.pulse) 13632cf86f6SMauro Carvalho Chehab break; 13732cf86f6SMauro Carvalho Chehab 13832cf86f6SMauro Carvalho Chehab if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) 13932cf86f6SMauro Carvalho Chehab break; 14032cf86f6SMauro Carvalho Chehab 141829bbf26SSean Young if (data->count == NEC_NBITS) { 14232cf86f6SMauro Carvalho Chehab address = bitrev8((data->bits >> 24) & 0xff); 14332cf86f6SMauro Carvalho Chehab not_address = bitrev8((data->bits >> 16) & 0xff); 14432cf86f6SMauro Carvalho Chehab command = bitrev8((data->bits >> 8) & 0xff); 14532cf86f6SMauro Carvalho Chehab not_command = bitrev8((data->bits >> 0) & 0xff); 14632cf86f6SMauro Carvalho Chehab 147829bbf26SSean Young scancode = ir_nec_bytes_to_scancode(address, 148829bbf26SSean Young not_address, 149829bbf26SSean Young command, 150829bbf26SSean Young not_command, 1516d741bfeSSean Young &rc_proto); 15232cf86f6SMauro Carvalho Chehab 15332cf86f6SMauro Carvalho Chehab if (data->is_nec_x) 15432cf86f6SMauro Carvalho Chehab data->necx_repeat = true; 15532cf86f6SMauro Carvalho Chehab 1566d741bfeSSean Young rc_keydown(dev, rc_proto, scancode, 0); 157829bbf26SSean Young } else { 158829bbf26SSean Young rc_repeat(dev); 159829bbf26SSean Young } 160829bbf26SSean Young 16132cf86f6SMauro Carvalho Chehab data->state = STATE_INACTIVE; 16232cf86f6SMauro Carvalho Chehab return 0; 16332cf86f6SMauro Carvalho Chehab } 16432cf86f6SMauro Carvalho Chehab 16550078a90SSean Young dev_dbg(&dev->dev, "NEC decode failed at count %d state %d (%uus %s)\n", 166528222d8SSean Young data->count, data->state, ev.duration, TO_STR(ev.pulse)); 16732cf86f6SMauro Carvalho Chehab data->state = STATE_INACTIVE; 16832cf86f6SMauro Carvalho Chehab return -EINVAL; 16932cf86f6SMauro Carvalho Chehab } 17032cf86f6SMauro Carvalho Chehab 171141cfb14SJames Hogan /** 172141cfb14SJames Hogan * ir_nec_scancode_to_raw() - encode an NEC scancode ready for modulation. 173141cfb14SJames Hogan * @protocol: specific protocol to use 174141cfb14SJames Hogan * @scancode: a single NEC scancode. 175141cfb14SJames Hogan */ 1766d741bfeSSean Young static u32 ir_nec_scancode_to_raw(enum rc_proto protocol, u32 scancode) 177141cfb14SJames Hogan { 178141cfb14SJames Hogan unsigned int addr, addr_inv, data, data_inv; 179141cfb14SJames Hogan 180141cfb14SJames Hogan data = scancode & 0xff; 181141cfb14SJames Hogan 1826d741bfeSSean Young if (protocol == RC_PROTO_NEC32) { 183141cfb14SJames Hogan /* 32-bit NEC (used by Apple and TiVo remotes) */ 184141cfb14SJames Hogan /* scan encoding: aaAAddDD */ 185141cfb14SJames Hogan addr_inv = (scancode >> 24) & 0xff; 186141cfb14SJames Hogan addr = (scancode >> 16) & 0xff; 187141cfb14SJames Hogan data_inv = (scancode >> 8) & 0xff; 1886d741bfeSSean Young } else if (protocol == RC_PROTO_NECX) { 189141cfb14SJames Hogan /* Extended NEC */ 190141cfb14SJames Hogan /* scan encoding AAaaDD */ 191141cfb14SJames Hogan addr = (scancode >> 16) & 0xff; 192141cfb14SJames Hogan addr_inv = (scancode >> 8) & 0xff; 193141cfb14SJames Hogan data_inv = data ^ 0xff; 194141cfb14SJames Hogan } else { 195141cfb14SJames Hogan /* Normal NEC */ 196141cfb14SJames Hogan /* scan encoding: AADD */ 197141cfb14SJames Hogan addr = (scancode >> 8) & 0xff; 198141cfb14SJames Hogan addr_inv = addr ^ 0xff; 199141cfb14SJames Hogan data_inv = data ^ 0xff; 200141cfb14SJames Hogan } 201141cfb14SJames Hogan 202141cfb14SJames Hogan /* raw encoding: ddDDaaAA */ 203141cfb14SJames Hogan return data_inv << 24 | 204141cfb14SJames Hogan data << 16 | 205141cfb14SJames Hogan addr_inv << 8 | 206141cfb14SJames Hogan addr; 207141cfb14SJames Hogan } 208141cfb14SJames Hogan 209141cfb14SJames Hogan static const struct ir_raw_timings_pd ir_nec_timings = { 210141cfb14SJames Hogan .header_pulse = NEC_HEADER_PULSE, 211141cfb14SJames Hogan .header_space = NEC_HEADER_SPACE, 212141cfb14SJames Hogan .bit_pulse = NEC_BIT_PULSE, 213141cfb14SJames Hogan .bit_space[0] = NEC_BIT_0_SPACE, 214141cfb14SJames Hogan .bit_space[1] = NEC_BIT_1_SPACE, 215141cfb14SJames Hogan .trailer_pulse = NEC_TRAILER_PULSE, 216141cfb14SJames Hogan .trailer_space = NEC_TRAILER_SPACE, 217141cfb14SJames Hogan .msb_first = 0, 218141cfb14SJames Hogan }; 219141cfb14SJames Hogan 220141cfb14SJames Hogan /** 221141cfb14SJames Hogan * ir_nec_encode() - Encode a scancode as a stream of raw events 222141cfb14SJames Hogan * 223141cfb14SJames Hogan * @protocol: protocol to encode 224141cfb14SJames Hogan * @scancode: scancode to encode 225141cfb14SJames Hogan * @events: array of raw ir events to write into 226141cfb14SJames Hogan * @max: maximum size of @events 227141cfb14SJames Hogan * 228141cfb14SJames Hogan * Returns: The number of events written. 229141cfb14SJames Hogan * -ENOBUFS if there isn't enough space in the array to fit the 230141cfb14SJames Hogan * encoding. In this case all @max events will have been written. 231141cfb14SJames Hogan */ 2326d741bfeSSean Young static int ir_nec_encode(enum rc_proto protocol, u32 scancode, 233141cfb14SJames Hogan struct ir_raw_event *events, unsigned int max) 234141cfb14SJames Hogan { 235141cfb14SJames Hogan struct ir_raw_event *e = events; 236141cfb14SJames Hogan int ret; 237141cfb14SJames Hogan u32 raw; 238141cfb14SJames Hogan 239141cfb14SJames Hogan /* Convert a NEC scancode to raw NEC data */ 240141cfb14SJames Hogan raw = ir_nec_scancode_to_raw(protocol, scancode); 241141cfb14SJames Hogan 242141cfb14SJames Hogan /* Modulate the raw data using a pulse distance modulation */ 243141cfb14SJames Hogan ret = ir_raw_gen_pd(&e, max, &ir_nec_timings, NEC_NBITS, raw); 244141cfb14SJames Hogan if (ret < 0) 245141cfb14SJames Hogan return ret; 246141cfb14SJames Hogan 247141cfb14SJames Hogan return e - events; 248141cfb14SJames Hogan } 249141cfb14SJames Hogan 25032cf86f6SMauro Carvalho Chehab static struct ir_raw_handler nec_handler = { 2516d741bfeSSean Young .protocols = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | 2526d741bfeSSean Young RC_PROTO_BIT_NEC32, 25332cf86f6SMauro Carvalho Chehab .decode = ir_nec_decode, 254141cfb14SJames Hogan .encode = ir_nec_encode, 255cdfaa01cSSean Young .carrier = 38000, 256a86d6df8SSean Young .min_timeout = NEC_TRAILER_SPACE, 25732cf86f6SMauro Carvalho Chehab }; 25832cf86f6SMauro Carvalho Chehab 25932cf86f6SMauro Carvalho Chehab static int __init ir_nec_decode_init(void) 26032cf86f6SMauro Carvalho Chehab { 26132cf86f6SMauro Carvalho Chehab ir_raw_handler_register(&nec_handler); 26232cf86f6SMauro Carvalho Chehab 26332cf86f6SMauro Carvalho Chehab printk(KERN_INFO "IR NEC protocol handler initialized\n"); 26432cf86f6SMauro Carvalho Chehab return 0; 26532cf86f6SMauro Carvalho Chehab } 26632cf86f6SMauro Carvalho Chehab 26732cf86f6SMauro Carvalho Chehab static void __exit ir_nec_decode_exit(void) 26832cf86f6SMauro Carvalho Chehab { 26932cf86f6SMauro Carvalho Chehab ir_raw_handler_unregister(&nec_handler); 27032cf86f6SMauro Carvalho Chehab } 27132cf86f6SMauro Carvalho Chehab 27232cf86f6SMauro Carvalho Chehab module_init(ir_nec_decode_init); 27332cf86f6SMauro Carvalho Chehab module_exit(ir_nec_decode_exit); 27432cf86f6SMauro Carvalho Chehab 27520835280SMauro Carvalho Chehab MODULE_LICENSE("GPL v2"); 27637e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab"); 27732cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); 27832cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("NEC IR protocol decoder"); 279