Path: blob/master/drivers/input/joystick/spaceball.c
15111 views
/*1* Copyright (c) 1999-2001 Vojtech Pavlik2*3* Based on the work of:4* David Thompson5* Joseph Krahn6*/78/*9* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux10*/1112/*13* This program is free software; you can redistribute it and/or modify14* it under the terms of the GNU General Public License as published by15* the Free Software Foundation; either version 2 of the License, or16* (at your option) any later version.17*18* This program is distributed in the hope that it will be useful,19* but WITHOUT ANY WARRANTY; without even the implied warranty of20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the21* GNU General Public License for more details.22*23* You should have received a copy of the GNU General Public License24* along with this program; if not, write to the Free Software25* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA26*27* Should you need to contact me, the author, you can do so either by28* e-mail - mail your message to <[email protected]>, or by paper mail:29* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic30*/3132#include <linux/kernel.h>33#include <linux/slab.h>34#include <linux/module.h>35#include <linux/init.h>36#include <linux/input.h>37#include <linux/serio.h>3839#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver"4041MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");42MODULE_DESCRIPTION(DRIVER_DESC);43MODULE_LICENSE("GPL");4445/*46* Constants.47*/4849#define SPACEBALL_MAX_LENGTH 12850#define SPACEBALL_MAX_ID 95152#define SPACEBALL_1003 153#define SPACEBALL_2003B 354#define SPACEBALL_2003C 455#define SPACEBALL_3003C 756#define SPACEBALL_4000FLX 857#define SPACEBALL_4000FLX_L 95859static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };60static char *spaceball_names[] = {61"?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B",62"SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController",63"SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" };6465/*66* Per-Ball data.67*/6869struct spaceball {70struct input_dev *dev;71int idx;72int escape;73unsigned char data[SPACEBALL_MAX_LENGTH];74char phys[32];75};7677/*78* spaceball_process_packet() decodes packets the driver receives from the79* SpaceBall.80*/8182static void spaceball_process_packet(struct spaceball* spaceball)83{84struct input_dev *dev = spaceball->dev;85unsigned char *data = spaceball->data;86int i;8788if (spaceball->idx < 2) return;8990switch (spaceball->data[0]) {9192case 'D': /* Ball data */93if (spaceball->idx != 15) return;94for (i = 0; i < 6; i++)95input_report_abs(dev, spaceball_axes[i],96(__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));97break;9899case 'K': /* Button data */100if (spaceball->idx != 3) return;101input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20));102input_report_key(dev, BTN_2, data[2] & 0x02);103input_report_key(dev, BTN_3, data[2] & 0x04);104input_report_key(dev, BTN_4, data[2] & 0x08);105input_report_key(dev, BTN_5, data[1] & 0x01);106input_report_key(dev, BTN_6, data[1] & 0x02);107input_report_key(dev, BTN_7, data[1] & 0x04);108input_report_key(dev, BTN_8, data[1] & 0x10);109break;110111case '.': /* Advanced button data */112if (spaceball->idx != 3) return;113input_report_key(dev, BTN_1, data[2] & 0x01);114input_report_key(dev, BTN_2, data[2] & 0x02);115input_report_key(dev, BTN_3, data[2] & 0x04);116input_report_key(dev, BTN_4, data[2] & 0x08);117input_report_key(dev, BTN_5, data[2] & 0x10);118input_report_key(dev, BTN_6, data[2] & 0x20);119input_report_key(dev, BTN_7, data[2] & 0x80);120input_report_key(dev, BTN_8, data[1] & 0x01);121input_report_key(dev, BTN_9, data[1] & 0x02);122input_report_key(dev, BTN_A, data[1] & 0x04);123input_report_key(dev, BTN_B, data[1] & 0x08);124input_report_key(dev, BTN_C, data[1] & 0x10);125input_report_key(dev, BTN_MODE, data[1] & 0x20);126break;127128case 'E': /* Device error */129spaceball->data[spaceball->idx - 1] = 0;130printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);131break;132133case '?': /* Bad command packet */134spaceball->data[spaceball->idx - 1] = 0;135printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);136break;137}138139input_sync(dev);140}141142/*143* Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,144* and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which145* can occur in the axis values.146*/147148static irqreturn_t spaceball_interrupt(struct serio *serio,149unsigned char data, unsigned int flags)150{151struct spaceball *spaceball = serio_get_drvdata(serio);152153switch (data) {154case 0xd:155spaceball_process_packet(spaceball);156spaceball->idx = 0;157spaceball->escape = 0;158break;159case '^':160if (!spaceball->escape) {161spaceball->escape = 1;162break;163}164spaceball->escape = 0;165case 'M':166case 'Q':167case 'S':168if (spaceball->escape) {169spaceball->escape = 0;170data &= 0x1f;171}172default:173if (spaceball->escape)174spaceball->escape = 0;175if (spaceball->idx < SPACEBALL_MAX_LENGTH)176spaceball->data[spaceball->idx++] = data;177break;178}179return IRQ_HANDLED;180}181182/*183* spaceball_disconnect() is the opposite of spaceball_connect()184*/185186static void spaceball_disconnect(struct serio *serio)187{188struct spaceball* spaceball = serio_get_drvdata(serio);189190serio_close(serio);191serio_set_drvdata(serio, NULL);192input_unregister_device(spaceball->dev);193kfree(spaceball);194}195196/*197* spaceball_connect() is the routine that is called when someone adds a198* new serio device that supports Spaceball protocol and registers it as199* an input device.200*/201202static int spaceball_connect(struct serio *serio, struct serio_driver *drv)203{204struct spaceball *spaceball;205struct input_dev *input_dev;206int err = -ENOMEM;207int i, id;208209if ((id = serio->id.id) > SPACEBALL_MAX_ID)210return -ENODEV;211212spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL);213input_dev = input_allocate_device();214if (!spaceball || !input_dev)215goto fail1;216217spaceball->dev = input_dev;218snprintf(spaceball->phys, sizeof(spaceball->phys), "%s/input0", serio->phys);219220input_dev->name = spaceball_names[id];221input_dev->phys = spaceball->phys;222input_dev->id.bustype = BUS_RS232;223input_dev->id.vendor = SERIO_SPACEBALL;224input_dev->id.product = id;225input_dev->id.version = 0x0100;226input_dev->dev.parent = &serio->dev;227228input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);229230switch (id) {231case SPACEBALL_4000FLX:232case SPACEBALL_4000FLX_L:233input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_9);234input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) |235BIT_MASK(BTN_B) | BIT_MASK(BTN_C) |236BIT_MASK(BTN_MODE);237default:238input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) |239BIT_MASK(BTN_3) | BIT_MASK(BTN_4) |240BIT_MASK(BTN_5) | BIT_MASK(BTN_6) |241BIT_MASK(BTN_7) | BIT_MASK(BTN_8);242case SPACEBALL_3003C:243input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) |244BIT_MASK(BTN_8);245}246247for (i = 0; i < 3; i++) {248input_set_abs_params(input_dev, ABS_X + i, -8000, 8000, 8, 40);249input_set_abs_params(input_dev, ABS_RX + i, -1600, 1600, 2, 8);250}251252serio_set_drvdata(serio, spaceball);253254err = serio_open(serio, drv);255if (err)256goto fail2;257258err = input_register_device(spaceball->dev);259if (err)260goto fail3;261262return 0;263264fail3: serio_close(serio);265fail2: serio_set_drvdata(serio, NULL);266fail1: input_free_device(input_dev);267kfree(spaceball);268return err;269}270271/*272* The serio driver structure.273*/274275static struct serio_device_id spaceball_serio_ids[] = {276{277.type = SERIO_RS232,278.proto = SERIO_SPACEBALL,279.id = SERIO_ANY,280.extra = SERIO_ANY,281},282{ 0 }283};284285MODULE_DEVICE_TABLE(serio, spaceball_serio_ids);286287static struct serio_driver spaceball_drv = {288.driver = {289.name = "spaceball",290},291.description = DRIVER_DESC,292.id_table = spaceball_serio_ids,293.interrupt = spaceball_interrupt,294.connect = spaceball_connect,295.disconnect = spaceball_disconnect,296};297298/*299* The functions for inserting/removing us as a module.300*/301302static int __init spaceball_init(void)303{304return serio_register_driver(&spaceball_drv);305}306307static void __exit spaceball_exit(void)308{309serio_unregister_driver(&spaceball_drv);310}311312module_init(spaceball_init);313module_exit(spaceball_exit);314315316