Commit | Line | Data |
---|---|---|
721074b0 PL |
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> | |
721074b0 | 8 | |
528222d8 SY |
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) */ | |
721074b0 PL |
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 | | |
528222d8 SY |
67 | RC_PROTO_BIT_RCMM24 | |
68 | RC_PROTO_BIT_RCMM12))) | |
721074b0 PL |
69 | return 0; |
70 | ||
71 | if (!is_timing_event(ev)) { | |
950170d6 | 72 | if (ev.overflow) |
721074b0 PL |
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 | ||
81bab3fa | 82 | if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT)) |
721074b0 PL |
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 | ||
81bab3fa | 94 | if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT)) |
721074b0 PL |
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 | ||
81bab3fa | 167 | dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n", |
528222d8 | 168 | data->count, data->state, ev.duration, TO_STR(ev.pulse)); |
721074b0 PL |
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"); |