13e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 23e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * derived from "twidjoy.c" 33e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 43e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Copyright (c) 2008 Martin Kebert 53e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Copyright (c) 2001 Arndt Schoenewald 63e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Copyright (c) 2000-2001 Vojtech Pavlik 73e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Copyright (c) 2000 Mark Fletcher 83e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 93e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 103e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 113e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 123e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama, 133e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * EasyCopter etc.) as a joystick under Linux. 143e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 153e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels 163e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * transmitters for control a RC planes or RC helicopters with possibility to 173e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * connect on a serial port. 183e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Data coming from transmitter is in this order: 193e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 1. byte = synchronisation byte 203e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 2. byte = X axis 213e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 3. byte = Y axis 223e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 4. byte = RZ axis 233e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 5. byte = Z axis 243e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * (and this is repeated) 253e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 263e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * For questions or feedback regarding this driver module please contact: 273e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel 283e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * coder :-( 293e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 303e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 313e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 323e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * This program is free software; you can redistribute it and/or modify 333e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * it under the terms of the GNU General Public License as published by 343e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * the Free Software Foundation; either version 2 of the License, or 353e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * (at your option) any later version. 363e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 373e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * This program is distributed in the hope that it will be useful, 383e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * but WITHOUT ANY WARRANTY; without even the implied warranty of 393e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 403e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * GNU General Public License for more details. 413e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * 423e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * You should have received a copy of the GNU General Public License 433e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * along with this program; if not, write to the Free Software 443e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 453e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 463e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 473e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#include <linux/kernel.h> 483e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#include <linux/module.h> 493e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#include <linux/slab.h> 503e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#include <linux/input.h> 513e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#include <linux/serio.h> 523e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 533e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver" 543e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 553e24e2b5ae03394d9510530f9dd973050fd18730Martin KebertMODULE_DESCRIPTION(DRIVER_DESC); 563e24e2b5ae03394d9510530f9dd973050fd18730Martin KebertMODULE_LICENSE("GPL"); 573e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 583e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 593e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Constants. 603e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 613e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 623e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert#define ZHENHUA_MAX_LENGTH 5 633e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 643e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 653e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * Zhen Hua data. 663e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 673e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 683e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstruct zhenhua { 693e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert struct input_dev *dev; 703e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert int idx; 713e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert unsigned char data[ZHENHUA_MAX_LENGTH]; 723e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert char phys[32]; 733e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert}; 743e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 753e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 763e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* bits in all incoming bytes needs to be "reversed" */ 773e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic int zhenhua_bitreverse(int x) 783e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert{ 793e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); 803e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); 813e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4); 823e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert return x; 833e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert} 843e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 853e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 863e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * zhenhua_process_packet() decodes packets the driver receives from the 873e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * RC transmitter. It updates the data accordingly. 883e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 893e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 903e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic void zhenhua_process_packet(struct zhenhua *zhenhua) 913e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert{ 923e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert struct input_dev *dev = zhenhua->dev; 933e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert unsigned char *data = zhenhua->data; 943e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 953e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_report_abs(dev, ABS_Y, data[1]); 963e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_report_abs(dev, ABS_X, data[2]); 973e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_report_abs(dev, ABS_RZ, data[3]); 983e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_report_abs(dev, ABS_Z, data[4]); 993e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1003e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_sync(dev); 1013e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert} 1023e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1033e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 1043e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * zhenhua_interrupt() is called by the low level driver when characters 1053e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * are ready for us. We then buffer them for further processing, or call the 1063e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * packet processing routine. 1073e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 1083e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1093e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags) 1103e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert{ 1113e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert struct zhenhua *zhenhua = serio_get_drvdata(serio); 1123e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1133e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert /* All Zhen Hua packets are 5 bytes. The fact that the first byte 1143e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200) 1153e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * can be used to check and regain sync. */ 1163e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1173e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert if (data == 0xef) 1183e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert zhenhua->idx = 0; /* this byte starts a new packet */ 1193e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert else if (zhenhua->idx == 0) 1203e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ 1213e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1223e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert if (zhenhua->idx < ZHENHUA_MAX_LENGTH) 1233e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data); 1243e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1253e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert if (zhenhua->idx == ZHENHUA_MAX_LENGTH) { 1263e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert zhenhua_process_packet(zhenhua); 1273e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert zhenhua->idx = 0; 1283e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert } 1293e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1303e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert return IRQ_HANDLED; 1313e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert} 1323e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1333e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 1343e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * zhenhua_disconnect() is the opposite of zhenhua_connect() 1353e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 1363e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1373e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic void zhenhua_disconnect(struct serio *serio) 1383e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert{ 1393e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert struct zhenhua *zhenhua = serio_get_drvdata(serio); 1403e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1413e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert serio_close(serio); 1423e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert serio_set_drvdata(serio, NULL); 1433e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_unregister_device(zhenhua->dev); 1443e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert kfree(zhenhua); 1453e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert} 1463e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1473e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 1483e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * zhenhua_connect() is the routine that is called when someone adds a 1493e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * new serio device. It looks for the Twiddler, and if found, registers 1503e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * it as an input device. 1513e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 1523e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1533e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic int zhenhua_connect(struct serio *serio, struct serio_driver *drv) 1543e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert{ 1553e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert struct zhenhua *zhenhua; 1563e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert struct input_dev *input_dev; 1573e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert int err = -ENOMEM; 1583e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1593e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL); 1603e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev = input_allocate_device(); 1613e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert if (!zhenhua || !input_dev) 1623e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert goto fail1; 1633e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1643e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert zhenhua->dev = input_dev; 1653e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys); 1663e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1673e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->name = "Zhen Hua 5-byte device"; 1683e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->phys = zhenhua->phys; 1693e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->id.bustype = BUS_RS232; 1703e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->id.vendor = SERIO_ZHENHUA; 1713e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->id.product = 0x0001; 1723e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->id.version = 0x0100; 1733e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->dev.parent = &serio->dev; 1743e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1753e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_dev->evbit[0] = BIT(EV_ABS); 1763e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0); 1773e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0); 1783e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0); 1793e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0); 1803e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1813e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert serio_set_drvdata(serio, zhenhua); 1823e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1833e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert err = serio_open(serio, drv); 1843e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert if (err) 1853e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert goto fail2; 1863e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1873e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert err = input_register_device(zhenhua->dev); 1883e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert if (err) 1893e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert goto fail3; 1903e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1913e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert return 0; 1923e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 1933e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert fail3: serio_close(serio); 1943e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert fail2: serio_set_drvdata(serio, NULL); 1953e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert fail1: input_free_device(input_dev); 1963e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert kfree(zhenhua); 1973e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert return err; 1983e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert} 1993e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 2003e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert/* 2013e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert * The serio driver structure. 2023e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert */ 2033e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 2043e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic struct serio_device_id zhenhua_serio_ids[] = { 2053e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert { 2063e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .type = SERIO_RS232, 2073e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .proto = SERIO_ZHENHUA, 2083e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .id = SERIO_ANY, 2093e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .extra = SERIO_ANY, 2103e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert }, 2113e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert { 0 } 2123e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert}; 2133e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 2143e24e2b5ae03394d9510530f9dd973050fd18730Martin KebertMODULE_DEVICE_TABLE(serio, zhenhua_serio_ids); 2153e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 2163e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebertstatic struct serio_driver zhenhua_drv = { 2173e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .driver = { 2183e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .name = "zhenhua", 2193e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert }, 2203e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .description = DRIVER_DESC, 2213e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .id_table = zhenhua_serio_ids, 2223e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .interrupt = zhenhua_interrupt, 2233e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .connect = zhenhua_connect, 2243e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert .disconnect = zhenhua_disconnect, 2253e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert}; 2263e24e2b5ae03394d9510530f9dd973050fd18730Martin Kebert 22765ac9f7a23c934ee8c40dc20955e75db4924bfeaAxel Linmodule_serio_driver(zhenhua_drv); 228