| Mauro Carvalho Chehab | b32e724 | 2011-11-23 12:04:08 -0300 | [diff] [blame] | 1 | /* ir-sanyo-decoder.c - handle SANYO IR Pulse/Space protocol | 
|  | 2 | * | 
|  | 3 | * Copyright (C) 2011 by Mauro Carvalho Chehab <mchehab@redhat.com> | 
|  | 4 | * | 
|  | 5 | * This program is free software; you can redistribute it and/or modify | 
|  | 6 | *  it under the terms of the GNU General Public License as published by | 
|  | 7 | *  the Free Software Foundation version 2 of the License. | 
|  | 8 | * | 
|  | 9 | *  This program is distributed in the hope that it will be useful, | 
|  | 10 | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11 | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12 | *  GNU General Public License for more details. | 
|  | 13 | * | 
|  | 14 | * This protocol uses the NEC protocol timings. However, data is formatted as: | 
|  | 15 | *	13 bits Custom Code | 
|  | 16 | *	13 bits NOT(Custom Code) | 
|  | 17 | *	8 bits Key data | 
|  | 18 | *	8 bits NOT(Key data) | 
|  | 19 | * | 
|  | 20 | * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon | 
|  | 21 | * Information for this protocol is available at the Sanyo LC7461 datasheet. | 
|  | 22 | */ | 
|  | 23 |  | 
| Hans Verkuil | 2e962f4 | 2011-11-24 08:47:51 -0300 | [diff] [blame] | 24 | #include <linux/module.h> | 
| Mauro Carvalho Chehab | b32e724 | 2011-11-23 12:04:08 -0300 | [diff] [blame] | 25 | #include <linux/bitrev.h> | 
|  | 26 | #include "rc-core-priv.h" | 
|  | 27 |  | 
|  | 28 | #define SANYO_NBITS		(13+13+8+8) | 
|  | 29 | #define SANYO_UNIT		562500  /* ns */ | 
|  | 30 | #define SANYO_HEADER_PULSE	(16  * SANYO_UNIT) | 
|  | 31 | #define SANYO_HEADER_SPACE	(8   * SANYO_UNIT) | 
|  | 32 | #define SANYO_BIT_PULSE		(1   * SANYO_UNIT) | 
|  | 33 | #define SANYO_BIT_0_SPACE	(1   * SANYO_UNIT) | 
|  | 34 | #define SANYO_BIT_1_SPACE	(3   * SANYO_UNIT) | 
|  | 35 | #define SANYO_REPEAT_SPACE	(150 * SANYO_UNIT) | 
|  | 36 | #define	SANYO_TRAILER_PULSE	(1   * SANYO_UNIT) | 
|  | 37 | #define	SANYO_TRAILER_SPACE	(10  * SANYO_UNIT)	/* in fact, 42 */ | 
|  | 38 |  | 
|  | 39 | enum sanyo_state { | 
|  | 40 | STATE_INACTIVE, | 
|  | 41 | STATE_HEADER_SPACE, | 
|  | 42 | STATE_BIT_PULSE, | 
|  | 43 | STATE_BIT_SPACE, | 
|  | 44 | STATE_TRAILER_PULSE, | 
|  | 45 | STATE_TRAILER_SPACE, | 
|  | 46 | }; | 
|  | 47 |  | 
|  | 48 | /** | 
|  | 49 | * ir_sanyo_decode() - Decode one SANYO pulse or space | 
|  | 50 | * @dev:	the struct rc_dev descriptor of the device | 
|  | 51 | * @duration:	the struct ir_raw_event descriptor of the pulse/space | 
|  | 52 | * | 
|  | 53 | * This function returns -EINVAL if the pulse violates the state machine | 
|  | 54 | */ | 
|  | 55 | static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) | 
|  | 56 | { | 
|  | 57 | struct sanyo_dec *data = &dev->raw->sanyo; | 
|  | 58 | u32 scancode; | 
| Hans Verkuil | 5becbc5 | 2012-05-14 10:22:58 -0300 | [diff] [blame] | 59 | u8 address, command, not_command; | 
| Mauro Carvalho Chehab | b32e724 | 2011-11-23 12:04:08 -0300 | [diff] [blame] | 60 |  | 
| David Härdeman | 9719afa | 2013-03-06 16:52:05 -0300 | [diff] [blame] | 61 | if (!(dev->enabled_protocols & RC_BIT_SANYO)) | 
| Mauro Carvalho Chehab | b32e724 | 2011-11-23 12:04:08 -0300 | [diff] [blame] | 62 | return 0; | 
|  | 63 |  | 
|  | 64 | if (!is_timing_event(ev)) { | 
|  | 65 | if (ev.reset) { | 
|  | 66 | IR_dprintk(1, "SANYO event reset received. reset to state 0\n"); | 
|  | 67 | data->state = STATE_INACTIVE; | 
|  | 68 | } | 
|  | 69 | return 0; | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | IR_dprintk(2, "SANYO decode started at state %d (%uus %s)\n", | 
|  | 73 | data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | 
|  | 74 |  | 
|  | 75 | switch (data->state) { | 
|  | 76 |  | 
|  | 77 | case STATE_INACTIVE: | 
|  | 78 | if (!ev.pulse) | 
|  | 79 | break; | 
|  | 80 |  | 
|  | 81 | if (eq_margin(ev.duration, SANYO_HEADER_PULSE, SANYO_UNIT / 2)) { | 
|  | 82 | data->count = 0; | 
|  | 83 | data->state = STATE_HEADER_SPACE; | 
|  | 84 | return 0; | 
|  | 85 | } | 
|  | 86 | break; | 
|  | 87 |  | 
|  | 88 |  | 
|  | 89 | case STATE_HEADER_SPACE: | 
|  | 90 | if (ev.pulse) | 
|  | 91 | break; | 
|  | 92 |  | 
|  | 93 | if (eq_margin(ev.duration, SANYO_HEADER_SPACE, SANYO_UNIT / 2)) { | 
|  | 94 | data->state = STATE_BIT_PULSE; | 
|  | 95 | return 0; | 
|  | 96 | } | 
|  | 97 |  | 
|  | 98 | break; | 
|  | 99 |  | 
|  | 100 | case STATE_BIT_PULSE: | 
|  | 101 | if (!ev.pulse) | 
|  | 102 | break; | 
|  | 103 |  | 
|  | 104 | if (!eq_margin(ev.duration, SANYO_BIT_PULSE, SANYO_UNIT / 2)) | 
|  | 105 | break; | 
|  | 106 |  | 
|  | 107 | data->state = STATE_BIT_SPACE; | 
|  | 108 | return 0; | 
|  | 109 |  | 
|  | 110 | case STATE_BIT_SPACE: | 
|  | 111 | if (ev.pulse) | 
|  | 112 | break; | 
|  | 113 |  | 
|  | 114 | if (!data->count && geq_margin(ev.duration, SANYO_REPEAT_SPACE, SANYO_UNIT / 2)) { | 
|  | 115 | if (!dev->keypressed) { | 
|  | 116 | IR_dprintk(1, "SANYO discarding last key repeat: event after key up\n"); | 
|  | 117 | } else { | 
|  | 118 | rc_repeat(dev); | 
|  | 119 | IR_dprintk(1, "SANYO repeat last key\n"); | 
|  | 120 | data->state = STATE_INACTIVE; | 
|  | 121 | } | 
|  | 122 | return 0; | 
|  | 123 | } | 
|  | 124 |  | 
|  | 125 | data->bits <<= 1; | 
|  | 126 | if (eq_margin(ev.duration, SANYO_BIT_1_SPACE, SANYO_UNIT / 2)) | 
|  | 127 | data->bits |= 1; | 
|  | 128 | else if (!eq_margin(ev.duration, SANYO_BIT_0_SPACE, SANYO_UNIT / 2)) | 
|  | 129 | break; | 
|  | 130 | data->count++; | 
|  | 131 |  | 
|  | 132 | if (data->count == SANYO_NBITS) | 
|  | 133 | data->state = STATE_TRAILER_PULSE; | 
|  | 134 | else | 
|  | 135 | data->state = STATE_BIT_PULSE; | 
|  | 136 |  | 
|  | 137 | return 0; | 
|  | 138 |  | 
|  | 139 | case STATE_TRAILER_PULSE: | 
|  | 140 | if (!ev.pulse) | 
|  | 141 | break; | 
|  | 142 |  | 
|  | 143 | if (!eq_margin(ev.duration, SANYO_TRAILER_PULSE, SANYO_UNIT / 2)) | 
|  | 144 | break; | 
|  | 145 |  | 
|  | 146 | data->state = STATE_TRAILER_SPACE; | 
|  | 147 | return 0; | 
|  | 148 |  | 
|  | 149 | case STATE_TRAILER_SPACE: | 
|  | 150 | if (ev.pulse) | 
|  | 151 | break; | 
|  | 152 |  | 
|  | 153 | if (!geq_margin(ev.duration, SANYO_TRAILER_SPACE, SANYO_UNIT / 2)) | 
|  | 154 | break; | 
|  | 155 |  | 
|  | 156 | address     = bitrev16((data->bits >> 29) & 0x1fff) >> 3; | 
| Hans Verkuil | 5becbc5 | 2012-05-14 10:22:58 -0300 | [diff] [blame] | 157 | /* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */ | 
| Mauro Carvalho Chehab | b32e724 | 2011-11-23 12:04:08 -0300 | [diff] [blame] | 158 | command	    = bitrev8((data->bits >>  8) & 0xff); | 
|  | 159 | not_command = bitrev8((data->bits >>  0) & 0xff); | 
|  | 160 |  | 
|  | 161 | if ((command ^ not_command) != 0xff) { | 
|  | 162 | IR_dprintk(1, "SANYO checksum error: received 0x%08Lx\n", | 
|  | 163 | data->bits); | 
|  | 164 | data->state = STATE_INACTIVE; | 
|  | 165 | return 0; | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | scancode = address << 8 | command; | 
|  | 169 | IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode); | 
|  | 170 | rc_keydown(dev, scancode, 0); | 
|  | 171 | data->state = STATE_INACTIVE; | 
|  | 172 | return 0; | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | IR_dprintk(1, "SANYO decode failed at count %d state %d (%uus %s)\n", | 
|  | 176 | data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | 
|  | 177 | data->state = STATE_INACTIVE; | 
|  | 178 | return -EINVAL; | 
|  | 179 | } | 
|  | 180 |  | 
|  | 181 | static struct ir_raw_handler sanyo_handler = { | 
| David Härdeman | c003ab1 | 2012-10-11 19:11:54 -0300 | [diff] [blame] | 182 | .protocols	= RC_BIT_SANYO, | 
| Mauro Carvalho Chehab | b32e724 | 2011-11-23 12:04:08 -0300 | [diff] [blame] | 183 | .decode		= ir_sanyo_decode, | 
|  | 184 | }; | 
|  | 185 |  | 
|  | 186 | static int __init ir_sanyo_decode_init(void) | 
|  | 187 | { | 
|  | 188 | ir_raw_handler_register(&sanyo_handler); | 
|  | 189 |  | 
|  | 190 | printk(KERN_INFO "IR SANYO protocol handler initialized\n"); | 
|  | 191 | return 0; | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | static void __exit ir_sanyo_decode_exit(void) | 
|  | 195 | { | 
|  | 196 | ir_raw_handler_unregister(&sanyo_handler); | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | module_init(ir_sanyo_decode_init); | 
|  | 200 | module_exit(ir_sanyo_decode_exit); | 
|  | 201 |  | 
|  | 202 | MODULE_LICENSE("GPL"); | 
|  | 203 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); | 
|  | 204 | MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); | 
|  | 205 | MODULE_DESCRIPTION("SANYO IR protocol decoder"); |