cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

zhenhua.c (5003B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  derived from "twidjoy.c"
      4 *
      5 *  Copyright (c) 2008 Martin Kebert
      6 *  Copyright (c) 2001 Arndt Schoenewald
      7 *  Copyright (c) 2000-2001 Vojtech Pavlik
      8 *  Copyright (c) 2000 Mark Fletcher
      9 */
     10
     11/*
     12 * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama,
     13 * EasyCopter etc.) as a joystick under Linux.
     14 *
     15 * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels
     16 * transmitters for control a RC planes or RC helicopters with possibility to
     17 * connect on a serial port.
     18 * Data coming from transmitter is in this order:
     19 * 1. byte = synchronisation byte
     20 * 2. byte = X axis
     21 * 3. byte = Y axis
     22 * 4. byte = RZ axis
     23 * 5. byte = Z axis
     24 * (and this is repeated)
     25 *
     26 * For questions or feedback regarding this driver module please contact:
     27 * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel
     28 * coder :-(
     29 */
     30
     31/*
     32 */
     33
     34#include <linux/kernel.h>
     35#include <linux/module.h>
     36#include <linux/slab.h>
     37#include <linux/bitrev.h>
     38#include <linux/input.h>
     39#include <linux/serio.h>
     40
     41#define DRIVER_DESC	"RC transmitter with 5-byte Zhen Hua protocol joystick driver"
     42
     43MODULE_DESCRIPTION(DRIVER_DESC);
     44MODULE_LICENSE("GPL");
     45
     46/*
     47 * Constants.
     48 */
     49
     50#define ZHENHUA_MAX_LENGTH 5
     51
     52/*
     53 * Zhen Hua data.
     54 */
     55
     56struct zhenhua {
     57	struct input_dev *dev;
     58	int idx;
     59	unsigned char data[ZHENHUA_MAX_LENGTH];
     60	char phys[32];
     61};
     62
     63/*
     64 * zhenhua_process_packet() decodes packets the driver receives from the
     65 * RC transmitter. It updates the data accordingly.
     66 */
     67
     68static void zhenhua_process_packet(struct zhenhua *zhenhua)
     69{
     70	struct input_dev *dev = zhenhua->dev;
     71	unsigned char *data = zhenhua->data;
     72
     73	input_report_abs(dev, ABS_Y, data[1]);
     74	input_report_abs(dev, ABS_X, data[2]);
     75	input_report_abs(dev, ABS_RZ, data[3]);
     76	input_report_abs(dev, ABS_Z, data[4]);
     77
     78	input_sync(dev);
     79}
     80
     81/*
     82 * zhenhua_interrupt() is called by the low level driver when characters
     83 * are ready for us. We then buffer them for further processing, or call the
     84 * packet processing routine.
     85 */
     86
     87static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
     88{
     89	struct zhenhua *zhenhua = serio_get_drvdata(serio);
     90
     91	/* All Zhen Hua packets are 5 bytes. The fact that the first byte
     92	 * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200)
     93	 * can be used to check and regain sync. */
     94
     95	if (data == 0xef)
     96		zhenhua->idx = 0;	/* this byte starts a new packet */
     97	else if (zhenhua->idx == 0)
     98		return IRQ_HANDLED;	/* wrong MSB -- ignore this byte */
     99
    100	if (zhenhua->idx < ZHENHUA_MAX_LENGTH)
    101		zhenhua->data[zhenhua->idx++] = bitrev8(data);
    102
    103	if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {
    104		zhenhua_process_packet(zhenhua);
    105		zhenhua->idx = 0;
    106	}
    107
    108	return IRQ_HANDLED;
    109}
    110
    111/*
    112 * zhenhua_disconnect() is the opposite of zhenhua_connect()
    113 */
    114
    115static void zhenhua_disconnect(struct serio *serio)
    116{
    117	struct zhenhua *zhenhua = serio_get_drvdata(serio);
    118
    119	serio_close(serio);
    120	serio_set_drvdata(serio, NULL);
    121	input_unregister_device(zhenhua->dev);
    122	kfree(zhenhua);
    123}
    124
    125/*
    126 * zhenhua_connect() is the routine that is called when someone adds a
    127 * new serio device. It looks for the Twiddler, and if found, registers
    128 * it as an input device.
    129 */
    130
    131static int zhenhua_connect(struct serio *serio, struct serio_driver *drv)
    132{
    133	struct zhenhua *zhenhua;
    134	struct input_dev *input_dev;
    135	int err = -ENOMEM;
    136
    137	zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL);
    138	input_dev = input_allocate_device();
    139	if (!zhenhua || !input_dev)
    140		goto fail1;
    141
    142	zhenhua->dev = input_dev;
    143	snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys);
    144
    145	input_dev->name = "Zhen Hua 5-byte device";
    146	input_dev->phys = zhenhua->phys;
    147	input_dev->id.bustype = BUS_RS232;
    148	input_dev->id.vendor = SERIO_ZHENHUA;
    149	input_dev->id.product = 0x0001;
    150	input_dev->id.version = 0x0100;
    151	input_dev->dev.parent = &serio->dev;
    152
    153	input_dev->evbit[0] = BIT(EV_ABS);
    154	input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0);
    155	input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0);
    156	input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0);
    157	input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0);
    158
    159	serio_set_drvdata(serio, zhenhua);
    160
    161	err = serio_open(serio, drv);
    162	if (err)
    163		goto fail2;
    164
    165	err = input_register_device(zhenhua->dev);
    166	if (err)
    167		goto fail3;
    168
    169	return 0;
    170
    171 fail3:	serio_close(serio);
    172 fail2:	serio_set_drvdata(serio, NULL);
    173 fail1:	input_free_device(input_dev);
    174	kfree(zhenhua);
    175	return err;
    176}
    177
    178/*
    179 * The serio driver structure.
    180 */
    181
    182static const struct serio_device_id zhenhua_serio_ids[] = {
    183	{
    184		.type	= SERIO_RS232,
    185		.proto	= SERIO_ZHENHUA,
    186		.id	= SERIO_ANY,
    187		.extra	= SERIO_ANY,
    188	},
    189	{ 0 }
    190};
    191
    192MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids);
    193
    194static struct serio_driver zhenhua_drv = {
    195	.driver		= {
    196		.name	= "zhenhua",
    197	},
    198	.description	= DRIVER_DESC,
    199	.id_table	= zhenhua_serio_ids,
    200	.interrupt	= zhenhua_interrupt,
    201	.connect	= zhenhua_connect,
    202	.disconnect	= zhenhua_disconnect,
    203};
    204
    205module_serio_driver(zhenhua_drv);