1// SPDX-License-Identifier: GPL-2.0+
2// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol
3//
4// Copyright (C) 2018 by Patrick Lerda <patrick9876@free.fr>
5
6#include "rc-core-priv.h"
7#include <linux/module.h>
8
9#define RCMM_UNIT		166  /* microseconds */
10#define RCMM_PREFIX_PULSE	417  /* 166.666666666666*2.5 */
11#define RCMM_PULSE_0            278  /* 166.666666666666*(1+2/3) */
12#define RCMM_PULSE_1            444  /* 166.666666666666*(2+2/3) */
13#define RCMM_PULSE_2            611  /* 166.666666666666*(3+2/3) */
14#define RCMM_PULSE_3            778  /* 166.666666666666*(4+2/3) */
15
16enum rcmm_state {
17	STATE_INACTIVE,
18	STATE_LOW,
19	STATE_BUMP,
20	STATE_VALUE,
21	STATE_FINISHED,
22};
23
24static bool rcmm_mode(const struct rcmm_dec *data)
25{
26	return !((0x000c0000 & data->bits) == 0x000c0000);
27}
28
29static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data)
30{
31	switch (data->count) {
32	case 24:
33		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) {
34			rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0);
35			data->state = STATE_INACTIVE;
36			return 0;
37		}
38		return -1;
39
40	case 12:
41		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) {
42			rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0);
43			data->state = STATE_INACTIVE;
44			return 0;
45		}
46		return -1;
47	}
48
49	return -1;
50}
51
52/**
53 * ir_rcmm_decode() - Decode one RCMM pulse or space
54 * @dev:	the struct rc_dev descriptor of the device
55 * @ev:		the struct ir_raw_event descriptor of the pulse/space
56 *
57 * This function returns -EINVAL if the pulse violates the state machine
58 */
59static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev)
60{
61	struct rcmm_dec *data = &dev->raw->rcmm;
62	u32 scancode;
63	u8 toggle;
64	int value;
65
66	if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 |
67					RC_PROTO_BIT_RCMM24 |
68					RC_PROTO_BIT_RCMM12)))
69		return 0;
70
71	if (!is_timing_event(ev)) {
72		if (ev.overflow)
73			data->state = STATE_INACTIVE;
74		return 0;
75	}
76
77	switch (data->state) {
78	case STATE_INACTIVE:
79		if (!ev.pulse)
80			break;
81
82		if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT))
83			break;
84
85		data->state = STATE_LOW;
86		data->count = 0;
87		data->bits  = 0;
88		return 0;
89
90	case STATE_LOW:
91		if (ev.pulse)
92			break;
93
94		if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT))
95			break;
96
97		data->state = STATE_BUMP;
98		return 0;
99
100	case STATE_BUMP:
101		if (!ev.pulse)
102			break;
103
104		if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
105			break;
106
107		data->state = STATE_VALUE;
108		return 0;
109
110	case STATE_VALUE:
111		if (ev.pulse)
112			break;
113
114		if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
115			value = 0;
116		else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2))
117			value = 1;
118		else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2))
119			value = 2;
120		else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2))
121			value = 3;
122		else
123			value = -1;
124
125		if (value == -1) {
126			if (!rcmm_miscmode(dev, data))
127				return 0;
128			break;
129		}
130
131		data->bits <<= 2;
132		data->bits |= value;
133
134		data->count += 2;
135
136		if (data->count < 32)
137			data->state = STATE_BUMP;
138		else
139			data->state = STATE_FINISHED;
140
141		return 0;
142
143	case STATE_FINISHED:
144		if (!ev.pulse)
145			break;
146
147		if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
148			break;
149
150		if (rcmm_mode(data)) {
151			toggle = !!(0x8000 & data->bits);
152			scancode = data->bits & ~0x8000;
153		} else {
154			toggle = 0;
155			scancode = data->bits;
156		}
157
158		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) {
159			rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle);
160			data->state = STATE_INACTIVE;
161			return 0;
162		}
163
164		break;
165	}
166
167	dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n",
168		data->count, data->state, ev.duration, TO_STR(ev.pulse));
169	data->state = STATE_INACTIVE;
170	return -EINVAL;
171}
172
173static const int rcmmspace[] = {
174	RCMM_PULSE_0,
175	RCMM_PULSE_1,
176	RCMM_PULSE_2,
177	RCMM_PULSE_3,
178};
179
180static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max,
181			      unsigned int n, u32 data)
182{
183	int i;
184	int ret;
185
186	ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0);
187	if (ret)
188		return ret;
189
190	for (i = n - 2; i >= 0; i -= 2) {
191		const unsigned int space = rcmmspace[(data >> i) & 3];
192
193		ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space);
194		if (ret)
195			return ret;
196	}
197
198	return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2);
199}
200
201static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode,
202			  struct ir_raw_event *events, unsigned int max)
203{
204	struct ir_raw_event *e = events;
205	int ret;
206
207	switch (protocol) {
208	case RC_PROTO_RCMM32:
209		ret = ir_rcmm_rawencoder(&e, max, 32, scancode);
210		break;
211	case RC_PROTO_RCMM24:
212		ret = ir_rcmm_rawencoder(&e, max, 24, scancode);
213		break;
214	case RC_PROTO_RCMM12:
215		ret = ir_rcmm_rawencoder(&e, max, 12, scancode);
216		break;
217	default:
218		ret = -EINVAL;
219	}
220
221	if (ret < 0)
222		return ret;
223
224	return e - events;
225}
226
227static struct ir_raw_handler rcmm_handler = {
228	.protocols	= RC_PROTO_BIT_RCMM32 |
229			  RC_PROTO_BIT_RCMM24 |
230			  RC_PROTO_BIT_RCMM12,
231	.decode		= ir_rcmm_decode,
232	.encode         = ir_rcmm_encode,
233	.carrier        = 36000,
234	.min_timeout	= RCMM_PULSE_3 + RCMM_UNIT,
235};
236
237static int __init ir_rcmm_decode_init(void)
238{
239	ir_raw_handler_register(&rcmm_handler);
240
241	pr_info("IR RCMM protocol handler initialized\n");
242	return 0;
243}
244
245static void __exit ir_rcmm_decode_exit(void)
246{
247	ir_raw_handler_unregister(&rcmm_handler);
248}
249
250module_init(ir_rcmm_decode_init);
251module_exit(ir_rcmm_decode_exit);
252
253MODULE_LICENSE("GPL");
254MODULE_AUTHOR("Patrick Lerda");
255MODULE_DESCRIPTION("RCMM IR protocol decoder");
256