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