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
23f62de675f796a992011c598c405a3d6fada9aa20Mauro Carvalho Chehab#include "rc-core-priv.h"
247a707b89202f905bd9f9fbde326933c59a81214cPaul Gortmaker#include <linux/module.h>
25db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
269b09df51b8c2b4615376e5ada3e2eb7eeed3cf5dMauro Carvalho Chehab#define RC5_NBITS		14
27733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman#define RC5X_NBITS		20
28733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman#define CHECK_RC5X_NBITS	8
29724e2495502a98aaa3f93c404472a991da8ff857David Härdeman#define RC5_UNIT		888888 /* ns */
30e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman#define RC5_BIT_START		(1 * RC5_UNIT)
31e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman#define RC5_BIT_END		(1 * RC5_UNIT)
32e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman#define RC5X_SPACE		(4 * RC5_UNIT)
33db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
34db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabenum rc5_state {
35db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	STATE_INACTIVE,
36724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	STATE_BIT_START,
37724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	STATE_BIT_END,
38733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	STATE_CHECK_RC5X,
39724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	STATE_FINISHED,
40db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab};
41db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
42db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab/**
43724e2495502a98aaa3f93c404472a991da8ff857David Härdeman * ir_rc5_decode() - Decode one RC-5 pulse or space
44d8b4b5822f51e2142b731b42c81e3f03eec475b2David Härdeman * @dev:	the struct rc_dev descriptor of the device
45e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman * @ev:		the struct ir_raw_event descriptor of the pulse/space
46db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab *
47db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab * This function returns -EINVAL if the pulse violates the state machine
48db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab */
49d8b4b5822f51e2142b731b42c81e3f03eec475b2David Härdemanstatic int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
50db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab{
51d8b4b5822f51e2142b731b42c81e3f03eec475b2David Härdeman	struct rc5_dec *data = &dev->raw->rc5;
52733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	u8 toggle;
53724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	u32 scancode;
54db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
5552b661449aecc47e652a164c0d8078b31e10aca0Mauro Carvalho Chehab        if (!(dev->raw->enabled_protocols & RC_TYPE_RC5))
56667c9ebe97f7e5f1e48e7eb321644c6fb1668de5David Härdeman                return 0;
577f20d32d446097789ade5ada6b645742ddac4eceMauro Carvalho Chehab
584651918a4afdd49bdea21d2f919b189ef17a6399Maxim Levitsky	if (!is_timing_event(ev)) {
594651918a4afdd49bdea21d2f919b189ef17a6399Maxim Levitsky		if (ev.reset)
604651918a4afdd49bdea21d2f919b189ef17a6399Maxim Levitsky			data->state = STATE_INACTIVE;
61db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		return 0;
62724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	}
63db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
64e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
65724e2495502a98aaa3f93c404472a991da8ff857David Härdeman		goto out;
66db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
67724e2495502a98aaa3f93c404472a991da8ff857David Härdemanagain:
68e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	IR_dprintk(2, "RC5(x) decode started at state %i (%uus %s)\n",
69e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
70db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
71e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
72724e2495502a98aaa3f93c404472a991da8ff857David Härdeman		return 0;
73db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
74724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	switch (data->state) {
75db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
76724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_INACTIVE:
77e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!ev.pulse)
78e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
79e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
80e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->state = STATE_BIT_START;
81e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->count = 1;
82e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		/* We just need enough bits to get to STATE_CHECK_RC5X */
83e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->wanted_bits = RC5X_NBITS;
84e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		decrease_duration(&ev, RC5_BIT_START);
85e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		goto again;
86724e2495502a98aaa3f93c404472a991da8ff857David Härdeman
87724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_BIT_START:
88e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2))
89e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
90e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
91c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman		data->bits <<= 1;
92e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!ev.pulse)
93c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			data->bits |= 1;
94e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->count++;
95e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		data->state = STATE_BIT_END;
96e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		return 0;
979b09df51b8c2b4615376e5ada3e2eb7eeed3cf5dMauro Carvalho Chehab
98724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_BIT_END:
99d8b4b5822f51e2142b731b42c81e3f03eec475b2David Härdeman		if (!is_transition(&ev, &dev->raw->prev_ev))
100e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
101e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
102e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (data->count == data->wanted_bits)
103e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			data->state = STATE_FINISHED;
104e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		else if (data->count == CHECK_RC5X_NBITS)
105e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			data->state = STATE_CHECK_RC5X;
106e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		else
107e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			data->state = STATE_BIT_START;
108e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
109e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		decrease_duration(&ev, RC5_BIT_END);
110e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		goto again;
111724e2495502a98aaa3f93c404472a991da8ff857David Härdeman
112733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	case STATE_CHECK_RC5X:
113e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (!ev.pulse && geq_margin(ev.duration, RC5X_SPACE, RC5_UNIT / 2)) {
114733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5X */
115733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			data->wanted_bits = RC5X_NBITS;
116e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			decrease_duration(&ev, RC5X_SPACE);
117733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		} else {
118733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5 */
119733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			data->wanted_bits = RC5_NBITS;
120733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		}
121733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		data->state = STATE_BIT_START;
122733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		goto again;
123733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
124724e2495502a98aaa3f93c404472a991da8ff857David Härdeman	case STATE_FINISHED:
125e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		if (ev.pulse)
126e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman			break;
127e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman
128733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		if (data->wanted_bits == RC5X_NBITS) {
129733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5X */
130733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			u8 xdata, command, system;
131c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			xdata    = (data->bits & 0x0003F) >> 0;
132c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command  = (data->bits & 0x00FC0) >> 6;
133c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			system   = (data->bits & 0x1F000) >> 12;
134c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			toggle   = (data->bits & 0x20000) ? 1 : 0;
135c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command += (data->bits & 0x01000) ? 0 : 0x40;
136733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			scancode = system << 16 | command << 8 | xdata;
137733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
138733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n",
139733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman				   scancode, toggle);
140733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
141733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		} else {
142733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			/* RC5 */
143733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			u8 command, system;
144c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command  = (data->bits & 0x0003F) >> 0;
145c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			system   = (data->bits & 0x007C0) >> 6;
146c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			toggle   = (data->bits & 0x00800) ? 1 : 0;
147c216369e61fae586cd48c0913cca2a37fbfeb912David Härdeman			command += (data->bits & 0x01000) ? 0 : 0x40;
148733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			scancode = system << 8 | command;
149733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
150733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman			IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n",
151733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman				   scancode, toggle);
152733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman		}
153733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman
154ca86674b8a93ea11c4bb6f4dd0113b1adf1fa841Mauro Carvalho Chehab		rc_keydown(dev, scancode, toggle);
155db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		data->state = STATE_INACTIVE;
156db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab		return 0;
157db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	}
158db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
159724e2495502a98aaa3f93c404472a991da8ff857David Härdemanout:
160e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman	IR_dprintk(1, "RC5(x) decode failed at state %i (%uus %s)\n",
161e40b1127f994a427568319d1be9b9e5ab1f58dd1David Härdeman		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
162db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	data->state = STATE_INACTIVE;
163db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	return -EINVAL;
164db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab}
165db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
166db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabstatic struct ir_raw_handler rc5_handler = {
16752b661449aecc47e652a164c0d8078b31e10aca0Mauro Carvalho Chehab	.protocols	= RC_TYPE_RC5,
168db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	.decode		= ir_rc5_decode,
169db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab};
170db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
171db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabstatic int __init ir_rc5_decode_init(void)
172db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab{
173db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	ir_raw_handler_register(&rc5_handler);
174db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
175733419b5c4d29186006982a9a27227e214a39dbcDavid Härdeman	printk(KERN_INFO "IR RC5(x) protocol handler initialized\n");
176db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	return 0;
177db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab}
178db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
179db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabstatic void __exit ir_rc5_decode_exit(void)
180db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab{
181db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab	ir_raw_handler_unregister(&rc5_handler);
182db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab}
183db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
184db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabmodule_init(ir_rc5_decode_init);
185db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehabmodule_exit(ir_rc5_decode_exit);
186db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho Chehab
187db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho ChehabMODULE_LICENSE("GPL");
188db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho ChehabMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
189db1423a6c79f66db2b1846614c13bde9c2db7ad2Mauro Carvalho ChehabMODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
190733419b5c4d29186006982a9a27227e214a39dbcDavid HärdemanMODULE_DESCRIPTION("RC5(x) IR protocol decoder");
191