Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  derived from "twidjoy.c"
0004  *
0005  *  Copyright (c) 2008 Martin Kebert
0006  *  Copyright (c) 2001 Arndt Schoenewald
0007  *  Copyright (c) 2000-2001 Vojtech Pavlik
0008  *  Copyright (c) 2000 Mark Fletcher
0009  */
0010 
0011 /*
0012  * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama,
0013  * EasyCopter etc.) as a joystick under Linux.
0014  *
0015  * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels
0016  * transmitters for control a RC planes or RC helicopters with possibility to
0017  * connect on a serial port.
0018  * Data coming from transmitter is in this order:
0019  * 1. byte = synchronisation byte
0020  * 2. byte = X axis
0021  * 3. byte = Y axis
0022  * 4. byte = RZ axis
0023  * 5. byte = Z axis
0024  * (and this is repeated)
0025  *
0026  * For questions or feedback regarding this driver module please contact:
0027  * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel
0028  * coder :-(
0029  */
0030 
0031 /*
0032  */
0033 
0034 #include <linux/kernel.h>
0035 #include <linux/module.h>
0036 #include <linux/slab.h>
0037 #include <linux/bitrev.h>
0038 #include <linux/input.h>
0039 #include <linux/serio.h>
0040 
0041 #define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver"
0042 
0043 MODULE_DESCRIPTION(DRIVER_DESC);
0044 MODULE_LICENSE("GPL");
0045 
0046 /*
0047  * Constants.
0048  */
0049 
0050 #define ZHENHUA_MAX_LENGTH 5
0051 
0052 /*
0053  * Zhen Hua data.
0054  */
0055 
0056 struct zhenhua {
0057     struct input_dev *dev;
0058     int idx;
0059     unsigned char data[ZHENHUA_MAX_LENGTH];
0060     char phys[32];
0061 };
0062 
0063 /*
0064  * zhenhua_process_packet() decodes packets the driver receives from the
0065  * RC transmitter. It updates the data accordingly.
0066  */
0067 
0068 static void zhenhua_process_packet(struct zhenhua *zhenhua)
0069 {
0070     struct input_dev *dev = zhenhua->dev;
0071     unsigned char *data = zhenhua->data;
0072 
0073     input_report_abs(dev, ABS_Y, data[1]);
0074     input_report_abs(dev, ABS_X, data[2]);
0075     input_report_abs(dev, ABS_RZ, data[3]);
0076     input_report_abs(dev, ABS_Z, data[4]);
0077 
0078     input_sync(dev);
0079 }
0080 
0081 /*
0082  * zhenhua_interrupt() is called by the low level driver when characters
0083  * are ready for us. We then buffer them for further processing, or call the
0084  * packet processing routine.
0085  */
0086 
0087 static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
0088 {
0089     struct zhenhua *zhenhua = serio_get_drvdata(serio);
0090 
0091     /* All Zhen Hua packets are 5 bytes. The fact that the first byte
0092      * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200)
0093      * can be used to check and regain sync. */
0094 
0095     if (data == 0xef)
0096         zhenhua->idx = 0;   /* this byte starts a new packet */
0097     else if (zhenhua->idx == 0)
0098         return IRQ_HANDLED; /* wrong MSB -- ignore this byte */
0099 
0100     if (zhenhua->idx < ZHENHUA_MAX_LENGTH)
0101         zhenhua->data[zhenhua->idx++] = bitrev8(data);
0102 
0103     if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {
0104         zhenhua_process_packet(zhenhua);
0105         zhenhua->idx = 0;
0106     }
0107 
0108     return IRQ_HANDLED;
0109 }
0110 
0111 /*
0112  * zhenhua_disconnect() is the opposite of zhenhua_connect()
0113  */
0114 
0115 static void zhenhua_disconnect(struct serio *serio)
0116 {
0117     struct zhenhua *zhenhua = serio_get_drvdata(serio);
0118 
0119     serio_close(serio);
0120     serio_set_drvdata(serio, NULL);
0121     input_unregister_device(zhenhua->dev);
0122     kfree(zhenhua);
0123 }
0124 
0125 /*
0126  * zhenhua_connect() is the routine that is called when someone adds a
0127  * new serio device. It looks for the Twiddler, and if found, registers
0128  * it as an input device.
0129  */
0130 
0131 static int zhenhua_connect(struct serio *serio, struct serio_driver *drv)
0132 {
0133     struct zhenhua *zhenhua;
0134     struct input_dev *input_dev;
0135     int err = -ENOMEM;
0136 
0137     zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL);
0138     input_dev = input_allocate_device();
0139     if (!zhenhua || !input_dev)
0140         goto fail1;
0141 
0142     zhenhua->dev = input_dev;
0143     snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys);
0144 
0145     input_dev->name = "Zhen Hua 5-byte device";
0146     input_dev->phys = zhenhua->phys;
0147     input_dev->id.bustype = BUS_RS232;
0148     input_dev->id.vendor = SERIO_ZHENHUA;
0149     input_dev->id.product = 0x0001;
0150     input_dev->id.version = 0x0100;
0151     input_dev->dev.parent = &serio->dev;
0152 
0153     input_dev->evbit[0] = BIT(EV_ABS);
0154     input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0);
0155     input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0);
0156     input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0);
0157     input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0);
0158 
0159     serio_set_drvdata(serio, zhenhua);
0160 
0161     err = serio_open(serio, drv);
0162     if (err)
0163         goto fail2;
0164 
0165     err = input_register_device(zhenhua->dev);
0166     if (err)
0167         goto fail3;
0168 
0169     return 0;
0170 
0171  fail3: serio_close(serio);
0172  fail2: serio_set_drvdata(serio, NULL);
0173  fail1: input_free_device(input_dev);
0174     kfree(zhenhua);
0175     return err;
0176 }
0177 
0178 /*
0179  * The serio driver structure.
0180  */
0181 
0182 static const struct serio_device_id zhenhua_serio_ids[] = {
0183     {
0184         .type   = SERIO_RS232,
0185         .proto  = SERIO_ZHENHUA,
0186         .id = SERIO_ANY,
0187         .extra  = SERIO_ANY,
0188     },
0189     { 0 }
0190 };
0191 
0192 MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids);
0193 
0194 static struct serio_driver zhenhua_drv = {
0195     .driver     = {
0196         .name   = "zhenhua",
0197     },
0198     .description    = DRIVER_DESC,
0199     .id_table   = zhenhua_serio_ids,
0200     .interrupt  = zhenhua_interrupt,
0201     .connect    = zhenhua_connect,
0202     .disconnect = zhenhua_disconnect,
0203 };
0204 
0205 module_serio_driver(zhenhua_drv);