From: Peter Popovec Date: Sun, 12 Oct 2008 14:23:06 +0000 (+0200) Subject: kernel module for walkera WK-0701 TX connected to parport X-Git-Tag: stable1.0-rc1~3 X-Git-Url: http://zub.fei.tuke.sk/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b245542aba7de930575afbf4bd92ec4126151719;p=walkera0701-joystick kernel module for walkera WK-0701 TX connected to parport --- diff --git a/Lindent b/Lindent new file mode 100755 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 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 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 index 0000000..a3014e5 --- /dev/null +++ b/walkera0701.c @@ -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 +#include +#include +#include +#include + +#define DRIVER_DESC "Walkera WK-0701 TX as joystick" + +MODULE_AUTHOR("Peter Popovec "); +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);