1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Parallel port to Walkera WK-0701 TX joystick |
4 | * |
5 | * Copyright (c) 2008 Peter Popovec |
6 | * |
7 | * More about driver: <file:Documentation/input/devices/walkera0701.rst> |
8 | */ |
9 | |
10 | |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
12 | |
13 | #define RESERVE 20000 |
14 | #define SYNC_PULSE 1306000 |
15 | #define BIN0_PULSE 288000 |
16 | #define BIN1_PULSE 438000 |
17 | |
18 | #define ANALOG_MIN_PULSE 318000 |
19 | #define ANALOG_MAX_PULSE 878000 |
20 | #define ANALOG_DELTA 80000 |
21 | |
22 | #define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2) |
23 | |
24 | #define NO_SYNC 25 |
25 | |
26 | #include <linux/kernel.h> |
27 | #include <linux/module.h> |
28 | #include <linux/parport.h> |
29 | #include <linux/input.h> |
30 | #include <linux/hrtimer.h> |
31 | |
32 | MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>" ); |
33 | MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick" ); |
34 | MODULE_LICENSE("GPL" ); |
35 | |
36 | static unsigned int walkera0701_pp_no; |
37 | module_param_named(port, walkera0701_pp_no, int, 0); |
38 | MODULE_PARM_DESC(port, |
39 | "Parallel port adapter for Walkera WK-0701 TX (default is 0)" ); |
40 | |
41 | /* |
42 | * For now, only one device is supported, if somebody need more devices, code |
43 | * can be expanded, one struct walkera_dev per device must be allocated and |
44 | * set up by walkera0701_connect (release of device by walkera0701_disconnect) |
45 | */ |
46 | |
47 | struct walkera_dev { |
48 | unsigned char buf[25]; |
49 | u64 irq_time, irq_lasttime; |
50 | int counter; |
51 | int ack; |
52 | |
53 | struct input_dev *input_dev; |
54 | struct hrtimer timer; |
55 | |
56 | struct parport *parport; |
57 | struct pardevice *pardevice; |
58 | }; |
59 | |
60 | static struct walkera_dev w_dev; |
61 | |
62 | static inline void walkera0701_parse_frame(struct walkera_dev *w) |
63 | { |
64 | int i; |
65 | int val1, val2, val3, val4, val5, val6, val7, val8; |
66 | int magic, magic_bit; |
67 | int crc1, crc2; |
68 | |
69 | for (crc1 = crc2 = i = 0; i < 10; i++) { |
70 | crc1 += w->buf[i] & 7; |
71 | crc2 += (w->buf[i] & 8) >> 3; |
72 | } |
73 | if ((w->buf[10] & 7) != (crc1 & 7)) |
74 | return; |
75 | if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1)) |
76 | return; |
77 | for (crc1 = crc2 = 0, i = 11; i < 23; i++) { |
78 | crc1 += w->buf[i] & 7; |
79 | crc2 += (w->buf[i] & 8) >> 3; |
80 | } |
81 | if ((w->buf[23] & 7) != (crc1 & 7)) |
82 | return; |
83 | if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1)) |
84 | return; |
85 | val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2; |
86 | val1 *= ((w->buf[0] >> 2) & 2) - 1; /* sign */ |
87 | val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4]; |
88 | val2 *= (w->buf[2] & 2) - 1; /* sign */ |
89 | val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2; |
90 | val3 *= ((w->buf[5] >> 2) & 2) - 1; /* sign */ |
91 | val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9]; |
92 | val4 *= (w->buf[7] & 2) - 1; /* sign */ |
93 | val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2; |
94 | val5 *= ((w->buf[11] >> 2) & 2) - 1; /* sign */ |
95 | val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15]; |
96 | val6 *= (w->buf[13] & 2) - 1; /* sign */ |
97 | val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2; |
98 | val7 *= ((w->buf[16] >> 2) & 2) - 1; /*sign */ |
99 | val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20]; |
100 | val8 *= (w->buf[18] & 2) - 1; /*sign */ |
101 | |
102 | magic = (w->buf[21] << 4) | w->buf[22]; |
103 | magic_bit = (w->buf[24] & 8) >> 3; |
104 | pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n" , |
105 | val1, val2, val3, val4, val5, val6, val7, val8, |
106 | magic, magic_bit); |
107 | |
108 | input_report_abs(dev: w->input_dev, ABS_X, value: val2); |
109 | input_report_abs(dev: w->input_dev, ABS_Y, value: val1); |
110 | input_report_abs(dev: w->input_dev, ABS_Z, value: val6); |
111 | input_report_abs(dev: w->input_dev, ABS_THROTTLE, value: val3); |
112 | input_report_abs(dev: w->input_dev, ABS_RUDDER, value: val4); |
113 | input_report_abs(dev: w->input_dev, ABS_MISC, value: val7); |
114 | input_report_key(dev: w->input_dev, BTN_GEAR_DOWN, value: val5 > 0); |
115 | } |
116 | |
117 | static inline int read_ack(struct pardevice *p) |
118 | { |
119 | return parport_read_status(p->port) & 0x40; |
120 | } |
121 | |
122 | /* falling edge, prepare to BIN value calculation */ |
123 | static void walkera0701_irq_handler(void *handler_data) |
124 | { |
125 | u64 pulse_time; |
126 | struct walkera_dev *w = handler_data; |
127 | |
128 | w->irq_time = ktime_to_ns(kt: ktime_get()); |
129 | pulse_time = w->irq_time - w->irq_lasttime; |
130 | w->irq_lasttime = w->irq_time; |
131 | |
132 | /* cancel timer, if in handler or active do resync */ |
133 | if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) { |
134 | w->counter = NO_SYNC; |
135 | return; |
136 | } |
137 | |
138 | if (w->counter < NO_SYNC) { |
139 | if (w->ack) { |
140 | pulse_time -= BIN1_PULSE; |
141 | w->buf[w->counter] = 8; |
142 | } else { |
143 | pulse_time -= BIN0_PULSE; |
144 | w->buf[w->counter] = 0; |
145 | } |
146 | if (w->counter == 24) { /* full frame */ |
147 | walkera0701_parse_frame(w); |
148 | w->counter = NO_SYNC; |
149 | if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */ |
150 | w->counter = 0; |
151 | } else { |
152 | if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE) |
153 | && (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) { |
154 | pulse_time -= (ANALOG_MIN_PULSE - RESERVE); |
155 | pulse_time = (u32) pulse_time / ANALOG_DELTA; /* overtiping is safe, pulsetime < s32.. */ |
156 | w->buf[w->counter++] |= (pulse_time & 7); |
157 | } else |
158 | w->counter = NO_SYNC; |
159 | } |
160 | } else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) < |
161 | RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */ |
162 | w->counter = 0; |
163 | |
164 | hrtimer_start(timer: &w->timer, BIN_SAMPLE, mode: HRTIMER_MODE_REL); |
165 | } |
166 | |
167 | static enum hrtimer_restart timer_handler(struct hrtimer |
168 | *handle) |
169 | { |
170 | struct walkera_dev *w; |
171 | |
172 | w = container_of(handle, struct walkera_dev, timer); |
173 | w->ack = read_ack(p: w->pardevice); |
174 | |
175 | return HRTIMER_NORESTART; |
176 | } |
177 | |
178 | static int walkera0701_open(struct input_dev *dev) |
179 | { |
180 | struct walkera_dev *w = input_get_drvdata(dev); |
181 | |
182 | if (parport_claim(dev: w->pardevice)) |
183 | return -EBUSY; |
184 | |
185 | parport_enable_irq(w->parport); |
186 | return 0; |
187 | } |
188 | |
189 | static void walkera0701_close(struct input_dev *dev) |
190 | { |
191 | struct walkera_dev *w = input_get_drvdata(dev); |
192 | |
193 | parport_disable_irq(w->parport); |
194 | hrtimer_cancel(timer: &w->timer); |
195 | |
196 | parport_release(dev: w->pardevice); |
197 | } |
198 | |
199 | static void walkera0701_attach(struct parport *pp) |
200 | { |
201 | struct pardev_cb walkera0701_parport_cb; |
202 | struct walkera_dev *w = &w_dev; |
203 | |
204 | if (pp->number != walkera0701_pp_no) { |
205 | pr_debug("Not using parport%d.\n" , pp->number); |
206 | return; |
207 | } |
208 | |
209 | if (pp->irq == -1) { |
210 | pr_err("parport %d does not have interrupt assigned\n" , |
211 | pp->number); |
212 | return; |
213 | } |
214 | |
215 | w->parport = pp; |
216 | |
217 | memset(&walkera0701_parport_cb, 0, sizeof(walkera0701_parport_cb)); |
218 | walkera0701_parport_cb.flags = PARPORT_FLAG_EXCL; |
219 | walkera0701_parport_cb.irq_func = walkera0701_irq_handler; |
220 | walkera0701_parport_cb.private = w; |
221 | |
222 | w->pardevice = parport_register_dev_model(port: pp, name: "walkera0701" , |
223 | par_dev_cb: &walkera0701_parport_cb, cnt: 0); |
224 | |
225 | if (!w->pardevice) { |
226 | pr_err("failed to register parport device\n" ); |
227 | return; |
228 | } |
229 | |
230 | if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) { |
231 | pr_err("failed to negotiate parport mode\n" ); |
232 | goto err_unregister_device; |
233 | } |
234 | |
235 | hrtimer_init(timer: &w->timer, CLOCK_MONOTONIC, mode: HRTIMER_MODE_REL); |
236 | w->timer.function = timer_handler; |
237 | |
238 | w->input_dev = input_allocate_device(); |
239 | if (!w->input_dev) { |
240 | pr_err("failed to allocate input device\n" ); |
241 | goto err_unregister_device; |
242 | } |
243 | |
244 | input_set_drvdata(dev: w->input_dev, data: w); |
245 | w->input_dev->name = "Walkera WK-0701 TX" ; |
246 | w->input_dev->phys = w->parport->name; |
247 | w->input_dev->id.bustype = BUS_PARPORT; |
248 | |
249 | /* TODO what id vendor/product/version ? */ |
250 | w->input_dev->id.vendor = 0x0001; |
251 | w->input_dev->id.product = 0x0001; |
252 | w->input_dev->id.version = 0x0100; |
253 | w->input_dev->dev.parent = w->parport->dev; |
254 | w->input_dev->open = walkera0701_open; |
255 | w->input_dev->close = walkera0701_close; |
256 | |
257 | w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY); |
258 | w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN); |
259 | |
260 | input_set_abs_params(dev: w->input_dev, ABS_X, min: -512, max: 512, fuzz: 0, flat: 0); |
261 | input_set_abs_params(dev: w->input_dev, ABS_Y, min: -512, max: 512, fuzz: 0, flat: 0); |
262 | input_set_abs_params(dev: w->input_dev, ABS_Z, min: -512, max: 512, fuzz: 0, flat: 0); |
263 | input_set_abs_params(dev: w->input_dev, ABS_THROTTLE, min: -512, max: 512, fuzz: 0, flat: 0); |
264 | input_set_abs_params(dev: w->input_dev, ABS_RUDDER, min: -512, max: 512, fuzz: 0, flat: 0); |
265 | input_set_abs_params(dev: w->input_dev, ABS_MISC, min: -512, max: 512, fuzz: 0, flat: 0); |
266 | |
267 | if (input_register_device(w->input_dev)) { |
268 | pr_err("failed to register input device\n" ); |
269 | goto err_free_input_dev; |
270 | } |
271 | |
272 | return; |
273 | |
274 | err_free_input_dev: |
275 | input_free_device(dev: w->input_dev); |
276 | err_unregister_device: |
277 | parport_unregister_device(dev: w->pardevice); |
278 | } |
279 | |
280 | static void walkera0701_detach(struct parport *port) |
281 | { |
282 | struct walkera_dev *w = &w_dev; |
283 | |
284 | if (!w->pardevice || w->parport->number != port->number) |
285 | return; |
286 | |
287 | input_unregister_device(w->input_dev); |
288 | parport_unregister_device(dev: w->pardevice); |
289 | w->parport = NULL; |
290 | } |
291 | |
292 | static struct parport_driver walkera0701_parport_driver = { |
293 | .name = "walkera0701" , |
294 | .match_port = walkera0701_attach, |
295 | .detach = walkera0701_detach, |
296 | .devmodel = true, |
297 | }; |
298 | |
299 | static int __init walkera0701_init(void) |
300 | { |
301 | return parport_register_driver(&walkera0701_parport_driver); |
302 | } |
303 | |
304 | static void __exit walkera0701_exit(void) |
305 | { |
306 | parport_unregister_driver(&walkera0701_parport_driver); |
307 | } |
308 | |
309 | module_init(walkera0701_init); |
310 | module_exit(walkera0701_exit); |
311 | |