1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // ir-imon-decoder.c - handle iMon protocol |
3 | // |
4 | // Copyright (C) 2018 by Sean Young <sean@mess.org> |
5 | |
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
7 | |
8 | #include <linux/module.h> |
9 | #include "rc-core-priv.h" |
10 | |
11 | #define IMON_UNIT 416 /* us */ |
12 | #define IMON_BITS 30 |
13 | #define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \ |
14 | BIT(21) | BIT(20) | BIT(19) | BIT(18) | \ |
15 | BIT(17) | BIT(16) | BIT(14) | BIT(13) | \ |
16 | BIT(12) | BIT(11) | BIT(10) | BIT(9)) |
17 | |
18 | /* |
19 | * This protocol has 30 bits. The format is one IMON_UNIT header pulse, |
20 | * followed by 30 bits. Each bit is one IMON_UNIT check field, and then |
21 | * one IMON_UNIT field with the actual bit (1=space, 0=pulse). |
22 | * The check field is always space for some bits, for others it is pulse if |
23 | * both the preceding and current bit are zero, else space. IMON_CHKBITS |
24 | * defines which bits are of type check. |
25 | * |
26 | * There is no way to distinguish an incomplete message from one where |
27 | * the lower bits are all set, iow. the last pulse is for the lowest |
28 | * bit which is 0. |
29 | */ |
30 | enum imon_state { |
31 | STATE_INACTIVE, |
32 | STATE_BIT_CHK, |
33 | STATE_BIT_START, |
34 | STATE_FINISHED, |
35 | STATE_ERROR, |
36 | }; |
37 | |
38 | static void ir_imon_decode_scancode(struct rc_dev *dev) |
39 | { |
40 | struct imon_dec *imon = &dev->raw->imon; |
41 | |
42 | /* Keyboard/Mouse toggle */ |
43 | if (imon->bits == 0x299115b7) |
44 | imon->stick_keyboard = !imon->stick_keyboard; |
45 | |
46 | if ((imon->bits & 0xfc0000ff) == 0x680000b7) { |
47 | int rel_x, rel_y; |
48 | u8 buf; |
49 | |
50 | buf = imon->bits >> 16; |
51 | rel_x = (buf & 0x08) | (buf & 0x10) >> 2 | |
52 | (buf & 0x20) >> 4 | (buf & 0x40) >> 6; |
53 | if (imon->bits & 0x02000000) |
54 | rel_x |= ~0x0f; |
55 | buf = imon->bits >> 8; |
56 | rel_y = (buf & 0x08) | (buf & 0x10) >> 2 | |
57 | (buf & 0x20) >> 4 | (buf & 0x40) >> 6; |
58 | if (imon->bits & 0x01000000) |
59 | rel_y |= ~0x0f; |
60 | |
61 | if (rel_x && rel_y && imon->stick_keyboard) { |
62 | if (abs(rel_y) > abs(rel_x)) |
63 | imon->bits = rel_y > 0 ? |
64 | 0x289515b7 : /* KEY_DOWN */ |
65 | 0x2aa515b7; /* KEY_UP */ |
66 | else |
67 | imon->bits = rel_x > 0 ? |
68 | 0x2ba515b7 : /* KEY_RIGHT */ |
69 | 0x29a515b7; /* KEY_LEFT */ |
70 | } |
71 | |
72 | if (!imon->stick_keyboard) { |
73 | input_report_rel(dev: dev->input_dev, REL_X, value: rel_x); |
74 | input_report_rel(dev: dev->input_dev, REL_Y, value: rel_y); |
75 | |
76 | input_report_key(dev: dev->input_dev, BTN_LEFT, |
77 | value: (imon->bits & 0x00010000) != 0); |
78 | input_report_key(dev: dev->input_dev, BTN_RIGHT, |
79 | value: (imon->bits & 0x00040000) != 0); |
80 | } |
81 | } |
82 | |
83 | rc_keydown(dev, protocol: RC_PROTO_IMON, scancode: imon->bits, toggle: 0); |
84 | } |
85 | |
86 | /** |
87 | * ir_imon_decode() - Decode one iMON pulse or space |
88 | * @dev: the struct rc_dev descriptor of the device |
89 | * @ev: the struct ir_raw_event descriptor of the pulse/space |
90 | * |
91 | * This function returns -EINVAL if the pulse violates the state machine |
92 | */ |
93 | static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) |
94 | { |
95 | struct imon_dec *data = &dev->raw->imon; |
96 | |
97 | if (!is_timing_event(ev)) { |
98 | if (ev.overflow) |
99 | data->state = STATE_INACTIVE; |
100 | return 0; |
101 | } |
102 | |
103 | dev_dbg(&dev->dev, |
104 | "iMON decode started at state %d bitno %d (%uus %s)\n" , |
105 | data->state, data->count, ev.duration, TO_STR(ev.pulse)); |
106 | |
107 | /* |
108 | * Since iMON protocol is a series of bits, if at any point |
109 | * we encounter an error, make sure that any remaining bits |
110 | * aren't parsed as a scancode made up of less bits. |
111 | * |
112 | * Note that if the stick is held, then the remote repeats |
113 | * the scancode with about 12ms between them. So, make sure |
114 | * we have at least 10ms of space after an error. That way, |
115 | * we're at a new scancode. |
116 | */ |
117 | if (data->state == STATE_ERROR) { |
118 | if (!ev.pulse && ev.duration > MS_TO_US(10)) |
119 | data->state = STATE_INACTIVE; |
120 | return 0; |
121 | } |
122 | |
123 | for (;;) { |
124 | if (!geq_margin(d1: ev.duration, IMON_UNIT, IMON_UNIT / 2)) |
125 | return 0; |
126 | |
127 | decrease_duration(ev: &ev, IMON_UNIT); |
128 | |
129 | switch (data->state) { |
130 | case STATE_INACTIVE: |
131 | if (ev.pulse) { |
132 | data->state = STATE_BIT_CHK; |
133 | data->bits = 0; |
134 | data->count = IMON_BITS; |
135 | } |
136 | break; |
137 | case STATE_BIT_CHK: |
138 | if (IMON_CHKBITS & BIT(data->count)) |
139 | data->last_chk = ev.pulse; |
140 | else if (ev.pulse) |
141 | goto err_out; |
142 | data->state = STATE_BIT_START; |
143 | break; |
144 | case STATE_BIT_START: |
145 | data->bits <<= 1; |
146 | if (!ev.pulse) |
147 | data->bits |= 1; |
148 | |
149 | if (IMON_CHKBITS & BIT(data->count)) { |
150 | if (data->last_chk != !(data->bits & 3)) |
151 | goto err_out; |
152 | } |
153 | |
154 | if (!data->count--) |
155 | data->state = STATE_FINISHED; |
156 | else |
157 | data->state = STATE_BIT_CHK; |
158 | break; |
159 | case STATE_FINISHED: |
160 | if (ev.pulse) |
161 | goto err_out; |
162 | ir_imon_decode_scancode(dev); |
163 | data->state = STATE_INACTIVE; |
164 | break; |
165 | } |
166 | } |
167 | |
168 | err_out: |
169 | dev_dbg(&dev->dev, |
170 | "iMON decode failed at state %d bitno %d (%uus %s)\n" , |
171 | data->state, data->count, ev.duration, TO_STR(ev.pulse)); |
172 | |
173 | data->state = STATE_ERROR; |
174 | |
175 | return -EINVAL; |
176 | } |
177 | |
178 | /** |
179 | * ir_imon_encode() - Encode a scancode as a stream of raw events |
180 | * |
181 | * @protocol: protocol to encode |
182 | * @scancode: scancode to encode |
183 | * @events: array of raw ir events to write into |
184 | * @max: maximum size of @events |
185 | * |
186 | * Returns: The number of events written. |
187 | * -ENOBUFS if there isn't enough space in the array to fit the |
188 | * encoding. In this case all @max events will have been written. |
189 | */ |
190 | static int ir_imon_encode(enum rc_proto protocol, u32 scancode, |
191 | struct ir_raw_event *events, unsigned int max) |
192 | { |
193 | struct ir_raw_event *e = events; |
194 | int i, pulse; |
195 | |
196 | if (!max--) |
197 | return -ENOBUFS; |
198 | init_ir_raw_event_duration(ev: e, pulse: 1, IMON_UNIT); |
199 | |
200 | for (i = IMON_BITS; i >= 0; i--) { |
201 | if (BIT(i) & IMON_CHKBITS) |
202 | pulse = !(scancode & (BIT(i) | BIT(i + 1))); |
203 | else |
204 | pulse = 0; |
205 | |
206 | if (pulse == e->pulse) { |
207 | e->duration += IMON_UNIT; |
208 | } else { |
209 | if (!max--) |
210 | return -ENOBUFS; |
211 | init_ir_raw_event_duration(ev: ++e, pulse, IMON_UNIT); |
212 | } |
213 | |
214 | pulse = !(scancode & BIT(i)); |
215 | |
216 | if (pulse == e->pulse) { |
217 | e->duration += IMON_UNIT; |
218 | } else { |
219 | if (!max--) |
220 | return -ENOBUFS; |
221 | init_ir_raw_event_duration(ev: ++e, pulse, IMON_UNIT); |
222 | } |
223 | } |
224 | |
225 | if (e->pulse) |
226 | e++; |
227 | |
228 | return e - events; |
229 | } |
230 | |
231 | static int ir_imon_register(struct rc_dev *dev) |
232 | { |
233 | struct imon_dec *imon = &dev->raw->imon; |
234 | |
235 | imon->stick_keyboard = false; |
236 | |
237 | return 0; |
238 | } |
239 | |
240 | static struct ir_raw_handler imon_handler = { |
241 | .protocols = RC_PROTO_BIT_IMON, |
242 | .decode = ir_imon_decode, |
243 | .encode = ir_imon_encode, |
244 | .carrier = 38000, |
245 | .raw_register = ir_imon_register, |
246 | .min_timeout = IMON_UNIT * IMON_BITS * 2, |
247 | }; |
248 | |
249 | static int __init ir_imon_decode_init(void) |
250 | { |
251 | ir_raw_handler_register(ir_raw_handler: &imon_handler); |
252 | |
253 | pr_info("IR iMON protocol handler initialized\n" ); |
254 | return 0; |
255 | } |
256 | |
257 | static void __exit ir_imon_decode_exit(void) |
258 | { |
259 | ir_raw_handler_unregister(ir_raw_handler: &imon_handler); |
260 | } |
261 | |
262 | module_init(ir_imon_decode_init); |
263 | module_exit(ir_imon_decode_exit); |
264 | |
265 | MODULE_LICENSE("GPL" ); |
266 | MODULE_AUTHOR("Sean Young <sean@mess.org>" ); |
267 | MODULE_DESCRIPTION("iMON IR protocol decoder" ); |
268 | |