aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/input/walkera0701.txt109
-rw-r--r--drivers/input/joystick/Kconfig12
-rw-r--r--drivers/input/joystick/Makefile1
-rw-r--r--drivers/input/joystick/walkera0701.c292
4 files changed, 414 insertions, 0 deletions
diff --git a/Documentation/input/walkera0701.txt b/Documentation/input/walkera0701.txt
new file mode 100644
index 000000000000..8f4289efc5c4
--- /dev/null
+++ b/Documentation/input/walkera0701.txt
@@ -0,0 +1,109 @@
1
2Walkera WK-0701 transmitter is supplied with a ready to fly Walkera
3helicopters such as HM36, HM37, HM60. The walkera0701 module enables to use
4this transmitter as joystick
5
6Devel homepage and download:
7http://zub.fei.tuke.sk/walkera-wk0701/
8
9or use cogito:
10cg-clone http://zub.fei.tuke.sk/GIT/walkera0701-joystick
11
12
13Connecting to PC:
14
15At back side of transmitter S-video connector can be found. Modulation
16pulses from processor to HF part can be found at pin 2 of this connector,
17pin 3 is GND. Between pin 3 and CPU 5k6 resistor can be found. To get
18modulation pulses to PC, signal pulses must be amplified.
19
20Cable: (walkera TX to parport)
21
22Walkera WK-0701 TX S-VIDEO connector:
23 (back side of TX)
24 __ __ S-video: canon25
25 / |_| \ pin 2 (signal) NPN parport
26 / O 4 3 O \ pin 3 (GND) LED ________________ 10 ACK
27 ( O 2 1 O ) | C
28 \ ___ / 2 ________________________|\|_____|/
29 | [___] | |/| B |\
30 ------- 3 __________________________________|________________ 25 GND
31 E
32
33
34I use green LED and BC109 NPN transistor.
35
36Software:
37
38Build kernel with walkera0701 module. Module walkera0701 need exclusive
39access to parport, modules like lp must be unloaded before loading
40walkera0701 module, check dmesg for error messages. Connect TX to PC by
41cable and run jstest /dev/input/js0 to see values from TX. If no value can
42be changed by TX "joystick", check output from /proc/interrupts. Value for
43(usually irq7) parport must increase if TX is on.
44
45
46
47Technical details:
48
49Driver use interrupt from parport ACK input bit to measure pulse length
50using hrtimers.
51
52Frame format:
53Based on walkera WK-0701 PCM Format description by Shaul Eizikovich.
54(downloaded from http://www.smartpropoplus.com/Docs/Walkera_Wk-0701_PCM.pdf)
55
56Signal pulses:
57 (ANALOG)
58 SYNC BIN OCT
59 +---------+ +------+
60 | | | |
61--+ +------+ +---
62
63Frame:
64 SYNC , BIN1, OCT1, BIN2, OCT2 ... BIN24, OCT24, BIN25, next frame SYNC ..
65
66pulse length:
67 Binary values: Analog octal values:
68
69 288 uS Binary 0 318 uS 000
70 438 uS Binary 1 398 uS 001
71 478 uS 010
72 558 uS 011
73 638 uS 100
74 1306 uS SYNC 718 uS 101
75 798 uS 110
76 878 uS 111
77
7824 bin+oct values + 1 bin value = 24*4+1 bits = 97 bits
79
80(Warning, pulses on ACK ar inverted by transistor, irq is rised up on sync
81to bin change or octal value to bin change).
82
83Binary data representations:
84
85One binary and octal value can be grouped to nibble. 24 nibbles + one binary
86values can be sampled between sync pulses.
87
88Values for first four channels (analog joystick values) can be found in
89first 10 nibbles. Analog value is represented by one sign bit and 9 bit
90absolute binary value. (10 bits per channel). Next nibble is checksum for
91first ten nibbles.
92
93Next nibbles 12 .. 21 represents four channels (not all channels can be
94directly controlled from TX). Binary representations ar the same as in first
95four channels. In nibbles 22 and 23 is a special magic number. Nibble 24 is
96checksum for nibbles 12..23.
97
98After last octal value for nibble 24 and next sync pulse one additional
99binary value can be sampled. This bit and magic number is not used in
100software driver. Some details about this magic numbers can be found in
101Walkera_Wk-0701_PCM.pdf.
102
103Checksum calculation:
104
105Summary of octal values in nibbles must be same as octal value in checksum
106nibble (only first 3 bits are used). Binary value for checksum nibble is
107calculated by sum of binary values in checked nibbles + sum of octal values
108in checked nibbles divided by 8. Only bit 0 of this sum is used.
109
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index be5c14a5a0a4..268dd3fef0ab 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -294,4 +294,16 @@ config JOYSTICK_XPAD_LEDS
294 This option enables support for the LED which surrounds the Big X on 294 This option enables support for the LED which surrounds the Big X on
295 XBox 360 controller. 295 XBox 360 controller.
296 296
297config JOYSTICK_WALKERA0701
298 tristate "Walkera WK-0701 RC transmitter"
299 depends on HIGH_RES_TIMERS && PARPORT
300 help
301 Say Y or M here if you have a Walkera WK-0701 transmitter which is
302 supplied with a ready to fly Walkera helicopters such as HM36,
303 HM37, HM60 and want to use it via parport as a joystick. More
304 information is available: <file:Documentation/input/walkera0701.txt>
305
306 To compile this driver as a module, choose M here: the
307 module will be called walkera0701.
308
297endif 309endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index fdbf8c4c2876..723036295685 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -29,4 +29,5 @@ obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
29obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o 29obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
30obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o 30obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
31obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o 31obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
32obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
32 33
diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c
new file mode 100644
index 000000000000..4dfa1eed4b7c
--- /dev/null
+++ b/drivers/input/joystick/walkera0701.c
@@ -0,0 +1,292 @@
1/*
2 * Parallel port to Walkera WK-0701 TX joystick
3 *
4 * Copyright (c) 2008 Peter Popovec
5 *
6 * More about driver: <file:Documentation/input/walkera0701.txt>
7 */
8
9/*
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13*/
14
15/* #define WK0701_DEBUG */
16
17#define RESERVE 20000
18#define SYNC_PULSE 1306000
19#define BIN0_PULSE 288000
20#define BIN1_PULSE 438000
21
22#define ANALOG_MIN_PULSE 318000
23#define ANALOG_MAX_PULSE 878000
24#define ANALOG_DELTA 80000
25
26#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2)
27
28#define NO_SYNC 25
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/parport.h>
33#include <linux/input.h>
34#include <linux/hrtimer.h>
35
36MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
37MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick");
38MODULE_LICENSE("GPL");
39
40static unsigned int walkera0701_pp_no;
41module_param_named(port, walkera0701_pp_no, int, 0);
42MODULE_PARM_DESC(port,
43 "Parallel port adapter for Walkera WK-0701 TX (default is 0)");
44
45/*
46 * For now, only one device is supported, if somebody need more devices, code
47 * can be expanded, one struct walkera_dev per device must be allocated and
48 * set up by walkera0701_connect (release of device by walkera0701_disconnect)
49 */
50
51struct walkera_dev {
52 unsigned char buf[25];
53 u64 irq_time, irq_lasttime;
54 int counter;
55 int ack;
56
57 struct input_dev *input_dev;
58 struct hrtimer timer;
59
60 struct parport *parport;
61 struct pardevice *pardevice;
62};
63
64static struct walkera_dev w_dev;
65
66static inline void walkera0701_parse_frame(struct walkera_dev *w)
67{
68 int i;
69 int val1, val2, val3, val4, val5, val6, val7, val8;
70 int crc1, crc2;
71
72 for (crc1 = crc2 = i = 0; i < 10; i++) {
73 crc1 += w->buf[i] & 7;
74 crc2 += (w->buf[i] & 8) >> 3;
75 }
76 if ((w->buf[10] & 7) != (crc1 & 7))
77 return;
78 if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
79 return;
80 for (crc1 = crc2 = 0, i = 11; i < 23; i++) {
81 crc1 += w->buf[i] & 7;
82 crc2 += (w->buf[i] & 8) >> 3;
83 }
84 if ((w->buf[23] & 7) != (crc1 & 7))
85 return;
86 if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
87 return;
88 val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2;
89 val1 *= ((w->buf[0] >> 2) & 2) - 1; /* sign */
90 val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4];
91 val2 *= (w->buf[2] & 2) - 1; /* sign */
92 val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2;
93 val3 *= ((w->buf[5] >> 2) & 2) - 1; /* sign */
94 val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9];
95 val4 *= (w->buf[7] & 2) - 1; /* sign */
96 val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2;
97 val5 *= ((w->buf[11] >> 2) & 2) - 1; /* sign */
98 val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15];
99 val6 *= (w->buf[13] & 2) - 1; /* sign */
100 val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2;
101 val7 *= ((w->buf[16] >> 2) & 2) - 1; /*sign */
102 val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
103 val8 *= (w->buf[18] & 2) - 1; /*sign */
104
105#ifdef WK0701_DEBUG
106 {
107 int magic, magic_bit;
108 magic = (w->buf[21] << 4) | w->buf[22];
109 magic_bit = (w->buf[24] & 8) >> 3;
110 printk(KERN_DEBUG
111 "walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
112 val1, val2, val3, val4, val5, val6, val7, val8, magic,
113 magic_bit);
114 }
115#endif
116 input_report_abs(w->input_dev, ABS_X, val2);
117 input_report_abs(w->input_dev, ABS_Y, val1);
118 input_report_abs(w->input_dev, ABS_Z, val6);
119 input_report_abs(w->input_dev, ABS_THROTTLE, val3);
120 input_report_abs(w->input_dev, ABS_RUDDER, val4);
121 input_report_abs(w->input_dev, ABS_MISC, val7);
122 input_report_key(w->input_dev, BTN_GEAR_DOWN, val5 > 0);
123}
124
125static inline int read_ack(struct pardevice *p)
126{
127 return parport_read_status(p->port) & 0x40;
128}
129
130/* falling edge, prepare to BIN value calculation */
131static void walkera0701_irq_handler(void *handler_data)
132{
133 u64 pulse_time;
134 struct walkera_dev *w = handler_data;
135
136 w->irq_time = ktime_to_ns(ktime_get());
137 pulse_time = w->irq_time - w->irq_lasttime;
138 w->irq_lasttime = w->irq_time;
139
140 /* cancel timer, if in handler or active do resync */
141 if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) {
142 w->counter = NO_SYNC;
143 return;
144 }
145
146 if (w->counter < NO_SYNC) {
147 if (w->ack) {
148 pulse_time -= BIN1_PULSE;
149 w->buf[w->counter] = 8;
150 } else {
151 pulse_time -= BIN0_PULSE;
152 w->buf[w->counter] = 0;
153 }
154 if (w->counter == 24) { /* full frame */
155 walkera0701_parse_frame(w);
156 w->counter = NO_SYNC;
157 if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */
158 w->counter = 0;
159 } else {
160 if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE)
161 && (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) {
162 pulse_time -= (ANALOG_MIN_PULSE - RESERVE);
163 pulse_time = (u32) pulse_time / ANALOG_DELTA; /* overtiping is safe, pulsetime < s32.. */
164 w->buf[w->counter++] |= (pulse_time & 7);
165 } else
166 w->counter = NO_SYNC;
167 }
168 } else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) <
169 RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */
170 w->counter = 0;
171
172 hrtimer_start(&w->timer, ktime_set(0, BIN_SAMPLE), HRTIMER_MODE_REL);
173}
174
175static enum hrtimer_restart timer_handler(struct hrtimer
176 *handle)
177{
178 struct walkera_dev *w;
179
180 w = container_of(handle, struct walkera_dev, timer);
181 w->ack = read_ack(w->pardevice);
182
183 return HRTIMER_NORESTART;
184}
185
186static int walkera0701_open(struct input_dev *dev)
187{
188 struct walkera_dev *w = input_get_drvdata(dev);
189
190 parport_enable_irq(w->parport);
191 return 0;
192}
193
194static void walkera0701_close(struct input_dev *dev)
195{
196 struct walkera_dev *w = input_get_drvdata(dev);
197
198 parport_disable_irq(w->parport);
199}
200
201static int walkera0701_connect(struct walkera_dev *w, int parport)
202{
203 int err = -ENODEV;
204
205 w->parport = parport_find_number(parport);
206 if (w->parport == NULL)
207 return -ENODEV;
208
209 if (w->parport->irq == -1) {
210 printk(KERN_ERR "walkera0701: parport without interrupt\n");
211 goto init_err;
212 }
213
214 err = -EBUSY;
215 w->pardevice = parport_register_device(w->parport, "walkera0701",
216 NULL, NULL, walkera0701_irq_handler,
217 PARPORT_DEV_EXCL, w);
218 if (!w->pardevice)
219 goto init_err;
220
221 if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
222 goto init_err1;
223
224 if (parport_claim(w->pardevice))
225 goto init_err1;
226
227 w->input_dev = input_allocate_device();
228 if (!w->input_dev)
229 goto init_err2;
230
231 input_set_drvdata(w->input_dev, w);
232 w->input_dev->name = "Walkera WK-0701 TX";
233 w->input_dev->phys = w->parport->name;
234 w->input_dev->id.bustype = BUS_PARPORT;
235
236 /* TODO what id vendor/product/version ? */
237 w->input_dev->id.vendor = 0x0001;
238 w->input_dev->id.product = 0x0001;
239 w->input_dev->id.version = 0x0100;
240 w->input_dev->open = walkera0701_open;
241 w->input_dev->close = walkera0701_close;
242
243 w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY);
244 w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN);
245
246 input_set_abs_params(w->input_dev, ABS_X, -512, 512, 0, 0);
247 input_set_abs_params(w->input_dev, ABS_Y, -512, 512, 0, 0);
248 input_set_abs_params(w->input_dev, ABS_Z, -512, 512, 0, 0);
249 input_set_abs_params(w->input_dev, ABS_THROTTLE, -512, 512, 0, 0);
250 input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
251 input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
252
253 err = input_register_device(w->input_dev);
254 if (err)
255 goto init_err3;
256
257 hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
258 w->timer.function = timer_handler;
259 return 0;
260
261 init_err3:
262 input_free_device(w->input_dev);
263 init_err2:
264 parport_release(w->pardevice);
265 init_err1:
266 parport_unregister_device(w->pardevice);
267 init_err:
268 parport_put_port(w->parport);
269 return err;
270}
271
272static void walkera0701_disconnect(struct walkera_dev *w)
273{
274 hrtimer_cancel(&w->timer);
275 input_unregister_device(w->input_dev);
276 parport_release(w->pardevice);
277 parport_unregister_device(w->pardevice);
278 parport_put_port(w->parport);
279}
280
281static int __init walkera0701_init(void)
282{
283 return walkera0701_connect(&w_dev, walkera0701_pp_no);
284}
285
286static void __exit walkera0701_exit(void)
287{
288 walkera0701_disconnect(&w_dev);
289}
290
291module_init(walkera0701_init);
292module_exit(walkera0701_exit);