Path: blob/master/drivers/input/joystick/zhenhua.c
15111 views
/*1* derived from "twidjoy.c"2*3* Copyright (c) 2008 Martin Kebert4* Copyright (c) 2001 Arndt Schoenewald5* Copyright (c) 2000-2001 Vojtech Pavlik6* Copyright (c) 2000 Mark Fletcher7*8*/910/*11* Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama,12* EasyCopter etc.) as a joystick under Linux.13*14* RC transmitters using Zhen Hua 5-byte protocol are cheap four channels15* transmitters for control a RC planes or RC helicopters with possibility to16* connect on a serial port.17* Data coming from transmitter is in this order:18* 1. byte = synchronisation byte19* 2. byte = X axis20* 3. byte = Y axis21* 4. byte = RZ axis22* 5. byte = Z axis23* (and this is repeated)24*25* For questions or feedback regarding this driver module please contact:26* Martin Kebert <[email protected]> - but I am not a C-programmer nor kernel27* coder :-(28*/2930/*31* This program is free software; you can redistribute it and/or modify32* it under the terms of the GNU General Public License as published by33* the Free Software Foundation; either version 2 of the License, or34* (at your option) any later version.35*36* This program is distributed in the hope that it will be useful,37* but WITHOUT ANY WARRANTY; without even the implied warranty of38* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the39* GNU General Public License for more details.40*41* You should have received a copy of the GNU General Public License42* along with this program; if not, write to the Free Software43* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA44*/4546#include <linux/kernel.h>47#include <linux/module.h>48#include <linux/slab.h>49#include <linux/input.h>50#include <linux/serio.h>51#include <linux/init.h>5253#define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver"5455MODULE_DESCRIPTION(DRIVER_DESC);56MODULE_LICENSE("GPL");5758/*59* Constants.60*/6162#define ZHENHUA_MAX_LENGTH 56364/*65* Zhen Hua data.66*/6768struct zhenhua {69struct input_dev *dev;70int idx;71unsigned char data[ZHENHUA_MAX_LENGTH];72char phys[32];73};747576/* bits in all incoming bytes needs to be "reversed" */77static int zhenhua_bitreverse(int x)78{79x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1);80x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2);81x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4);82return x;83}8485/*86* zhenhua_process_packet() decodes packets the driver receives from the87* RC transmitter. It updates the data accordingly.88*/8990static void zhenhua_process_packet(struct zhenhua *zhenhua)91{92struct input_dev *dev = zhenhua->dev;93unsigned char *data = zhenhua->data;9495input_report_abs(dev, ABS_Y, data[1]);96input_report_abs(dev, ABS_X, data[2]);97input_report_abs(dev, ABS_RZ, data[3]);98input_report_abs(dev, ABS_Z, data[4]);99100input_sync(dev);101}102103/*104* zhenhua_interrupt() is called by the low level driver when characters105* are ready for us. We then buffer them for further processing, or call the106* packet processing routine.107*/108109static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags)110{111struct zhenhua *zhenhua = serio_get_drvdata(serio);112113/* All Zhen Hua packets are 5 bytes. The fact that the first byte114* is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200)115* can be used to check and regain sync. */116117if (data == 0xef)118zhenhua->idx = 0; /* this byte starts a new packet */119else if (zhenhua->idx == 0)120return IRQ_HANDLED; /* wrong MSB -- ignore this byte */121122if (zhenhua->idx < ZHENHUA_MAX_LENGTH)123zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data);124125if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {126zhenhua_process_packet(zhenhua);127zhenhua->idx = 0;128}129130return IRQ_HANDLED;131}132133/*134* zhenhua_disconnect() is the opposite of zhenhua_connect()135*/136137static void zhenhua_disconnect(struct serio *serio)138{139struct zhenhua *zhenhua = serio_get_drvdata(serio);140141serio_close(serio);142serio_set_drvdata(serio, NULL);143input_unregister_device(zhenhua->dev);144kfree(zhenhua);145}146147/*148* zhenhua_connect() is the routine that is called when someone adds a149* new serio device. It looks for the Twiddler, and if found, registers150* it as an input device.151*/152153static int zhenhua_connect(struct serio *serio, struct serio_driver *drv)154{155struct zhenhua *zhenhua;156struct input_dev *input_dev;157int err = -ENOMEM;158159zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL);160input_dev = input_allocate_device();161if (!zhenhua || !input_dev)162goto fail1;163164zhenhua->dev = input_dev;165snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys);166167input_dev->name = "Zhen Hua 5-byte device";168input_dev->phys = zhenhua->phys;169input_dev->id.bustype = BUS_RS232;170input_dev->id.vendor = SERIO_ZHENHUA;171input_dev->id.product = 0x0001;172input_dev->id.version = 0x0100;173input_dev->dev.parent = &serio->dev;174175input_dev->evbit[0] = BIT(EV_ABS);176input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0);177input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0);178input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0);179input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0);180181serio_set_drvdata(serio, zhenhua);182183err = serio_open(serio, drv);184if (err)185goto fail2;186187err = input_register_device(zhenhua->dev);188if (err)189goto fail3;190191return 0;192193fail3: serio_close(serio);194fail2: serio_set_drvdata(serio, NULL);195fail1: input_free_device(input_dev);196kfree(zhenhua);197return err;198}199200/*201* The serio driver structure.202*/203204static struct serio_device_id zhenhua_serio_ids[] = {205{206.type = SERIO_RS232,207.proto = SERIO_ZHENHUA,208.id = SERIO_ANY,209.extra = SERIO_ANY,210},211{ 0 }212};213214MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids);215216static struct serio_driver zhenhua_drv = {217.driver = {218.name = "zhenhua",219},220.description = DRIVER_DESC,221.id_table = zhenhua_serio_ids,222.interrupt = zhenhua_interrupt,223.connect = zhenhua_connect,224.disconnect = zhenhua_disconnect,225};226227/*228* The functions for inserting/removing us as a module.229*/230231static int __init zhenhua_init(void)232{233return serio_register_driver(&zhenhua_drv);234}235236static void __exit zhenhua_exit(void)237{238serio_unregister_driver(&zhenhua_drv);239}240241module_init(zhenhua_init);242module_exit(zhenhua_exit);243244245