Commit cec87e38e92cdfe86678ca2a5c29c38d05127601

Authored by Peter Popovec
Committed by Dmitry Torokhov
1 parent 59bdb43769

Input: add joystick driver for Walkera WK-0701 RC transmitter

Signed-off-by: Peter Popovec <popovec@fei.tuke.sk>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

Showing 4 changed files with 414 additions and 0 deletions Side-by-side Diff

Documentation/input/walkera0701.txt
  1 +
  2 +Walkera WK-0701 transmitter is supplied with a ready to fly Walkera
  3 +helicopters such as HM36, HM37, HM60. The walkera0701 module enables to use
  4 +this transmitter as joystick
  5 +
  6 +Devel homepage and download:
  7 +http://zub.fei.tuke.sk/walkera-wk0701/
  8 +
  9 +or use cogito:
  10 +cg-clone http://zub.fei.tuke.sk/GIT/walkera0701-joystick
  11 +
  12 +
  13 +Connecting to PC:
  14 +
  15 +At back side of transmitter S-video connector can be found. Modulation
  16 +pulses from processor to HF part can be found at pin 2 of this connector,
  17 +pin 3 is GND. Between pin 3 and CPU 5k6 resistor can be found. To get
  18 +modulation pulses to PC, signal pulses must be amplified.
  19 +
  20 +Cable: (walkera TX to parport)
  21 +
  22 +Walkera 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 +
  34 +I use green LED and BC109 NPN transistor.
  35 +
  36 +Software:
  37 +
  38 +Build kernel with walkera0701 module. Module walkera0701 need exclusive
  39 +access to parport, modules like lp must be unloaded before loading
  40 +walkera0701 module, check dmesg for error messages. Connect TX to PC by
  41 +cable and run jstest /dev/input/js0 to see values from TX. If no value can
  42 +be changed by TX "joystick", check output from /proc/interrupts. Value for
  43 +(usually irq7) parport must increase if TX is on.
  44 +
  45 +
  46 +
  47 +Technical details:
  48 +
  49 +Driver use interrupt from parport ACK input bit to measure pulse length
  50 +using hrtimers.
  51 +
  52 +Frame format:
  53 +Based on walkera WK-0701 PCM Format description by Shaul Eizikovich.
  54 +(downloaded from http://www.smartpropoplus.com/Docs/Walkera_Wk-0701_PCM.pdf)
  55 +
  56 +Signal pulses:
  57 + (ANALOG)
  58 + SYNC BIN OCT
  59 + +---------+ +------+
  60 + | | | |
  61 +--+ +------+ +---
  62 +
  63 +Frame:
  64 + SYNC , BIN1, OCT1, BIN2, OCT2 ... BIN24, OCT24, BIN25, next frame SYNC ..
  65 +
  66 +pulse 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 +
  78 +24 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
  81 +to bin change or octal value to bin change).
  82 +
  83 +Binary data representations:
  84 +
  85 +One binary and octal value can be grouped to nibble. 24 nibbles + one binary
  86 +values can be sampled between sync pulses.
  87 +
  88 +Values for first four channels (analog joystick values) can be found in
  89 +first 10 nibbles. Analog value is represented by one sign bit and 9 bit
  90 +absolute binary value. (10 bits per channel). Next nibble is checksum for
  91 +first ten nibbles.
  92 +
  93 +Next nibbles 12 .. 21 represents four channels (not all channels can be
  94 +directly controlled from TX). Binary representations ar the same as in first
  95 +four channels. In nibbles 22 and 23 is a special magic number. Nibble 24 is
  96 +checksum for nibbles 12..23.
  97 +
  98 +After last octal value for nibble 24 and next sync pulse one additional
  99 +binary value can be sampled. This bit and magic number is not used in
  100 +software driver. Some details about this magic numbers can be found in
  101 +Walkera_Wk-0701_PCM.pdf.
  102 +
  103 +Checksum calculation:
  104 +
  105 +Summary of octal values in nibbles must be same as octal value in checksum
  106 +nibble (only first 3 bits are used). Binary value for checksum nibble is
  107 +calculated by sum of binary values in checked nibbles + sum of octal values
  108 +in checked nibbles divided by 8. Only bit 0 of this sum is used.
drivers/input/joystick/Kconfig
... ... @@ -294,5 +294,17 @@
294 294 This option enables support for the LED which surrounds the Big X on
295 295 XBox 360 controller.
296 296  
  297 +config 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 +
297 309 endif
drivers/input/joystick/Makefile
... ... @@ -29,4 +29,5 @@
29 29 obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
30 30 obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
31 31 obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
  32 +obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
drivers/input/joystick/walkera0701.c
  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 +
  36 +MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
  37 +MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick");
  38 +MODULE_LICENSE("GPL");
  39 +
  40 +static unsigned int walkera0701_pp_no;
  41 +module_param_named(port, walkera0701_pp_no, int, 0);
  42 +MODULE_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 +
  51 +struct 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 +
  64 +static struct walkera_dev w_dev;
  65 +
  66 +static 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 +
  125 +static 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 */
  131 +static 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 +
  175 +static 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 +
  186 +static 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 +
  194 +static 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 +
  201 +static 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 +
  272 +static 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 +
  281 +static int __init walkera0701_init(void)
  282 +{
  283 + return walkera0701_connect(&w_dev, walkera0701_pp_no);
  284 +}
  285 +
  286 +static void __exit walkera0701_exit(void)
  287 +{
  288 + walkera0701_disconnect(&w_dev);
  289 +}
  290 +
  291 +module_init(walkera0701_init);
  292 +module_exit(walkera0701_exit);