132cf86f6SMauro Carvalho Chehab /* ir-sony-decoder.c - handle Sony IR Pulse/Space protocol 232cf86f6SMauro Carvalho Chehab * 332cf86f6SMauro Carvalho Chehab * Copyright (C) 2010 by David Härdeman <david@hardeman.nu> 432cf86f6SMauro Carvalho Chehab * 532cf86f6SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 632cf86f6SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 732cf86f6SMauro Carvalho Chehab * the Free Software Foundation version 2 of the License. 832cf86f6SMauro Carvalho Chehab * 932cf86f6SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 1032cf86f6SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 1132cf86f6SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1232cf86f6SMauro Carvalho Chehab * GNU General Public License for more details. 1332cf86f6SMauro Carvalho Chehab */ 1432cf86f6SMauro Carvalho Chehab 1532cf86f6SMauro Carvalho Chehab #include <linux/bitrev.h> 167a707b89SPaul Gortmaker #include <linux/module.h> 17f62de675SMauro Carvalho Chehab #include "rc-core-priv.h" 1832cf86f6SMauro Carvalho Chehab 1932cf86f6SMauro Carvalho Chehab #define SONY_UNIT 600000 /* ns */ 2032cf86f6SMauro Carvalho Chehab #define SONY_HEADER_PULSE (4 * SONY_UNIT) 2132cf86f6SMauro Carvalho Chehab #define SONY_HEADER_SPACE (1 * SONY_UNIT) 2232cf86f6SMauro Carvalho Chehab #define SONY_BIT_0_PULSE (1 * SONY_UNIT) 2332cf86f6SMauro Carvalho Chehab #define SONY_BIT_1_PULSE (2 * SONY_UNIT) 2432cf86f6SMauro Carvalho Chehab #define SONY_BIT_SPACE (1 * SONY_UNIT) 2532cf86f6SMauro Carvalho Chehab #define SONY_TRAILER_SPACE (10 * SONY_UNIT) /* minimum */ 2632cf86f6SMauro Carvalho Chehab 2732cf86f6SMauro Carvalho Chehab enum sony_state { 2832cf86f6SMauro Carvalho Chehab STATE_INACTIVE, 2932cf86f6SMauro Carvalho Chehab STATE_HEADER_SPACE, 3032cf86f6SMauro Carvalho Chehab STATE_BIT_PULSE, 3132cf86f6SMauro Carvalho Chehab STATE_BIT_SPACE, 3232cf86f6SMauro Carvalho Chehab STATE_FINISHED, 3332cf86f6SMauro Carvalho Chehab }; 3432cf86f6SMauro Carvalho Chehab 3532cf86f6SMauro Carvalho Chehab /** 3632cf86f6SMauro Carvalho Chehab * ir_sony_decode() - Decode one Sony pulse or space 37d8b4b582SDavid Härdeman * @dev: the struct rc_dev descriptor of the device 3832cf86f6SMauro Carvalho Chehab * @ev: the struct ir_raw_event descriptor of the pulse/space 3932cf86f6SMauro Carvalho Chehab * 4032cf86f6SMauro Carvalho Chehab * This function returns -EINVAL if the pulse violates the state machine 4132cf86f6SMauro Carvalho Chehab */ 42d8b4b582SDavid Härdeman static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) 4332cf86f6SMauro Carvalho Chehab { 44d8b4b582SDavid Härdeman struct sony_dec *data = &dev->raw->sony; 456d741bfeSSean Young enum rc_proto protocol; 4632cf86f6SMauro Carvalho Chehab u32 scancode; 4732cf86f6SMauro Carvalho Chehab u8 device, subdevice, function; 4832cf86f6SMauro Carvalho Chehab 4932cf86f6SMauro Carvalho Chehab if (!is_timing_event(ev)) { 5032cf86f6SMauro Carvalho Chehab if (ev.reset) 5132cf86f6SMauro Carvalho Chehab data->state = STATE_INACTIVE; 5232cf86f6SMauro Carvalho Chehab return 0; 5332cf86f6SMauro Carvalho Chehab } 5432cf86f6SMauro Carvalho Chehab 5532cf86f6SMauro Carvalho Chehab if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2)) 5632cf86f6SMauro Carvalho Chehab goto out; 5732cf86f6SMauro Carvalho Chehab 5850078a90SSean Young dev_dbg(&dev->dev, "Sony decode started at state %d (%uus %s)\n", 5932cf86f6SMauro Carvalho Chehab data->state, TO_US(ev.duration), TO_STR(ev.pulse)); 6032cf86f6SMauro Carvalho Chehab 6132cf86f6SMauro Carvalho Chehab switch (data->state) { 6232cf86f6SMauro Carvalho Chehab 6332cf86f6SMauro Carvalho Chehab case STATE_INACTIVE: 6432cf86f6SMauro Carvalho Chehab if (!ev.pulse) 6532cf86f6SMauro Carvalho Chehab break; 6632cf86f6SMauro Carvalho Chehab 6732cf86f6SMauro Carvalho Chehab if (!eq_margin(ev.duration, SONY_HEADER_PULSE, SONY_UNIT / 2)) 6832cf86f6SMauro Carvalho Chehab break; 6932cf86f6SMauro Carvalho Chehab 7032cf86f6SMauro Carvalho Chehab data->count = 0; 7132cf86f6SMauro Carvalho Chehab data->state = STATE_HEADER_SPACE; 7232cf86f6SMauro Carvalho Chehab return 0; 7332cf86f6SMauro Carvalho Chehab 7432cf86f6SMauro Carvalho Chehab case STATE_HEADER_SPACE: 7532cf86f6SMauro Carvalho Chehab if (ev.pulse) 7632cf86f6SMauro Carvalho Chehab break; 7732cf86f6SMauro Carvalho Chehab 7832cf86f6SMauro Carvalho Chehab if (!eq_margin(ev.duration, SONY_HEADER_SPACE, SONY_UNIT / 2)) 7932cf86f6SMauro Carvalho Chehab break; 8032cf86f6SMauro Carvalho Chehab 8132cf86f6SMauro Carvalho Chehab data->state = STATE_BIT_PULSE; 8232cf86f6SMauro Carvalho Chehab return 0; 8332cf86f6SMauro Carvalho Chehab 8432cf86f6SMauro Carvalho Chehab case STATE_BIT_PULSE: 8532cf86f6SMauro Carvalho Chehab if (!ev.pulse) 8632cf86f6SMauro Carvalho Chehab break; 8732cf86f6SMauro Carvalho Chehab 8832cf86f6SMauro Carvalho Chehab data->bits <<= 1; 8932cf86f6SMauro Carvalho Chehab if (eq_margin(ev.duration, SONY_BIT_1_PULSE, SONY_UNIT / 2)) 9032cf86f6SMauro Carvalho Chehab data->bits |= 1; 9132cf86f6SMauro Carvalho Chehab else if (!eq_margin(ev.duration, SONY_BIT_0_PULSE, SONY_UNIT / 2)) 9232cf86f6SMauro Carvalho Chehab break; 9332cf86f6SMauro Carvalho Chehab 9432cf86f6SMauro Carvalho Chehab data->count++; 9532cf86f6SMauro Carvalho Chehab data->state = STATE_BIT_SPACE; 9632cf86f6SMauro Carvalho Chehab return 0; 9732cf86f6SMauro Carvalho Chehab 9832cf86f6SMauro Carvalho Chehab case STATE_BIT_SPACE: 9932cf86f6SMauro Carvalho Chehab if (ev.pulse) 10032cf86f6SMauro Carvalho Chehab break; 10132cf86f6SMauro Carvalho Chehab 10232cf86f6SMauro Carvalho Chehab if (!geq_margin(ev.duration, SONY_BIT_SPACE, SONY_UNIT / 2)) 10332cf86f6SMauro Carvalho Chehab break; 10432cf86f6SMauro Carvalho Chehab 10532cf86f6SMauro Carvalho Chehab decrease_duration(&ev, SONY_BIT_SPACE); 10632cf86f6SMauro Carvalho Chehab 10732cf86f6SMauro Carvalho Chehab if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2)) { 10832cf86f6SMauro Carvalho Chehab data->state = STATE_BIT_PULSE; 10932cf86f6SMauro Carvalho Chehab return 0; 11032cf86f6SMauro Carvalho Chehab } 11132cf86f6SMauro Carvalho Chehab 11232cf86f6SMauro Carvalho Chehab data->state = STATE_FINISHED; 11332cf86f6SMauro Carvalho Chehab /* Fall through */ 11432cf86f6SMauro Carvalho Chehab 11532cf86f6SMauro Carvalho Chehab case STATE_FINISHED: 11632cf86f6SMauro Carvalho Chehab if (ev.pulse) 11732cf86f6SMauro Carvalho Chehab break; 11832cf86f6SMauro Carvalho Chehab 11932cf86f6SMauro Carvalho Chehab if (!geq_margin(ev.duration, SONY_TRAILER_SPACE, SONY_UNIT / 2)) 12032cf86f6SMauro Carvalho Chehab break; 12132cf86f6SMauro Carvalho Chehab 12232cf86f6SMauro Carvalho Chehab switch (data->count) { 12332cf86f6SMauro Carvalho Chehab case 12: 1246d741bfeSSean Young if (!(dev->enabled_protocols & RC_PROTO_BIT_SONY12)) 125abefe12aSMauro Carvalho Chehab goto finish_state_machine; 126abefe12aSMauro Carvalho Chehab 12732cf86f6SMauro Carvalho Chehab device = bitrev8((data->bits << 3) & 0xF8); 12832cf86f6SMauro Carvalho Chehab subdevice = 0; 12932cf86f6SMauro Carvalho Chehab function = bitrev8((data->bits >> 4) & 0xFE); 1306d741bfeSSean Young protocol = RC_PROTO_SONY12; 13132cf86f6SMauro Carvalho Chehab break; 13232cf86f6SMauro Carvalho Chehab case 15: 1336d741bfeSSean Young if (!(dev->enabled_protocols & RC_PROTO_BIT_SONY15)) 134abefe12aSMauro Carvalho Chehab goto finish_state_machine; 135abefe12aSMauro Carvalho Chehab 13632cf86f6SMauro Carvalho Chehab device = bitrev8((data->bits >> 0) & 0xFF); 13732cf86f6SMauro Carvalho Chehab subdevice = 0; 138ec9ee8e2SJames Hogan function = bitrev8((data->bits >> 7) & 0xFE); 1396d741bfeSSean Young protocol = RC_PROTO_SONY15; 14032cf86f6SMauro Carvalho Chehab break; 14132cf86f6SMauro Carvalho Chehab case 20: 1426d741bfeSSean Young if (!(dev->enabled_protocols & RC_PROTO_BIT_SONY20)) 143abefe12aSMauro Carvalho Chehab goto finish_state_machine; 144abefe12aSMauro Carvalho Chehab 14532cf86f6SMauro Carvalho Chehab device = bitrev8((data->bits >> 5) & 0xF8); 14632cf86f6SMauro Carvalho Chehab subdevice = bitrev8((data->bits >> 0) & 0xFF); 14732cf86f6SMauro Carvalho Chehab function = bitrev8((data->bits >> 12) & 0xFE); 1486d741bfeSSean Young protocol = RC_PROTO_SONY20; 14932cf86f6SMauro Carvalho Chehab break; 15032cf86f6SMauro Carvalho Chehab default: 15150078a90SSean Young dev_dbg(&dev->dev, "Sony invalid bitcount %u\n", 15250078a90SSean Young data->count); 15332cf86f6SMauro Carvalho Chehab goto out; 15432cf86f6SMauro Carvalho Chehab } 15532cf86f6SMauro Carvalho Chehab 15632cf86f6SMauro Carvalho Chehab scancode = device << 16 | subdevice << 8 | function; 15750078a90SSean Young dev_dbg(&dev->dev, "Sony(%u) scancode 0x%05x\n", data->count, 15850078a90SSean Young scancode); 159120703f9SDavid Härdeman rc_keydown(dev, protocol, scancode, 0); 160abefe12aSMauro Carvalho Chehab goto finish_state_machine; 16132cf86f6SMauro Carvalho Chehab } 16232cf86f6SMauro Carvalho Chehab 16332cf86f6SMauro Carvalho Chehab out: 16450078a90SSean Young dev_dbg(&dev->dev, "Sony decode failed at state %d (%uus %s)\n", 16532cf86f6SMauro Carvalho Chehab data->state, TO_US(ev.duration), TO_STR(ev.pulse)); 16632cf86f6SMauro Carvalho Chehab data->state = STATE_INACTIVE; 16732cf86f6SMauro Carvalho Chehab return -EINVAL; 168abefe12aSMauro Carvalho Chehab 169abefe12aSMauro Carvalho Chehab finish_state_machine: 170abefe12aSMauro Carvalho Chehab data->state = STATE_INACTIVE; 171abefe12aSMauro Carvalho Chehab return 0; 17232cf86f6SMauro Carvalho Chehab } 17332cf86f6SMauro Carvalho Chehab 174103293beSSean Young static const struct ir_raw_timings_pl ir_sony_timings = { 175103293beSSean Young .header_pulse = SONY_HEADER_PULSE, 176103293beSSean Young .bit_space = SONY_BIT_SPACE, 177103293beSSean Young .bit_pulse[0] = SONY_BIT_0_PULSE, 178103293beSSean Young .bit_pulse[1] = SONY_BIT_1_PULSE, 179103293beSSean Young .trailer_space = SONY_TRAILER_SPACE + SONY_BIT_SPACE, 180103293beSSean Young .msb_first = 0, 181103293beSSean Young }; 182103293beSSean Young 183103293beSSean Young /** 184103293beSSean Young * ir_sony_encode() - Encode a scancode as a stream of raw events 185103293beSSean Young * 186103293beSSean Young * @protocol: protocol to encode 187103293beSSean Young * @scancode: scancode to encode 188103293beSSean Young * @events: array of raw ir events to write into 189103293beSSean Young * @max: maximum size of @events 190103293beSSean Young * 191103293beSSean Young * Returns: The number of events written. 192103293beSSean Young * -ENOBUFS if there isn't enough space in the array to fit the 193103293beSSean Young * encoding. In this case all @max events will have been written. 194103293beSSean Young */ 1956d741bfeSSean Young static int ir_sony_encode(enum rc_proto protocol, u32 scancode, 196103293beSSean Young struct ir_raw_event *events, unsigned int max) 197103293beSSean Young { 198103293beSSean Young struct ir_raw_event *e = events; 199103293beSSean Young u32 raw, len; 200103293beSSean Young int ret; 201103293beSSean Young 2026d741bfeSSean Young if (protocol == RC_PROTO_SONY12) { 203103293beSSean Young raw = (scancode & 0x7f) | ((scancode & 0x1f0000) >> 9); 204103293beSSean Young len = 12; 2056d741bfeSSean Young } else if (protocol == RC_PROTO_SONY15) { 206103293beSSean Young raw = (scancode & 0x7f) | ((scancode & 0xff0000) >> 9); 207103293beSSean Young len = 15; 208103293beSSean Young } else { 209103293beSSean Young raw = (scancode & 0x7f) | ((scancode & 0x1f0000) >> 9) | 210103293beSSean Young ((scancode & 0xff00) << 4); 211103293beSSean Young len = 20; 212103293beSSean Young } 213103293beSSean Young 214103293beSSean Young ret = ir_raw_gen_pl(&e, max, &ir_sony_timings, len, raw); 215103293beSSean Young if (ret < 0) 216103293beSSean Young return ret; 217103293beSSean Young 218103293beSSean Young return e - events; 219103293beSSean Young } 220103293beSSean Young 22132cf86f6SMauro Carvalho Chehab static struct ir_raw_handler sony_handler = { 2226d741bfeSSean Young .protocols = RC_PROTO_BIT_SONY12 | RC_PROTO_BIT_SONY15 | 2236d741bfeSSean Young RC_PROTO_BIT_SONY20, 22432cf86f6SMauro Carvalho Chehab .decode = ir_sony_decode, 225103293beSSean Young .encode = ir_sony_encode, 226cdfaa01cSSean Young .carrier = 40000, 22732cf86f6SMauro Carvalho Chehab }; 22832cf86f6SMauro Carvalho Chehab 22932cf86f6SMauro Carvalho Chehab static int __init ir_sony_decode_init(void) 23032cf86f6SMauro Carvalho Chehab { 23132cf86f6SMauro Carvalho Chehab ir_raw_handler_register(&sony_handler); 23232cf86f6SMauro Carvalho Chehab 23332cf86f6SMauro Carvalho Chehab printk(KERN_INFO "IR Sony protocol handler initialized\n"); 23432cf86f6SMauro Carvalho Chehab return 0; 23532cf86f6SMauro Carvalho Chehab } 23632cf86f6SMauro Carvalho Chehab 23732cf86f6SMauro Carvalho Chehab static void __exit ir_sony_decode_exit(void) 23832cf86f6SMauro Carvalho Chehab { 23932cf86f6SMauro Carvalho Chehab ir_raw_handler_unregister(&sony_handler); 24032cf86f6SMauro Carvalho Chehab } 24132cf86f6SMauro Carvalho Chehab 24232cf86f6SMauro Carvalho Chehab module_init(ir_sony_decode_init); 24332cf86f6SMauro Carvalho Chehab module_exit(ir_sony_decode_exit); 24432cf86f6SMauro Carvalho Chehab 24532cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 24632cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); 24732cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("Sony IR protocol decoder"); 248