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, protocol: RC_PROTO_RCMM24, scancode: data->bits, toggle: 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, protocol: RC_PROTO_RCMM12, scancode: data->bits, toggle: 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.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(d1: 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(d1: 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(d1: 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(d1: ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2)) |
115 | value = 0; |
116 | else if (eq_margin(d1: ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2)) |
117 | value = 1; |
118 | else if (eq_margin(d1: ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2)) |
119 | value = 2; |
120 | else if (eq_margin(d1: 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(d1: 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, protocol: 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: &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: &max, RCMM_UNIT, space_width: space); |
194 | if (ret) |
195 | return ret; |
196 | } |
197 | |
198 | return ir_raw_gen_pulse_space(ev, max: &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(ev: &e, max, n: 32, data: scancode); |
210 | break; |
211 | case RC_PROTO_RCMM24: |
212 | ret = ir_rcmm_rawencoder(ev: &e, max, n: 24, data: scancode); |
213 | break; |
214 | case RC_PROTO_RCMM12: |
215 | ret = ir_rcmm_rawencoder(ev: &e, max, n: 12, data: 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(ir_raw_handler: &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(ir_raw_handler: &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 | |