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, 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 */
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(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
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: &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
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(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
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(ir_raw_handler: &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(ir_raw_handler: &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

source code of linux/drivers/media/rc/ir-rcmm-decoder.c