ir-rc5-decoder.c revision c216369e61fae586cd48c0913cca2a37fbfeb912
1733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman/* ir-rc5-decoder.c - handle RC5(x) IR Pulse/Space protocol
2db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *
3db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
4db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *
5db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify
6db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *  it under the terms of the GNU General Public License as published by
7db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *  the Free Software Foundation version 2 of the License.
8db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *
9db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *  This program is distributed in the hope that it will be useful,
10db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *  GNU General Public License for more details.
13db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab */
14db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
15db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab/*
16733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman * This code handles 14 bits RC5 protocols and 20 bits RC5x protocols.
17733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman * There are other variants that use a different number of bits.
18733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman * This is currently unsupported.
19733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman * It considers a carrier of 36 kHz, with a total of 14/20 bits, where
209b09df51b8c2b4615376e5ada3e2eb7eeed3cf5dMauro Carvalho Chehab * the first two bits are start bits, and a third one is a filing bit
21db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab */
22db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
233f113e36106f133752de47208816b28aa8e60f88Mauro Carvalho Chehab#include "ir-core-priv.h"
24db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
259b09df51b8c2b4615376e5ada3e2eb7eeed3cf5dMauro Carvalho Chehab#define RC5_NBITS		14
26733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman#define RC5X_NBITS		20
27733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman#define CHECK_RC5X_NBITS	8
28724e2495502a98aaa3f93c404472a991da8ff857David Härdeman#define RC5_UNIT		888888 /* ns */
29e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman#define RC5_BIT_START		(1 * RC5_UNIT)
30e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman#define RC5_BIT_END		(1 * RC5_UNIT)
31e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman#define RC5X_SPACE		(4 * RC5_UNIT)
32db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
33db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabenum rc5_state {
34db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	STATE_INACTIVE,
35724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	STATE_BIT_START,
36724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	STATE_BIT_END,
37733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	STATE_CHECK_RC5X,
38724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	STATE_FINISHED,
39db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab};
40db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
41db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab/**
42724e2495502a98aaa3f93c404472a991da8ff857David Härdeman * ir_rc5_decode() - Decode one RC-5 pulse or space
43db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab * @input_dev:	the struct input_dev descriptor of the device
44e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman * @ev:		the struct ir_raw_event descriptor of the pulse/space
45db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *
46db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab * This function returns -EINVAL if the pulse violates the state machine
47db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab */
48e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdemanstatic int ir_rc5_decode(struct input_dev *input_dev, struct ir_raw_event ev)
49db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab{
50db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
51c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman	struct rc5_dec *data = &ir_dev->raw->rc5;
52733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	u8 toggle;
53724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	u32 scancode;
54db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
55667c9ebe97f7e5f1e48e7eb321644c6fb1668de5David Härdeman        if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5))
56667c9ebe97f7e5f1e48e7eb321644c6fb1668de5David Härdeman                return 0;
577f20d32d446097789ade5ada6b645742ddac4eceMauro Carvalho Chehab
58e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	if (IS_RESET(ev)) {
59db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		data->state = STATE_INACTIVE;
60db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		return 0;
61724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	}
62db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
63e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
64724e2495502a98aaa3f93c404472a991da8ff857David Härdeman		goto out;
65db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
66724e2495502a98aaa3f93c404472a991da8ff857David Härdemanagain:
67e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	IR_dprintk(2, "RC5(x) decode started at state %i (%uus %s)\n",
68e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
69db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
70e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
71724e2495502a98aaa3f93c404472a991da8ff857David Härdeman		return 0;
72db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
73724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	switch (data->state) {
74db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
75724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_INACTIVE:
76e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!ev.pulse)
77e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
78e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
79e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->state = STATE_BIT_START;
80e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->count = 1;
81e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		/* We just need enough bits to get to STATE_CHECK_RC5X */
82e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->wanted_bits = RC5X_NBITS;
83e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		decrease_duration(&ev, RC5_BIT_START);
84e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		goto again;
85724e2495502a98aaa3f93c404472a991da8ff857David Härdeman
86724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_BIT_START:
87e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2))
88e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
89e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
90c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman		data->bits <<= 1;
91e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!ev.pulse)
92c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			data->bits |= 1;
93e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->count++;
94e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->state = STATE_BIT_END;
95e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		return 0;
969b09df51b8c2b4615376e5ada3e2eb7eeed3cf5dMauro Carvalho Chehab
97724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_BIT_END:
98c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman		if (!is_transition(&ev, &ir_dev->raw->prev_ev))
99e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
100e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
101e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (data->count == data->wanted_bits)
102e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			data->state = STATE_FINISHED;
103e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		else if (data->count == CHECK_RC5X_NBITS)
104e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			data->state = STATE_CHECK_RC5X;
105e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		else
106e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			data->state = STATE_BIT_START;
107e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
108e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		decrease_duration(&ev, RC5_BIT_END);
109e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		goto again;
110724e2495502a98aaa3f93c404472a991da8ff857David Härdeman
111733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	case STATE_CHECK_RC5X:
112e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!ev.pulse && geq_margin(ev.duration, RC5X_SPACE, RC5_UNIT / 2)) {
113733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5X */
114733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			data->wanted_bits = RC5X_NBITS;
115e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			decrease_duration(&ev, RC5X_SPACE);
116733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		} else {
117733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5 */
118733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			data->wanted_bits = RC5_NBITS;
119733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		}
120733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		data->state = STATE_BIT_START;
121733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		goto again;
122733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
123724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_FINISHED:
124e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (ev.pulse)
125e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
126e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
127733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		if (data->wanted_bits == RC5X_NBITS) {
128733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5X */
129733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			u8 xdata, command, system;
130c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			xdata    = (data->bits & 0x0003F) >> 0;
131c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command  = (data->bits & 0x00FC0) >> 6;
132c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			system   = (data->bits & 0x1F000) >> 12;
133c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			toggle   = (data->bits & 0x20000) ? 1 : 0;
134c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command += (data->bits & 0x01000) ? 0 : 0x40;
135733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			scancode = system << 16 | command << 8 | xdata;
136733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
137733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n",
138733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman				   scancode, toggle);
139733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
140733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		} else {
141733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5 */
142733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			u8 command, system;
143c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command  = (data->bits & 0x0003F) >> 0;
144c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			system   = (data->bits & 0x007C0) >> 6;
145c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			toggle   = (data->bits & 0x00800) ? 1 : 0;
146c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command += (data->bits & 0x01000) ? 0 : 0x40;
147733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			scancode = system << 8 | command;
148733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
149733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n",
150733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman				   scancode, toggle);
151733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		}
152733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
153724e2495502a98aaa3f93c404472a991da8ff857David Härdeman		ir_keydown(input_dev, scancode, toggle);
154db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		data->state = STATE_INACTIVE;
155db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		return 0;
156db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	}
157db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
158724e2495502a98aaa3f93c404472a991da8ff857David Härdemanout:
159e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	IR_dprintk(1, "RC5(x) decode failed at state %i (%uus %s)\n",
160e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
161db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	data->state = STATE_INACTIVE;
162db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	return -EINVAL;
163db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab}
164db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
165db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabstatic struct ir_raw_handler rc5_handler = {
166667c9ebe97f7e5f1e48e7eb321644c6fb1668de5David Härdeman	.protocols	= IR_TYPE_RC5,
167db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	.decode		= ir_rc5_decode,
168db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab};
169db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
170db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabstatic int __init ir_rc5_decode_init(void)
171db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab{
172db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	ir_raw_handler_register(&rc5_handler);
173db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
174733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	printk(KERN_INFO "IR RC5(x) protocol handler initialized\n");
175db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	return 0;
176db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab}
177db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
178db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabstatic void __exit ir_rc5_decode_exit(void)
179db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab{
180db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	ir_raw_handler_unregister(&rc5_handler);
181db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab}
182db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
183db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabmodule_init(ir_rc5_decode_init);
184db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabmodule_exit(ir_rc5_decode_exit);
185db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
186db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho ChehabMODULE_LICENSE("GPL");
187db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho ChehabMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
188db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho ChehabMODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
189733419b5c4d29186006982a9a27227e214a39dbcDavid HärdemanMODULE_DESCRIPTION("RC5(x) IR protocol decoder");
190