]> zub.fei.tuke.sk Git - walkera0701-joystick/commitdiff
kernel module for walkera WK-0701 TX connected to parport
authorPeter Popovec <popovec@fei.tuke.sk>
Sun, 12 Oct 2008 14:23:06 +0000 (16:23 +0200)
committerPeter Popovec <popovec@fei.tuke.sk>
Sun, 12 Oct 2008 14:23:06 +0000 (16:23 +0200)
Lindent [new file with mode: 0755]
Makefile [new file with mode: 0644]
Walkera_Wk-0701_PCM.pdf [new file with mode: 0755]
walkera0701.c [new file with mode: 0644]

diff --git a/Lindent b/Lindent
new file mode 100755 (executable)
index 0000000..7d8d889
--- /dev/null
+++ b/Lindent
@@ -0,0 +1,2 @@
+#!/bin/sh
+indent -npro -kr -i8 -ts8 -sob -l80 -ss -ncs "$@"
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..9651dc3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+all:    modules
+
+modules:  walkera0701.c
+       rm -f *.o
+       rm -f *.ko
+       make -C /usr/src/linux-`uname -r` SUBDIRS=`pwd` modules
+       strip --strip-unneeded walkera0701.ko
+       
+obj-m += walkera0701.o 
+
+clean:
+       rm -f *.ko
+       rm -f *.o
+       rm -f *.mod.c
+       rm -f *~
+       rm -f .*.cmd
+       rm -rf .tmp_versions
+       rm -f modules.order
+       rm -f Module.symvers
+
+indent:        
+       ./Lindent walkera0701.c
+
+rmmod:
+       rmmod -f walkera0701
+
+insmod:
+       sync;insmod walkera0701.ko
+       
diff --git a/Walkera_Wk-0701_PCM.pdf b/Walkera_Wk-0701_PCM.pdf
new file mode 100755 (executable)
index 0000000..ac94262
Binary files /dev/null and b/Walkera_Wk-0701_PCM.pdf differ
diff --git a/walkera0701.c b/walkera0701.c
new file mode 100644 (file)
index 0000000..a3014e5
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ *  Parallel port to Walkera WK-0701 TX joystick
+ *
+ *  Copyright (c) 2008 Peter Popovec
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+*/
+
+/*
+Cable: (walkera TX to parralel port)
+
+Walkera 0701 TX S-VIDEO connector:
+                  (back side of TX)
+     __   __ 
+    /  |_|  \     connect pin 2 (signal)                             canon25
+   / O 4 3 O \    connect pin 3 (GND)        LED      NPN transistor
+  ( O 2   1 O )                                        / ---------------10
+   \   ___   /      2 o-----------------------|>|-----|
+    | [___] |                                          V\
+     -------        3 o --------------------------------v-------------- 25  
+
+Frame format:
+Based on walkera WK-0701 PCM Format description by Shaul Eizikovich.
+
+Signal pulses:
+                   (ANALOG)
+    SYNC      BIN   OCT
+  +---------+      +------+   
+  |         |      |      |
+--+         +------+      +---
+
+Frame: 
+ SYNC , BIN1, OCT1, BIN2, OCT2 ... BIN24, OCT24, BIN25, next frame SYNC .. 
+
+pulse length:
+   Binary values:              Analog octal values:
+
+   288 uS Binary 0             318 uS       000
+   438 uS Binary 1             398 uS       001
+                               478 uS       010
+                               558 uS       011
+                               638 uS       100
+  1306 uS SYNC                 718 uS       101
+                               798 uS       110
+                               878 uS       111
+
+24 bin+oct values + 1 bin value = 24*4+1 bits  = 97 bits
+
+all times in code  in ns
+*/
+#define RESERVE 20000
+#define SYNC_PULSE 1306000
+#define BIN0_PULSE 288000
+#define BIN1_PULSE 438000
+
+#define ANALOG_MIN_PULSE 318000
+#define ANALOG_MAX_PULSE 878000
+#define ANALOG_DELTA 80000
+
+#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2)
+
+#define NO_SYNC 25
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/hrtimer.h>
+
+#define DRIVER_DESC     "Walkera WK-0701 TX as joystick"
+
+MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned int walkera0701_pp_no;
+module_param_named(port, walkera0701_pp_no, int, 0);
+MODULE_PARM_DESC(port,
+                "Parallel port adapter for walkera WK-0701 TX (default is 0)");
+
+static unsigned char DATA[25];
+static unsigned char counter;
+static struct hrtimer walkera0701_timer;
+
+static u64 IRQtime, IRQlasttime;
+
+struct input_dev *input_dev;
+struct parport *walkera0701_parport;
+struct pardevice *walkera0701_pardevice;
+
+//TODO real paket parsing 
+static inline void walkera0701_parse_frame(void)
+{
+       int i;
+       int val1, val2, val3, val4, val5, val6, val7, val8, magic;
+       int crc1, crc2;
+       for (crc1 = crc2 = i = 0; i < 10; i++) {
+               crc1 += DATA[i] & 7;
+               crc2 += (DATA[i] & 8) >> 3;
+       }
+       if ((DATA[10] & 7) != (crc1 & 7))
+               printk(KERN_INFO "Bad OCT 1-4 checksum\n");
+       if (((DATA[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
+               printk(KERN_INFO "Bad BIN - OCT 1-4 checksum\n");
+
+       for (crc1 = crc2 = 0, i = 11; i < 23; i++) {
+               crc1 += DATA[i] & 7;
+               crc2 += (DATA[i] & 8) >> 3;
+       }
+       if ((DATA[23] & 7) != (crc1 & 7))
+               printk(KERN_INFO "Bad OCT 5-8 checksum\n");
+       if (((DATA[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
+               printk(KERN_INFO "Bad BIN - OCT 5-8 checksum\n");
+
+       val1 = ((DATA[0] & 7) * 256 + DATA[1] * 16 + DATA[2]) >> 2;
+       val1 *= ((DATA[0] >> 2) & 2) - 1;       //sign
+       val2 = (DATA[2] & 1) << 8 | (DATA[3] << 4) | DATA[4];
+       val2 *= (DATA[2] & 2) - 1;      //sign
+       val3 = ((DATA[5] & 7) * 256 + DATA[6] * 16 + DATA[7]) >> 2;
+       val3 *= ((DATA[5] >> 2) & 2) - 1;       //sign
+       val4 = (DATA[7] & 1) << 8 | (DATA[8] << 4) | DATA[9];
+       val4 *= (DATA[7] & 2) - 1;      //sign
+       val5 = ((DATA[11] & 7) * 256 + DATA[12] * 16 + DATA[13]) >> 2;
+       val5 *= ((DATA[11] >> 2) & 2) - 1;      //sign
+       val6 = (DATA[13] & 1) << 8 | (DATA[14] << 4) | DATA[15];
+       val6 *= (DATA[13] & 2) - 1;     //sign
+       val7 = ((DATA[16] & 7) * 256 + DATA[17] * 16 + DATA[18]) >> 2;
+       val7 *= ((DATA[16] >> 2) & 2) - 1;      //sign
+       val8 = (DATA[18] & 1) << 8 | (DATA[19] << 4) | DATA[20];
+       val8 *= (DATA[18] & 2) - 1;     //sign
+
+       magic = (DATA[21] << 4) | DATA[22];
+/*     printk(KERN_INFO "%4d %4d %4d %4d  %4d %4d %4d %4d (magic %2x %d)\n",
+              val1, val2, val3, val4, val5, val6, val7, val8, magic,
+              (DATA[24] & 8) >> 3);
+*/
+       input_report_abs(input_dev, ABS_X, val2);
+       input_report_abs(input_dev, ABS_Y, val1);
+       input_report_abs(input_dev, ABS_THROTTLE, val3);
+       input_report_abs(input_dev, ABS_RUDDER, val4);
+       input_report_abs(input_dev, ABS_MISC, val7);
+       input_report_key(input_dev,BTN_GEAR_DOWN,val5 >0);
+}
+
+static int ACK;
+static inline int read_ack(void)
+{
+       return (parport_read_status(walkera0701_pardevice->port) & 0x40);
+}
+
+// falling edge, prepare to BIN value calculation 
+static void walkera0701_irq_handler(void *dev_id)
+{
+       u64 pulseTime;
+       IRQtime = ktime_to_ns(ktime_get());
+       pulseTime = IRQtime - IRQlasttime;
+       IRQlasttime = IRQtime;
+       //cancel timer,if in handler or active do resync
+       if (unlikely(0 != hrtimer_try_to_cancel(&walkera0701_timer))) {
+               counter = NO_SYNC;
+               return;
+       }
+       if (unlikely(counter < NO_SYNC)) {
+               if (ACK) {
+                       pulseTime -= BIN1_PULSE;
+                       DATA[counter] = 8;
+               } else {
+                       pulseTime -= BIN0_PULSE;
+                       DATA[counter] = 0;
+               }
+               if (unlikely(counter == 24)) {  //full frame
+                       walkera0701_parse_frame();
+                       counter = NO_SYNC;
+                       if (likely(abs(pulseTime - SYNC_PULSE) < RESERVE))      //new frame sync
+                               counter = 0;
+               } else {
+                       if (likely((pulseTime > (ANALOG_MIN_PULSE - RESERVE))
+                                  && (pulseTime <
+                                      (ANALOG_MAX_PULSE + RESERVE)))) {
+                               pulseTime -= (ANALOG_MIN_PULSE - RESERVE);
+                               pulseTime = (u32) pulseTime / ANALOG_DELTA;     //overtiping is safe, pulsetime < s32.. 
+                               DATA[counter++] |= (pulseTime & 7);
+                       } else
+                               counter = NO_SYNC;
+               }
+       } else {
+               if (unlikely(abs(pulseTime - SYNC_PULSE - BIN0_PULSE) < (RESERVE + BIN1_PULSE - BIN0_PULSE)))   //frame sync ..
+                       counter = 0;
+       }
+       hrtimer_start(&walkera0701_timer, ktime_set(0, BIN_SAMPLE),
+                     HRTIMER_MODE_REL);
+
+       return;
+}
+
+static enum hrtimer_restart walkera0701_timer_handler(struct hrtimer
+                                                     *handle)
+{
+       ACK = read_ack();
+       return HRTIMER_NORESTART;
+}
+static int walkera0701_open(struct input_dev *dev)
+{
+       parport_enable_irq(walkera0701_parport);
+       return (0);
+}
+static void walkera0701_close(struct input_dev *dev)
+{
+       parport_disable_irq(walkera0701_parport);
+
+}
+static int walkera0701_connect(int parport)
+{
+       int err;
+       walkera0701_parport = parport_find_number(parport);
+       if (walkera0701_parport == NULL) {
+               printk(KERN_ERR "walkera0701: no such parport\n");
+               return -ENODEV;
+       }
+       if (walkera0701_parport->irq == -1) {
+               printk(KERN_ERR "walkera0701: parport without interrupt\n");
+               return -ENODEV;
+       }
+       walkera0701_pardevice =
+           parport_register_device(walkera0701_parport, "walkera0701",
+                                   NULL, NULL, walkera0701_irq_handler,
+                                   PARPORT_DEV_EXCL, NULL);
+       if (!walkera0701_pardevice) {
+               printk(KERN_ERR "walkera0701: parport busy\n");
+               return -EBUSY;
+       }
+       parport_negotiate(walkera0701_pardevice->port, IEEE1284_MODE_COMPAT);
+//TODO needed ?
+//      parport_put_port(walkera0701_parport);
+       if (parport_claim(walkera0701_pardevice)) {
+               printk(KERN_ERR
+                      "walkera0701: parport unable to claim, busy ?\n");
+               parport_unregister_device(walkera0701_pardevice);
+               return -EBUSY;
+       }
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               printk(KERN_ERR
+                      "walkera0701 unable to allocate input device\n");
+               parport_release(walkera0701_pardevice);
+               parport_unregister_device(walkera0701_pardevice);
+               return -ENOMEM;
+       }
+
+       input_dev->name = "Walkera WK-0701 TX";
+       input_dev->phys = walkera0701_parport->name;
+       input_dev->id.bustype = BUS_PARPORT;
+       input_dev->id.vendor = 0x0001;  //TODO
+       input_dev->id.product = 0x0001; //TODO
+       input_dev->id.version = 0x0100; //TODO
+       input_dev->open = walkera0701_open;
+       input_dev->close = walkera0701_close;
+
+       input_dev->evbit[0] = BIT(EV_ABS)|BIT_MASK(EV_KEY) ;
+       input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)]=BIT_MASK(BTN_GEAR_DOWN);
+       
+       input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0);
+       input_set_abs_params(input_dev, ABS_THROTTLE, -512, 512, 0, 0);
+       input_set_abs_params(input_dev, ABS_RUDDER, -512, 512, 0, 0);
+       input_set_abs_params(input_dev, ABS_MISC, -512, 512, 0, 0);
+       err = input_register_device(input_dev);
+       if (err) {
+               input_free_device(input_dev);
+               parport_release(walkera0701_pardevice);
+               parport_unregister_device(walkera0701_pardevice);
+               parport_put_port(walkera0701_parport);
+               return (err);
+       }
+       parport_enable_irq(walkera0701_parport);
+       return 0;
+}
+
+static int __init walkera0701_init(void)
+{
+       hrtimer_init(&walkera0701_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       walkera0701_timer.function = walkera0701_timer_handler;
+       return walkera0701_connect(walkera0701_pp_no);
+}
+static void __exit walkera0701_exit(void)
+{
+       hrtimer_cancel(&walkera0701_timer);
+       input_unregister_device(input_dev);
+       input_free_device(input_dev);
+       if (walkera0701_pardevice) {
+               parport_release(walkera0701_pardevice);
+               parport_unregister_device(walkera0701_pardevice);
+       }
+}
+
+module_init(walkera0701_init);
+module_exit(walkera0701_exit);