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 
16 enum rcmm_state {
17 	STATE_INACTIVE,
18 	STATE_LOW,
19 	STATE_BUMP,
20 	STATE_VALUE,
21 	STATE_FINISHED,
22 };
23 
24 static bool rcmm_mode(const struct rcmm_dec *data)
25 {
26 	return !((0x000c0000 & data->bits) == 0x000c0000);
27 }
28 
29 static 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  */
59 static 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.reset)
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 
173 static const int rcmmspace[] = {
174 	RCMM_PULSE_0,
175 	RCMM_PULSE_1,
176 	RCMM_PULSE_2,
177 	RCMM_PULSE_3,
178 };
179 
180 static 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 
201 static 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 
227 static 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 
237 static 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 
245 static void __exit ir_rcmm_decode_exit(void)
246 {
247 	ir_raw_handler_unregister(&rcmm_handler);
248 }
249 
250 module_init(ir_rcmm_decode_init);
251 module_exit(ir_rcmm_decode_exit);
252 
253 MODULE_LICENSE("GPL");
254 MODULE_AUTHOR("Patrick Lerda");
255 MODULE_DESCRIPTION("RCMM IR protocol decoder");
256