From 07b8fb25a96707cdd01072fd6298c2b8570801be Mon Sep 17 00:00:00 2001 From: Vojtech Pavlik Date: Wed, 3 Jul 2002 18:49:42 +0200 Subject: [PATCH] Update the iforce driver to the latest revision (it now lives in a separate directory), add twiddler and guillemot drivers. --- drivers/input/joystick/Config.help | 18 + drivers/input/joystick/Config.in | 2 + drivers/input/joystick/Makefile | 46 +- drivers/input/joystick/guillemot.c | 285 ++++ drivers/input/joystick/iforce.c | 1224 ----------------- drivers/input/joystick/iforce/Makefile | 41 + drivers/input/joystick/iforce/iforce-ff.c | 543 ++++++++ drivers/input/joystick/iforce/iforce-main.c | 543 ++++++++ .../input/joystick/iforce/iforce-packets.c | 314 +++++ drivers/input/joystick/iforce/iforce-serio.c | 166 +++ drivers/input/joystick/iforce/iforce-usb.c | 214 +++ drivers/input/joystick/iforce/iforce.h | 194 +++ drivers/input/joystick/twidjoy.c | 264 ++++ 13 files changed, 2611 insertions(+), 1243 deletions(-) create mode 100644 drivers/input/joystick/guillemot.c delete mode 100644 drivers/input/joystick/iforce.c create mode 100644 drivers/input/joystick/iforce/Makefile create mode 100644 drivers/input/joystick/iforce/iforce-ff.c create mode 100644 drivers/input/joystick/iforce/iforce-main.c create mode 100644 drivers/input/joystick/iforce/iforce-packets.c create mode 100644 drivers/input/joystick/iforce/iforce-serio.c create mode 100644 drivers/input/joystick/iforce/iforce-usb.c create mode 100644 drivers/input/joystick/iforce/iforce.h create mode 100644 drivers/input/joystick/twidjoy.c diff --git a/drivers/input/joystick/Config.help b/drivers/input/joystick/Config.help index 1b4187bbb8a3..a8998c103900 100644 --- a/drivers/input/joystick/Config.help +++ b/drivers/input/joystick/Config.help @@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP The module will be called grip.o. If you want to compile it as a module, say M here and read . +CONFIG_JOYSTICK_GUILLEMOT + Say Y here if you have a Guillemot joystick using a digital + protocol over the PC gameport. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called guillemot.o. If you want to compile it as a + module, say M here and read . + CONFIG_JOYSTICK_INTERACT Say Y here if you have an InterAct gameport or joystick communicating digitally over the gameport. @@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER The module will be called stinger.o. If you want to compile it as a module, say M here and read . +CONFIG_JOYSTICK_TWIDDLER + Say Y here if you have a Handykey Twiddler connected to your + computer's serial port and want to use it as a joystick. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called twidjoy.o. If you want to compile it as a + module, say M here and read . + CONFIG_JOYSTICK_DB9 Say Y here if you have a Sega Master System gamepad, Sega Genesis gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, diff --git a/drivers/input/joystick/Config.in b/drivers/input/joystick/Config.in index afd07b89dff3..25a293ddee3e 100644 --- a/drivers/input/joystick/Config.in +++ b/drivers/input/joystick/Config.in @@ -10,6 +10,7 @@ dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_JOYSTICK_ADI dep_tristate ' Creative Labs Blaster Cobra gamepad' CONFIG_JOYSTICK_COBRA $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' Genius Flight2000 Digital joysticks and gamepads' CONFIG_JOYSTICK_GF2K $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOYSTICK_GRIP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT +dep_tristate ' Guillemot joysticks and gamepads' CONFIG_JOYSTICK_GUILLEMOT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' InterAct digital joysticks and gamepads' CONFIG_JOYSTICK_INTERACT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' Microsoft SideWinder digital joysticks and gamepads' CONFIG_JOYSTICK_SIDEWINDER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOYSTICK_TMDC $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT @@ -21,6 +22,7 @@ dep_tristate ' LogiCad3d Magellan/SpaceMouse 6dof controllers' CONFIG_JOYSTICK_ dep_tristate ' SpaceTec SpaceOrb/Avenger 6dof controllers' CONFIG_JOYSTICK_SPACEORB $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO dep_tristate ' SpaceTec SpaceBall 6dof controllers' CONFIG_JOYSTICK_SPACEBALL $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO dep_tristate ' Gravis Stinger gamepad' CONFIG_JOYSTICK_STINGER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO +dep_tristate ' Twiddler as as joystick' CONFIG_JOYSTICK_TWIDDLER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO dep_tristate ' Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_JOYSTICK_DB9 $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_JOYSTICK_GAMECON $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index c70244633703..b06e9d878828 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -2,24 +2,6 @@ # Makefile for the input core drivers. # -# I-Force may need both USB and RS-232 - -CONFIG_JOYSTICK_IFORCE := n - -ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y) - ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y) - CONFIG_JOYSTICK_IFORCE := y - endif -endif - -ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m) - CONFIG_JOYSTICK_IFORCE := m -endif - -ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m) - CONFIG_JOYSTICK_IFORCE := m -endif - # Each configuration option enables a list of files. obj-$(CONFIG_JOYSTICK_A3D) += a3d.o @@ -31,8 +13,9 @@ obj-$(CONFIG_JOYSTICK_DB9) += db9.o obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o obj-$(CONFIG_JOYSTICK_GRIP) += grip.o -obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o +obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o +obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o @@ -40,8 +23,33 @@ obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o +obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o +# I-Force may need both USB and RS-232 + +CONFIG_JOYSTICK_IFORCE := n + +ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y) + ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y) + CONFIG_JOYSTICK_IFORCE := y + endif +endif + +ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m) + CONFIG_JOYSTICK_IFORCE := m +endif + +ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m) + CONFIG_JOYSTICK_IFORCE := m +endif + +subdir-$(CONFIG_JOYSTICK_IFORCE) += iforce + +ifeq ($(CONFIG_JOYSTICK_IFORCE),y) + obj-y += iforce/iforce-drv.o +endif + # The global Rules.make. include $(TOPDIR)/Rules.make diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c new file mode 100644 index 000000000000..53c46728a2bf --- /dev/null +++ b/drivers/input/joystick/guillemot.c @@ -0,0 +1,285 @@ +/* + * $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $ + * + * Copyright (c) 2001 Vojtech Pavlik + */ + +/* + * Guillemot Digital Interface Protocol driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Guillemot Digital joystick driver"); +MODULE_LICENSE("GPL"); + +#define GUILLEMOT_MAX_START 600 /* 600 us */ +#define GUILLEMOT_MAX_STROBE 60 /* 60 us */ +#define GUILLEMOT_MAX_LENGTH 17 /* 17 bytes */ +#define GUILLEMOT_REFRESH_TIME HZ/50 /* 20 ms */ + +static short guillemot_abs_pad[] = + { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, -1 }; + +static short guillemot_btn_pad[] = + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_MODE, BTN_SELECT, -1 }; + +static struct { + int x; + int y; +} guillemot_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct guillemot_type { + unsigned char id; + short *abs; + short *btn; + int hat; + char *name; +}; + +struct guillemot { + struct gameport *gameport; + struct input_dev dev; + struct timer_list timer; + int used; + int bads; + int reads; + struct guillemot_type *type; + unsigned char length; + char phys[32]; +}; + +static struct guillemot_type guillemot_type[] = { + { 0x00, guillemot_abs_pad, guillemot_btn_pad, 1, "Guillemot Pad" }, + { 0 }}; + +/* + * guillemot_read_packet() reads Guillemot joystick data. + */ + +static int guillemot_read_packet(struct gameport *gameport, u8 *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + for (i = 0; i < GUILLEMOT_MAX_LENGTH; i++) + data[i] = 0; + + i = 0; + t = gameport_time(gameport, GUILLEMOT_MAX_START); + s = gameport_time(gameport, GUILLEMOT_MAX_STROBE); + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < GUILLEMOT_MAX_LENGTH * 8) { + t--; + u = v; v = gameport_read(gameport); + if (v & ~u & 0x10) { + data[i >> 3] |= ((v >> 5) & 1) << (i & 7); + i++; + t = s; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * guillemot_timer() reads and analyzes Guillemot joystick data. + */ + +static void guillemot_timer(unsigned long private) +{ + struct guillemot *guillemot = (struct guillemot *) private; + struct input_dev *dev = &guillemot->dev; + u8 data[GUILLEMOT_MAX_LENGTH]; + int i; + + guillemot->reads++; + + if (guillemot_read_packet(guillemot->gameport, data) != GUILLEMOT_MAX_LENGTH * 8 || + data[0] != 0x55 || data[16] != 0xaa) { + guillemot->bads++; + } else { + + for (i = 0; i < 6 && guillemot->type->abs[i] >= 0; i++) + input_report_abs(dev, guillemot->type->abs[i], data[i + 5]); + + if (guillemot->type->hat) { + input_report_abs(dev, ABS_HAT0X, guillemot_hat_to_axis[data[4] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, guillemot_hat_to_axis[data[4] >> 4].y); + } + + for (i = 0; i < 16 && guillemot->type->btn[i] >= 0; i++) + input_report_key(dev, guillemot->type->btn[i], (data[2 + (i >> 3)] >> (i & 7)) & 1); + } + + mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME); +} + +/* + * guillemot_open() is a callback from the input open routine. + */ + +static int guillemot_open(struct input_dev *dev) +{ + struct guillemot *guillemot = dev->private; + if (!guillemot->used++) + mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME); + return 0; +} + +/* + * guillemot_close() is a callback from the input close routine. + */ + +static void guillemot_close(struct input_dev *dev) +{ + struct guillemot *guillemot = dev->private; + if (!--guillemot->used) + del_timer(&guillemot->timer); +} + +/* + * guillemot_connect() probes for Guillemot joysticks. + */ + +static void guillemot_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct guillemot *guillemot; + u8 data[GUILLEMOT_MAX_LENGTH]; + int i, t; + + if (!(guillemot = kmalloc(sizeof(struct guillemot), GFP_KERNEL))) + return; + memset(guillemot, 0, sizeof(struct guillemot)); + + gameport->private = guillemot; + + guillemot->gameport = gameport; + init_timer(&guillemot->timer); + guillemot->timer.data = (long) guillemot; + guillemot->timer.function = guillemot_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = guillemot_read_packet(gameport, data); + + if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa) + goto fail2; + + for (i = 0; guillemot_type[i].name; i++) + if (guillemot_type[i].id == data[11]) + break; + + if (!guillemot_type[i].name) { + printk(KERN_WARNING "guillemot.c: Unknown joystick on %s. [ %02x%02x:%04x, ver %d.%02d ]\n", + gameport->phys, data[12], data[13], data[11], data[14], data[15]); + goto fail2; + } + + sprintf(guillemot->phys, "%s/input0", gameport->phys); + + guillemot->type = guillemot_type + i; + + guillemot->dev.private = guillemot; + guillemot->dev.open = guillemot_open; + guillemot->dev.close = guillemot_close; + + guillemot->dev.name = guillemot_type[i].name; + guillemot->dev.phys = guillemot->phys; + guillemot->dev.idbus = BUS_GAMEPORT; + guillemot->dev.idvendor = GAMEPORT_ID_VENDOR_GUILLEMOT; + guillemot->dev.idproduct = guillemot_type[i].id; + guillemot->dev.idversion = (int)data[14] << 8 | data[15]; + + guillemot->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++) { + set_bit(t, guillemot->dev.absbit); + guillemot->dev.absmin[t] = 0; + guillemot->dev.absmax[t] = 255; + } + + if (guillemot->type->hat) + for (i = 0; i < 2; i++) { + t = ABS_HAT0X + i; + set_bit(t, guillemot->dev.absbit); + guillemot->dev.absmin[t] = -1; + guillemot->dev.absmax[t] = 1; + } + + for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++) + set_bit(t, guillemot->dev.keybit); + + input_register_device(&guillemot->dev); + printk(KERN_INFO "input: %s ver %d.%02d on %s\n", + guillemot->type->name, data[14], data[15], gameport->phys); + + return; +fail2: gameport_close(gameport); +fail1: kfree(guillemot); +} + +static void guillemot_disconnect(struct gameport *gameport) +{ + struct guillemot *guillemot = gameport->private; + printk(KERN_INFO "guillemot.c: Failed %d reads out of %d on %s\n", guillemot->reads, guillemot->bads, guillemot->phys); + input_unregister_device(&guillemot->dev); + gameport_close(gameport); + kfree(guillemot); +} + +static struct gameport_dev guillemot_dev = { + connect: guillemot_connect, + disconnect: guillemot_disconnect, +}; + +int __init guillemot_init(void) +{ + gameport_register_device(&guillemot_dev); + return 0; +} + +void __exit guillemot_exit(void) +{ + gameport_unregister_device(&guillemot_dev); +} + +module_init(guillemot_init); +module_exit(guillemot_exit); diff --git a/drivers/input/joystick/iforce.c b/drivers/input/joystick/iforce.c deleted file mode 100644 index 172c5d99a954..000000000000 --- a/drivers/input/joystick/iforce.c +++ /dev/null @@ -1,1224 +0,0 @@ -/* - * $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $ - * - * Copyright (c) 2000-2001 Vojtech Pavlik - * Copyright (c) 2001 Johann Deneux - * - * USB/RS232 I-Force joysticks and wheels. - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* FF: This module provides arbitrary resource management routines. - * I use it to manage the device's memory. - * Despite the name of this module, I am *not* going to access the ioports. - */ -#include - -MODULE_AUTHOR("Vojtech Pavlik , Johann Deneux "); -MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); -MODULE_LICENSE("GPL"); - -#define IFORCE_MAX_LENGTH 16 - -#if defined(CONFIG_JOYSTICK_IFORCE_232) || defined(CONFIG_JOYSTICK_IFORCE_232_MODULE) -#define IFORCE_232 1 -#endif -#if defined(CONFIG_JOYSTICK_IFORCE_USB) || defined(CONFIG_JOYSTICK_IFORCE_USB_MODULE) -#define IFORCE_USB 2 -#endif - -#define FF_EFFECTS_MAX 32 - -/* Each force feedback effect is made of one core effect, which can be - * associated to at most to effect modifiers - */ -#define FF_MOD1_IS_USED 0 -#define FF_MOD2_IS_USED 1 -#define FF_CORE_IS_USED 2 -#define FF_CORE_IS_PLAYED 3 -#define FF_MODCORE_MAX 3 - -struct iforce_core_effect { - /* Information about where modifiers are stored in the device's memory */ - struct resource mod1_chunk; - struct resource mod2_chunk; - unsigned long flags[NBITS(FF_MODCORE_MAX)]; -}; - -#define FF_CMD_EFFECT 0x010e -#define FF_CMD_SHAPE 0x0208 -#define FF_CMD_MAGNITUDE 0x0303 -#define FF_CMD_PERIOD 0x0407 -#define FF_CMD_INTERACT 0x050a - -#define FF_CMD_AUTOCENTER 0x4002 -#define FF_CMD_PLAY 0x4103 -#define FF_CMD_ENABLE 0x4201 -#define FF_CMD_GAIN 0x4301 - -#define FF_CMD_QUERY 0xff01 - -static signed short btn_joystick[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, - BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, BTN_DEAD, -1 }; -static signed short btn_wheel[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, - BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; -static signed short abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; -static signed short abs_wheel[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 }; -static signed short ff_iforce[] = { FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_FRICTION, - FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, FF_AUTOCENTER, -1 }; - -static struct iforce_device { - u16 idvendor; - u16 idproduct; - char *name; - signed short *btn; - signed short *abs; - signed short *ff; -} iforce_device[] = { - { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce }, - { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce }, - { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_joystick, abs_joystick, ff_iforce }, - { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce }, - { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, - { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } -}; - -struct iforce { - struct input_dev dev; /* Input device interface */ - struct iforce_device *type; - char name[64]; - int open; - int bus; - - unsigned char data[IFORCE_MAX_LENGTH]; - unsigned char edata[IFORCE_MAX_LENGTH]; - u16 ecmd; - u16 expect_packet; - -#ifdef IFORCE_232 - struct serio *serio; /* RS232 transfer */ - int idx, pkt, len, id; - unsigned char csum; -#endif -#ifdef IFORCE_USB - struct usb_device *usbdev; /* USB transfer */ - struct urb *irq, *out, *ctrl; - struct usb_ctrlrequest dr; -#endif - /* Force Feedback */ - wait_queue_head_t wait; - struct resource device_memory; - struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; -}; - -static struct { - __s32 x; - __s32 y; -} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -/* Get hi and low bytes of a 16-bits int */ -#define HI(a) ((unsigned char)((a) >> 8)) -#define LO(a) ((unsigned char)((a) & 0xff)) - -/* Encode a time value */ -#define TIME_SCALE(a) ((a) == 0xffff ? 0xffff : (a) * 1000 / 256) - -static void dump_packet(char *msg, u16 cmd, unsigned char *data) -{ - int i; - - printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd); - for (i = 0; i < LO(cmd); i++) - printk("%02x ", data[i]); - printk(")\n"); -} - -/* - * Send a packet of bytes to the device - */ -static void send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) -{ - switch (iforce->bus) { - -#ifdef IFORCE_232 - case IFORCE_232: { - - int i; - unsigned char csum = 0x2b ^ HI(cmd) ^ LO(cmd); - - serio_write(iforce->serio, 0x2b); - serio_write(iforce->serio, HI(cmd)); - serio_write(iforce->serio, LO(cmd)); - - for (i = 0; i < LO(cmd); i++) { - serio_write(iforce->serio, data[i]); - csum = csum ^ data[i]; - } - - serio_write(iforce->serio, csum); - return; - } -#endif -#ifdef IFORCE_USB - case IFORCE_USB: { - - DECLARE_WAITQUEUE(wait, current); - int timeout = HZ; /* 1 second */ - - memcpy(iforce->out->transfer_buffer + 1, data, LO(cmd)); - ((char*)iforce->out->transfer_buffer)[0] = HI(cmd); - iforce->out->transfer_buffer_length = LO(cmd) + 2; - iforce->out->dev = iforce->usbdev; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&iforce->wait, &wait); - - if (usb_submit_urb(iforce->out, GFP_ATOMIC)) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - return; - } - - while (timeout && iforce->out->status == -EINPROGRESS) - timeout = schedule_timeout(timeout); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - - if (!timeout) - usb_unlink_urb(iforce->out); - - return; - } -#endif - } -} - -static void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) -{ - struct input_dev *dev = &iforce->dev; - int i; - -#ifdef IFORCE_232 - if (HI(iforce->expect_packet) == HI(cmd)) { - iforce->expect_packet = 0; - iforce->ecmd = cmd; - memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); - } -#endif - - if (!iforce->type) - return; - - switch (HI(cmd)) { - - case 0x01: /* joystick position data */ - case 0x03: /* wheel position data */ - - if (HI(cmd) == 1) { - input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); - input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); - input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); - } else { - input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); - input_report_abs(dev, ABS_GAS, 255 - data[2]); - input_report_abs(dev, ABS_BRAKE, 255 - data[3]); - } - - input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); - input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); - - for (i = 0; iforce->type->btn[i] >= 0; i++) - input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); - - break; - - case 0x02: /* status report */ - - input_report_key(dev, BTN_DEAD, data[0] & 0x02); - break; - } -} - -static int get_id_packet(struct iforce *iforce, char *packet) -{ - DECLARE_WAITQUEUE(wait, current); - int timeout = HZ; /* 1 second */ - - switch (iforce->bus) { - -#ifdef IFORCE_USB - case IFORCE_USB: - - iforce->dr.bRequest = packet[0]; - iforce->ctrl->dev = iforce->usbdev; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&iforce->wait, &wait); - - if (usb_submit_urb(iforce->ctrl, GFP_ATOMIC)) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - return -1; - } - - while (timeout && iforce->ctrl->status == -EINPROGRESS) - timeout = schedule_timeout(timeout); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - - if (!timeout) { - usb_unlink_urb(iforce->ctrl); - return -1; - } - - break; -#endif -#ifdef IFORCE_232 - case IFORCE_232: - - iforce->expect_packet = FF_CMD_QUERY; - send_packet(iforce, FF_CMD_QUERY, packet); - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&iforce->wait, &wait); - - while (timeout && iforce->expect_packet) - timeout = schedule_timeout(timeout); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&iforce->wait, &wait); - - if (!timeout) { - iforce->expect_packet = 0; - return -1; - } - - break; -#endif - } - - return -(iforce->edata[0] != packet[0]); -} - -static int iforce_open(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - - switch (iforce->bus) { -#ifdef IFORCE_USB - case IFORCE_USB: - if (iforce->open++) - break; - iforce->irq->dev = iforce->usbdev; - if (usb_submit_urb(iforce->irq, GFP_KERNEL)) - return -EIO; - break; -#endif - } - return 0; -} - -static void iforce_close(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - - switch (iforce->bus) { -#ifdef IFORCE_USB - case IFORCE_USB: - if (!--iforce->open) - usb_unlink_urb(iforce->irq); - break; -#endif - } -} - -/* - * Start or stop playing an effect - */ - -static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct iforce* iforce = (struct iforce*)(dev->private); - unsigned char data[3]; - - printk(KERN_DEBUG "iforce.c: input_event(type = %d, code = %d, value = %d)\n", type, code, value); - - if (type != EV_FF) - return -1; - - switch (code) { - - case FF_GAIN: - - data[0] = value >> 9; - send_packet(iforce, FF_CMD_GAIN, data); - - return 0; - - case FF_AUTOCENTER: - - data[0] = 0x03; - data[1] = value >> 9; - send_packet(iforce, FF_CMD_AUTOCENTER, data); - - data[0] = 0x04; - data[1] = 0x01; - send_packet(iforce, FF_CMD_AUTOCENTER, data); - - return 0; - - default: /* Play an effect */ - - if (code >= iforce->dev.ff_effects_max) - return -1; - - data[0] = LO(code); - data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0; - data[2] = LO(value); - send_packet(iforce, FF_CMD_PLAY, data); - - return 0; - } - - return -1; -} - -/* - * Set the magnitude of a constant force effect - * Return error code - * - * Note: caller must ensure exclusive access to device - */ - -static int make_magnitude_modifier(struct iforce* iforce, - struct resource* mod_chunk, __s16 level) -{ - unsigned char data[3]; - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - data[2] = HI(level); - - send_packet(iforce, FF_CMD_MAGNITUDE, data); - - return 0; -} - -/* - * Upload the component of an effect dealing with the period, phase and magnitude - */ - -static int make_period_modifier(struct iforce* iforce, struct resource* mod_chunk, - __s16 magnitude, __s16 offset, u16 period, u16 phase) -{ - unsigned char data[7]; - - period = TIME_SCALE(period); - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - - data[2] = HI(magnitude); - data[3] = HI(offset); - data[4] = HI(phase); - - data[5] = LO(period); - data[6] = HI(period); - - send_packet(iforce, FF_CMD_PERIOD, data); - - return 0; -} - -/* - * Uploads the part of an effect setting the shape of the force - */ - -static int make_shape_modifier(struct iforce* iforce, struct resource* mod_chunk, - u16 attack_duration, __s16 initial_level, - u16 fade_duration, __s16 final_level) -{ - unsigned char data[8]; - - attack_duration = TIME_SCALE(attack_duration); - fade_duration = TIME_SCALE(fade_duration); - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - - data[2] = LO(attack_duration); - data[3] = HI(attack_duration); - data[4] = HI(initial_level); - - data[5] = LO(fade_duration); - data[6] = HI(fade_duration); - data[7] = HI(final_level); - - send_packet(iforce, FF_CMD_SHAPE, data); - - return 0; -} - -/* - * Component of spring, friction, inertia... effects - */ - -static int make_interactive_modifier(struct iforce* iforce, - struct resource* mod_chunk, - __s16 rsat, __s16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center) -{ - unsigned char data[10]; - - if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, - iforce->device_memory.start, iforce->device_memory.end, 2L, - NULL, NULL)) { - return -ENOMEM; - } - - data[0] = LO(mod_chunk->start); - data[1] = HI(mod_chunk->start); - - data[2] = HI(rk); - data[3] = HI(lk); - - data[4] = LO(center); - data[5] = HI(center); - - data[6] = LO(db); - data[7] = HI(db); - - data[8] = HI(rsat); - data[9] = HI(lsat); - - send_packet(iforce, FF_CMD_INTERACT, data); - - return 0; -} - -static unsigned char find_button(struct iforce *iforce, signed short button) -{ - int i; - for (i = 1; iforce->type->btn[i] >= 0; i++) - if (iforce->type->btn[i] == button) - return i + 1; - return 0; -} - -/* - * Send the part common to all effects to the device - */ - -static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2, - u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button, - u16 interval, u16 direction) -{ - unsigned char data[14]; - - duration = TIME_SCALE(duration); - delay = TIME_SCALE(delay); - interval = TIME_SCALE(interval); - - data[0] = LO(id); - data[1] = effect_type; - data[2] = LO(axes) | find_button(iforce, button); - - data[3] = LO(duration); - data[4] = HI(duration); - - data[5] = HI(direction); - - data[6] = LO(interval); - data[7] = HI(interval); - - data[8] = LO(mod_id1); - data[9] = HI(mod_id1); - data[10] = LO(mod_id2); - data[11] = HI(mod_id2); - - data[12] = LO(delay); - data[13] = HI(delay); - - send_packet(iforce, FF_CMD_EFFECT, data); - - return 0; -} - -/* - * Upload a periodic effect to the device - */ - -static int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect) -{ - u8 wave_code; - int core_id = effect->id; - struct iforce_core_effect* core_effect = iforce->core_effects + core_id; - struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); - struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); - int err = 0; - - err = make_period_modifier(iforce, mod1_chunk, - effect->u.periodic.magnitude, effect->u.periodic.offset, - effect->u.periodic.period, effect->u.periodic.phase); - if (err) return err; - set_bit(FF_MOD1_IS_USED, core_effect->flags); - - err = make_shape_modifier(iforce, mod2_chunk, - effect->u.periodic.shape.attack_length, - effect->u.periodic.shape.attack_level, - effect->u.periodic.shape.fade_length, - effect->u.periodic.shape.fade_level); - if (err) return err; - set_bit(FF_MOD2_IS_USED, core_effect->flags); - - switch (effect->u.periodic.waveform) { - case FF_SQUARE: wave_code = 0x20; break; - case FF_TRIANGLE: wave_code = 0x21; break; - case FF_SINE: wave_code = 0x22; break; - case FF_SAW_UP: wave_code = 0x23; break; - case FF_SAW_DOWN: wave_code = 0x24; break; - default: wave_code = 0x20; break; - } - - err = make_core(iforce, effect->id, - mod1_chunk->start, - mod2_chunk->start, - wave_code, - 0x20, - effect->replay.length, - effect->replay.delay, - effect->trigger.button, - effect->trigger.interval, - effect->direction); - - return err; -} - -/* - * Upload a constant force effect - */ -static int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect) -{ - int core_id = effect->id; - struct iforce_core_effect* core_effect = iforce->core_effects + core_id; - struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); - struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); - int err = 0; - - printk(KERN_DEBUG "iforce.c: make constant effect\n"); - - err = make_magnitude_modifier(iforce, mod1_chunk, effect->u.constant.level); - if (err) return err; - set_bit(FF_MOD1_IS_USED, core_effect->flags); - - err = make_shape_modifier(iforce, mod2_chunk, - effect->u.constant.shape.attack_length, - effect->u.constant.shape.attack_level, - effect->u.constant.shape.fade_length, - effect->u.constant.shape.fade_level); - if (err) return err; - set_bit(FF_MOD2_IS_USED, core_effect->flags); - - err = make_core(iforce, effect->id, - mod1_chunk->start, - mod2_chunk->start, - 0x00, - 0x20, - effect->replay.length, - effect->replay.delay, - effect->trigger.button, - effect->trigger.interval, - effect->direction); - - return err; -} - -/* - * Upload an interactive effect. Those are for example friction, inertia, springs... - */ -static int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect) -{ - int core_id = effect->id; - struct iforce_core_effect* core_effect = iforce->core_effects + core_id; - struct resource* mod_chunk = &(core_effect->mod1_chunk); - u8 type, axes; - u16 mod1, mod2, direction; - int err = 0; - - printk(KERN_DEBUG "iforce.c: make interactive effect\n"); - - switch (effect->type) { - case FF_SPRING: type = 0x40; break; - case FF_FRICTION: type = 0x41; break; - default: return -1; - } - - err = make_interactive_modifier(iforce, mod_chunk, - effect->u.interactive.right_saturation, - effect->u.interactive.left_saturation, - effect->u.interactive.right_coeff, - effect->u.interactive.left_coeff, - effect->u.interactive.deadband, - effect->u.interactive.center); - if (err) return err; - set_bit(FF_MOD1_IS_USED, core_effect->flags); - - switch ((test_bit(ABS_X, &effect->u.interactive.axis) || - test_bit(ABS_WHEEL, &effect->u.interactive.axis)) | - (!!test_bit(ABS_Y, &effect->u.interactive.axis) << 1)) { - - case 0: /* Only one axis, choose orientation */ - mod1 = mod_chunk->start; - mod2 = 0xffff; - direction = effect->direction; - axes = 0x20; - break; - - case 1: /* Only X axis */ - mod1 = mod_chunk->start; - mod2 = 0xffff; - direction = 0x5a00; - axes = 0x40; - break; - - case 2: /* Only Y axis */ - mod1 = 0xffff; - mod2 = mod_chunk->start; - direction = 0xb400; - axes = 0x80; - break; - - case 3: /* Both X and Y axes */ - /* TODO: same setting for both axes is not mandatory */ - mod1 = mod_chunk->start; - mod2 = mod_chunk->start; - direction = 0x6000; - axes = 0xc0; - break; - - default: - return -1; - } - - err = make_core(iforce, effect->id, - mod1, mod2, - type, axes, - effect->replay.length, effect->replay.delay, - effect->trigger.button, effect->trigger.interval, - direction); - - return err; -} - -/* - * Function called when an ioctl is performed on the event dev entry. - * It uploads an effect to the device - */ -static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect) -{ - struct iforce* iforce = (struct iforce*)(dev->private); - int id; - - printk(KERN_DEBUG "iforce.c: upload effect\n"); - -/* - * Get a free id - */ - - for (id=0; id < FF_EFFECTS_MAX; ++id) - if (!test_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break; - - if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max) - return -ENOMEM; - - effect->id = id; - set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags); - -/* - * Upload the effect - */ - - switch (effect->type) { - - case FF_PERIODIC: - return iforce_upload_periodic(iforce, effect); - - case FF_CONSTANT: - return iforce_upload_constant(iforce, effect); - - case FF_SPRING: - case FF_FRICTION: - return iforce_upload_interactive(iforce, effect); - - default: - return -1; - } -} - -/* - * Erases an effect: it frees the effect id and mark as unused the memory - * allocated for the parameters - */ -static int iforce_erase_effect(struct input_dev *dev, int effect_id) -{ - struct iforce* iforce = (struct iforce*)(dev->private); - int err = 0; - struct iforce_core_effect* core_effect; - - printk(KERN_DEBUG "iforce.c: erase effect %d\n", effect_id); - - if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX) - return -EINVAL; - - core_effect = iforce->core_effects + effect_id; - - if (test_bit(FF_MOD1_IS_USED, core_effect->flags)) - err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk)); - - if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags)) - err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk)); - - /*TODO: remember to change that if more FF_MOD* bits are added */ - core_effect->flags[0] = 0; - - return err; -} -static int iforce_init_device(struct iforce *iforce) -{ - unsigned char c[] = "CEOV"; - int i; - - init_waitqueue_head(&iforce->wait); - iforce->dev.ff_effects_max = 10; - -/* - * Input device fields. - */ - - iforce->dev.idbus = BUS_USB; - iforce->dev.private = iforce; - iforce->dev.name = iforce->name; - iforce->dev.open = iforce_open; - iforce->dev.close = iforce_close; - iforce->dev.event = iforce_input_event; - iforce->dev.upload_effect = iforce_upload_effect; - iforce->dev.erase_effect = iforce_erase_effect; - -/* - * On-device memory allocation. - */ - - iforce->device_memory.name = "I-Force device effect memory"; - iforce->device_memory.start = 0; - iforce->device_memory.end = 200; - iforce->device_memory.flags = IORESOURCE_MEM; - iforce->device_memory.parent = NULL; - iforce->device_memory.child = NULL; - iforce->device_memory.sibling = NULL; - -/* - * Wait until device ready - until it sends its first response. - */ - - for (i = 0; i < 20; i++) - if (!get_id_packet(iforce, "O")) - break; - - if (i == 20) { /* 5 seconds */ - printk(KERN_ERR "iforce.c: Timeout waiting for response from device.\n"); - iforce_close(&iforce->dev); - return -1; - } - -/* - * Get device info. - */ - - if (!get_id_packet(iforce, "M")) - iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1]; - if (!get_id_packet(iforce, "P")) - iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1]; - if (!get_id_packet(iforce, "B")) - iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; - if (!get_id_packet(iforce, "N")) - iforce->dev.ff_effects_max = iforce->edata[1]; - -/* - * Display additional info. - */ - - for (i = 0; c[i]; i++) - if (!get_id_packet(iforce, c + i)) - dump_packet("info", iforce->ecmd, iforce->edata); - -/* - * Disable spring, enable force feedback. - * FIXME: We should use iforce_set_autocenter() et al here. - */ - - send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000"); - send_packet(iforce, FF_CMD_ENABLE, "\004"); - -/* - * Find appropriate device entry - */ - - for (i = 0; iforce_device[i].idvendor; i++) - if (iforce_device[i].idvendor == iforce->dev.idvendor && - iforce_device[i].idproduct == iforce->dev.idproduct) - break; - - iforce->type = iforce_device + i; - - sprintf(iforce->name, iforce->type->name, - iforce->dev.idproduct, iforce->dev.idvendor); - -/* - * Set input device bitfields and ranges. - */ - - iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF); - - for (i = 0; iforce->type->btn[i] >= 0; i++) { - signed short t = iforce->type->btn[i]; - set_bit(t, iforce->dev.keybit); - if (t != BTN_DEAD) - set_bit(FF_BTN(t), iforce->dev.ffbit); - } - - for (i = 0; iforce->type->abs[i] >= 0; i++) { - - signed short t = iforce->type->abs[i]; - set_bit(t, iforce->dev.absbit); - - switch (t) { - - case ABS_X: - case ABS_Y: - case ABS_WHEEL: - - iforce->dev.absmax[t] = 1920; - iforce->dev.absmin[t] = -1920; - iforce->dev.absflat[t] = 128; - iforce->dev.absfuzz[t] = 16; - - set_bit(FF_ABS(t), iforce->dev.ffbit); - break; - - case ABS_THROTTLE: - case ABS_GAS: - case ABS_BRAKE: - - iforce->dev.absmax[t] = 255; - iforce->dev.absmin[t] = 0; - break; - - case ABS_HAT0X: - case ABS_HAT0Y: - iforce->dev.absmax[t] = 1; - iforce->dev.absmin[t] = -1; - break; - } - } - - for (i = 0; iforce->type->ff[i] >= 0; i++) - set_bit(iforce->type->ff[i], iforce->dev.ffbit); - -/* - * Register input device. - */ - - input_register_device(&iforce->dev); - - return 0; -} - -#ifdef IFORCE_USB - -static void iforce_usb_irq(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - iforce_process_packet(iforce, - (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1); -} - -static void iforce_usb_out(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); -} - -static void iforce_usb_ctrl(struct urb *urb) -{ - struct iforce *iforce = urb->context; - if (urb->status) return; - iforce->ecmd = 0xff00 | urb->actual_length; - if (waitqueue_active(&iforce->wait)) - wake_up(&iforce->wait); -} - -static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id) -{ - struct usb_endpoint_descriptor *epirq, *epout; - struct iforce *iforce; - - epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1; - - if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL; - memset(iforce, 0, sizeof(struct iforce)); - - iforce->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!iforce->irq) { - kfree(iforce); - return NULL; - } - iforce->out = usb_alloc_urb(0, GFP_KERNEL); - if (!iforce->out) { - usb_free_urb(iforce->irq); - kfree(iforce); - return NULL; - } - iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL); - if (!iforce->ctrl) { - usb_free_urb(iforce->out); - usb_free_urb(iforce->irq); - kfree(iforce); - return NULL; - } - - iforce->bus = IFORCE_USB; - iforce->usbdev = dev; - - iforce->dr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE; - iforce->dr.wIndex = 0; - iforce->dr.wLength = 16; - - FILL_INT_URB(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress), - iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval); - - FILL_BULK_URB(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress), - iforce + 1, 32, iforce_usb_out, iforce); - - FILL_CONTROL_URB(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0), - (void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce); - - if (iforce_init_device(iforce)) { - usb_free_urb(iforce->ctrl); - usb_free_urb(iforce->out); - usb_free_urb(iforce->irq); - kfree(iforce); - return NULL; - } - - printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on usb%d:%d.%d\n", - iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max, - iforce->device_memory.end, dev->bus->busnum, dev->devnum, ifnum); - - return iforce; -} - -static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) -{ - struct iforce *iforce = ptr; - usb_unlink_urb(iforce->irq); - input_unregister_device(&iforce->dev); - usb_free_urb(iforce->ctrl); - usb_free_urb(iforce->out); - usb_free_urb(iforce->irq); - kfree(iforce); -} - -static struct usb_device_id iforce_usb_ids [] = { - { USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */ - { USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */ - { USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */ - { USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */ - { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, iforce_usb_ids); - -static struct usb_driver iforce_usb_driver = { - name: "iforce", - probe: iforce_usb_probe, - disconnect: iforce_usb_disconnect, - id_table: iforce_usb_ids, -}; - -#endif - -#ifdef IFORCE_232 - -static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) -{ - struct iforce* iforce = serio->private; - - if (!iforce->pkt) { - if (data != 0x2b) { - return; - } - iforce->pkt = 1; - return; - } - - if (!iforce->id) { - if (data > 3 && data != 0xff) { - iforce->pkt = 0; - return; - } - iforce->id = data; - return; - } - - if (!iforce->len) { - if (data > IFORCE_MAX_LENGTH) { - iforce->pkt = 0; - iforce->id = 0; - return; - } - iforce->len = data; - return; - } - - if (iforce->idx < iforce->len) { - iforce->csum += iforce->data[iforce->idx++] = data; - return; - } - - if (iforce->idx == iforce->len) { - iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); - iforce->pkt = 0; - iforce->id = 0; - iforce->len = 0; - iforce->idx = 0; - iforce->csum = 0; - } -} - -static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) -{ - struct iforce *iforce; - if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) - return; - - if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return; - memset(iforce, 0, sizeof(struct iforce)); - - iforce->bus = IFORCE_232; - iforce->serio = serio; - serio->private = iforce; - - if (serio_open(serio, dev)) { - kfree(iforce); - return; - } - - if (iforce_init_device(iforce)) { - serio_close(serio); - kfree(iforce); - return; - } - - printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on serio%d\n", - iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max, - iforce->device_memory.end, serio->number); -} - -static void iforce_serio_disconnect(struct serio *serio) -{ - struct iforce* iforce = serio->private; - - input_unregister_device(&iforce->dev); - serio_close(serio); - kfree(iforce); -} - -static struct serio_dev iforce_serio_dev = { - interrupt: iforce_serio_irq, - connect: iforce_serio_connect, - disconnect: iforce_serio_disconnect, -}; - -#endif - -static int __init iforce_init(void) -{ -#ifdef IFORCE_USB - usb_register(&iforce_usb_driver); -#endif -#ifdef IFORCE_232 - serio_register_device(&iforce_serio_dev); -#endif - return 0; -} - -static void __exit iforce_exit(void) -{ -#ifdef IFORCE_USB - usb_deregister(&iforce_usb_driver); -#endif -#ifdef IFORCE_232 - serio_unregister_device(&iforce_serio_dev); -#endif -} - -module_init(iforce_init); -module_exit(iforce_exit); diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile new file mode 100644 index 000000000000..9960f933e524 --- /dev/null +++ b/drivers/input/joystick/iforce/Makefile @@ -0,0 +1,41 @@ +# +# Makefile for the I-Force driver +# + +O_TARGET := iforce-drv.o + +# I-Force may need both USB and RS-232 + +CONFIG_JOYSTICK_IFORCE := n + +ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y) + ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y) + CONFIG_JOYSTICK_IFORCE := y + endif +endif + +ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m) + CONFIG_JOYSTICK_IFORCE := m +endif + +ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m) + CONFIG_JOYSTICK_IFORCE := m +endif + +obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o + +include $(TOPDIR)/Rules.make + +IFORCEOBJS = iforce-ff.o iforce-main.o iforce-packets.o + +ifneq ($(CONFIG_JOYSTICK_IFORCE_232),) + IFORCEOBJS += iforce-serio.o +endif + +ifneq ($(CONFIG_JOYSTICK_IFORCE_USB),) + IFORCEOBJS += iforce-usb.o +endif + +iforce.o: $(IFORCEOBJS) + $(LD) -i $(IFORCEOBJS) -o $@ + diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c new file mode 100644 index 000000000000..267f92f0aa2f --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -0,0 +1,543 @@ +/* + * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $ + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +/* + * Set the magnitude of a constant force effect + * Return error code + * + * Note: caller must ensure exclusive access to device + */ + +static int make_magnitude_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, __s16 level) +{ + unsigned char data[3]; + + if (!no_alloc) { + down(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + up(&iforce->mem_mutex); + return -ENOMEM; + } + up(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + data[2] = HIFIX80(level); + + iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data); + + iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data); + return 0; +} + +/* + * Upload the component of an effect dealing with the period, phase and magnitude + */ + +static int make_period_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, + __s16 magnitude, __s16 offset, u16 period, u16 phase) +{ + unsigned char data[7]; + + period = TIME_SCALE(period); + + if (!no_alloc) { + down(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + up(&iforce->mem_mutex); + return -ENOMEM; + } + up(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + + data[2] = HIFIX80(magnitude); + data[3] = HIFIX80(offset); + data[4] = HI(phase); + + data[5] = LO(period); + data[6] = HI(period); + + iforce_send_packet(iforce, FF_CMD_PERIOD, data); + + return 0; +} + +/* + * Uploads the part of an effect setting the envelope of the force + */ + +static int make_envelope_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, + u16 attack_duration, __s16 initial_level, + u16 fade_duration, __s16 final_level) +{ + unsigned char data[8]; + + attack_duration = TIME_SCALE(attack_duration); + fade_duration = TIME_SCALE(fade_duration); + + if (!no_alloc) { + down(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + up(&iforce->mem_mutex); + return -ENOMEM; + } + up(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + + data[2] = LO(attack_duration); + data[3] = HI(attack_duration); + data[4] = HI(initial_level); + + data[5] = LO(fade_duration); + data[6] = HI(fade_duration); + data[7] = HI(final_level); + + iforce_send_packet(iforce, FF_CMD_ENVELOPE, data); + + return 0; +} + +/* + * Component of spring, friction, inertia... effects + */ + +static int make_condition_modifier(struct iforce* iforce, + struct resource* mod_chunk, int no_alloc, + __u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center) +{ + unsigned char data[10]; + + if (!no_alloc) { + down(&iforce->mem_mutex); + if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, + iforce->device_memory.start, iforce->device_memory.end, 2L, + NULL, NULL)) { + up(&iforce->mem_mutex); + return -ENOMEM; + } + up(&iforce->mem_mutex); + } + + data[0] = LO(mod_chunk->start); + data[1] = HI(mod_chunk->start); + + data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ + data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */ + + center = (500*center)>>15; + data[4] = LO(center); + data[5] = HI(center); + + db = (1000*db)>>16; + data[6] = LO(db); + data[7] = HI(db); + + data[8] = (100*rsat)>>16; + data[9] = (100*lsat)>>16; + + iforce_send_packet(iforce, FF_CMD_CONDITION, data); + iforce_dump_packet("condition", FF_CMD_CONDITION, data); + + return 0; +} + +static unsigned char find_button(struct iforce *iforce, signed short button) +{ + int i; + for (i = 1; iforce->type->btn[i] >= 0; i++) + if (iforce->type->btn[i] == button) + return i + 1; + return 0; +} + +/* + * Analyse the changes in an effect, and tell if we need to send an condition + * parameter packet + */ +static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new) +{ + int id = new->id; + struct ff_effect* old = &iforce->core_effects[id].effect; + int ret=0; + int i; + + if (new->type != FF_SPRING && new->type != FF_FRICTION) { + printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n"); + return FALSE; + } + + for(i=0; i<2; i++) { + ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation + || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation + || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff + || old->u.condition[i].left_coeff != new->u.condition[i].left_coeff + || old->u.condition[i].deadband != new->u.condition[i].deadband + || old->u.condition[i].center != new->u.condition[i].center; + } + return ret; +} + +/* + * Analyse the changes in an effect, and tell if we need to send a magnitude + * parameter packet + */ +static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect) +{ + int id = effect->id; + struct ff_effect* old = &iforce->core_effects[id].effect; + + if (effect->type != FF_CONSTANT) { + printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); + return FALSE; + } + + return (old->u.constant.level != effect->u.constant.level); +} + +/* + * Analyse the changes in an effect, and tell if we need to send an envelope + * parameter packet + */ +static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect) +{ + int id = effect->id; + struct ff_effect* old = &iforce->core_effects[id].effect; + + switch (effect->type) { + case FF_CONSTANT: + if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length + || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level + || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length + || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level) + return TRUE; + break; + + case FF_PERIODIC: + if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length + || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level + || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length + || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level) + return TRUE; + break; + + default: + printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); + } + + return FALSE; +} + +/* + * Analyse the changes in an effect, and tell if we need to send a periodic + * parameter effect + */ +static int need_period_modifier(struct iforce* iforce, struct ff_effect* new) +{ + int id = new->id; + struct ff_effect* old = &iforce->core_effects[id].effect; + + if (new->type != FF_PERIODIC) { + printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n"); + return FALSE; + } + + return (old->u.periodic.period != new->u.periodic.period + || old->u.periodic.magnitude != new->u.periodic.magnitude + || old->u.periodic.offset != new->u.periodic.offset + || old->u.periodic.phase != new->u.periodic.phase); +} + +/* + * Analyse the changes in an effect, and tell if we need to send an effect + * packet + */ +static int need_core(struct iforce* iforce, struct ff_effect* new) +{ + int id = new->id; + struct ff_effect* old = &iforce->core_effects[id].effect; + + if (old->direction != new->direction + || old->trigger.button != new->trigger.button + || old->trigger.interval != new->trigger.interval + || old->replay.length != new->replay.length + || old->replay.delay != new->replay.delay) + return TRUE; + + return FALSE; +} +/* + * Send the part common to all effects to the device + */ +static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2, + u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button, + u16 interval, u16 direction) +{ + unsigned char data[14]; + + duration = TIME_SCALE(duration); + delay = TIME_SCALE(delay); + interval = TIME_SCALE(interval); + + data[0] = LO(id); + data[1] = effect_type; + data[2] = LO(axes) | find_button(iforce, button); + + data[3] = LO(duration); + data[4] = HI(duration); + + data[5] = HI(direction); + + data[6] = LO(interval); + data[7] = HI(interval); + + data[8] = LO(mod_id1); + data[9] = HI(mod_id1); + data[10] = LO(mod_id2); + data[11] = HI(mod_id2); + + data[12] = LO(delay); + data[13] = HI(delay); + + /* Stop effect */ +/* iforce_control_playback(iforce, id, 0);*/ + + iforce_send_packet(iforce, FF_CMD_EFFECT, data); + + /* If needed, restart effect */ + if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) { + /* BUG: perhaps we should replay n times, instead of 1. But we do not know n */ + iforce_control_playback(iforce, id, 1); + } + + return 0; +} + +/* + * Upload a periodic effect to the device + * See also iforce_upload_constant. + */ +int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update) +{ + u8 wave_code; + int core_id = effect->id; + struct iforce_core_effect* core_effect = iforce->core_effects + core_id; + struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); + struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); + int param1_err = 1; + int param2_err = 1; + int core_err = 0; + + if (!is_update || need_period_modifier(iforce, effect)) { + param1_err = make_period_modifier(iforce, mod1_chunk, + is_update, + effect->u.periodic.magnitude, effect->u.periodic.offset, + effect->u.periodic.period, effect->u.periodic.phase); + if (param1_err) return param1_err; + set_bit(FF_MOD1_IS_USED, core_effect->flags); + } + + if (!is_update || need_envelope_modifier(iforce, effect)) { + param2_err = make_envelope_modifier(iforce, mod2_chunk, + is_update, + effect->u.periodic.envelope.attack_length, + effect->u.periodic.envelope.attack_level, + effect->u.periodic.envelope.fade_length, + effect->u.periodic.envelope.fade_level); + if (param2_err) return param2_err; + set_bit(FF_MOD2_IS_USED, core_effect->flags); + } + + switch (effect->u.periodic.waveform) { + case FF_SQUARE: wave_code = 0x20; break; + case FF_TRIANGLE: wave_code = 0x21; break; + case FF_SINE: wave_code = 0x22; break; + case FF_SAW_UP: wave_code = 0x23; break; + case FF_SAW_DOWN: wave_code = 0x24; break; + default: wave_code = 0x20; break; + } + + if (!is_update || need_core(iforce, effect)) { + core_err = make_core(iforce, effect->id, + mod1_chunk->start, + mod2_chunk->start, + wave_code, + 0x20, + effect->replay.length, + effect->replay.delay, + effect->trigger.button, + effect->trigger.interval, + effect->direction); + } + + /* If one of the parameter creation failed, we already returned an + * error code. + * If the core creation failed, we return its error code. + * Else: if one parameter at least was created, we return 0 + * else we return 1; + */ + return core_err < 0 ? core_err : (param1_err && param2_err); +} + +/* + * Upload a constant force effect + * Return value: + * <0 Error code + * 0 Ok, effect created or updated + * 1 effect did not change since last upload, and no packet was therefore sent + */ +int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update) +{ + int core_id = effect->id; + struct iforce_core_effect* core_effect = iforce->core_effects + core_id; + struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk); + struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk); + int param1_err = 1; + int param2_err = 1; + int core_err = 0; + + if (!is_update || need_magnitude_modifier(iforce, effect)) { + param1_err = make_magnitude_modifier(iforce, mod1_chunk, + is_update, + effect->u.constant.level); + if (param1_err) return param1_err; + set_bit(FF_MOD1_IS_USED, core_effect->flags); + } + + if (!is_update || need_envelope_modifier(iforce, effect)) { + param2_err = make_envelope_modifier(iforce, mod2_chunk, + is_update, + effect->u.constant.envelope.attack_length, + effect->u.constant.envelope.attack_level, + effect->u.constant.envelope.fade_length, + effect->u.constant.envelope.fade_level); + if (param2_err) return param2_err; + set_bit(FF_MOD2_IS_USED, core_effect->flags); + } + + if (!is_update || need_core(iforce, effect)) { + core_err = make_core(iforce, effect->id, + mod1_chunk->start, + mod2_chunk->start, + 0x00, + 0x20, + effect->replay.length, + effect->replay.delay, + effect->trigger.button, + effect->trigger.interval, + effect->direction); + } + + /* If one of the parameter creation failed, we already returned an + * error code. + * If the core creation failed, we return its error code. + * Else: if one parameter at least was created, we return 0 + * else we return 1; + */ + return core_err < 0 ? core_err : (param1_err && param2_err); +} + +/* + * Upload an condition effect. Those are for example friction, inertia, springs... + */ +int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update) +{ + int core_id = effect->id; + struct iforce_core_effect* core_effect = iforce->core_effects + core_id; + struct resource* mod1_chunk = &(core_effect->mod1_chunk); + struct resource* mod2_chunk = &(core_effect->mod2_chunk); + u8 type; + int param_err = 1; + int core_err = 0; + + switch (effect->type) { + case FF_SPRING: type = 0x40; break; + case FF_DAMPER: type = 0x41; break; + default: return -1; + } + + if (!is_update || need_condition_modifier(iforce, effect)) { + param_err = make_condition_modifier(iforce, mod1_chunk, + is_update, + effect->u.condition[0].right_saturation, + effect->u.condition[0].left_saturation, + effect->u.condition[0].right_coeff, + effect->u.condition[0].left_coeff, + effect->u.condition[0].deadband, + effect->u.condition[0].center); + if (param_err) return param_err; + set_bit(FF_MOD1_IS_USED, core_effect->flags); + + param_err = make_condition_modifier(iforce, mod2_chunk, + is_update, + effect->u.condition[1].right_saturation, + effect->u.condition[1].left_saturation, + effect->u.condition[1].right_coeff, + effect->u.condition[1].left_coeff, + effect->u.condition[1].deadband, + effect->u.condition[1].center); + if (param_err) return param_err; + set_bit(FF_MOD2_IS_USED, core_effect->flags); + + } + + if (!is_update || need_core(iforce, effect)) { + core_err = make_core(iforce, effect->id, + mod1_chunk->start, mod2_chunk->start, + type, 0xc0, + effect->replay.length, effect->replay.delay, + effect->trigger.button, effect->trigger.interval, + effect->direction); + } + + /* If the parameter creation failed, we already returned an + * error code. + * If the core creation failed, we return its error code. + * Else: if a parameter was created, we return 0 + * else we return 1; + */ + return core_err < 0 ? core_err : param_err; +} diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c new file mode 100644 index 000000000000..45b7c001e066 --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -0,0 +1,543 @@ +/* + * $Id: iforce-main.c,v 1.18 2002/06/09 11:03:03 jdeneux Exp $ + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +MODULE_AUTHOR("Vojtech Pavlik , Johann Deneux "); +MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); +MODULE_LICENSE("GPL"); + +static signed short btn_joystick[] = +{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; + +static signed short btn_avb_pegasus[] = +{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; + +static signed short btn_wheel[] = +{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 }; + +static signed short btn_avb_tw[] = +{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, + BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 }; + +static signed short btn_avb_wheel[] = +{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3, + BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 }; + +static signed short abs_joystick[] = +{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static signed short abs_avb_pegasus[] = +{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y, + ABS_HAT1X, ABS_HAT1Y, -1 }; + +static signed short abs_wheel[] = +{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static signed short ff_iforce[] = +{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER, + FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, + FF_AUTOCENTER, -1 }; + +static struct iforce_device iforce_device[] = { + { 0x044f, 0xa01c, "Thrustmaster Motor Sport GT", btn_wheel, abs_wheel, ff_iforce }, + { 0x046d, 0xc281, "Logitech WingMan Force", btn_joystick, abs_joystick, ff_iforce }, + { 0x046d, 0xc291, "Logitech WingMan Formula Force", btn_wheel, abs_wheel, ff_iforce }, + { 0x05ef, 0x020a, "AVB Top Shot Pegasus", btn_avb_pegasus, abs_avb_pegasus, ff_iforce }, + { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_avb_wheel, abs_wheel, ff_iforce }, + { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce }, //? + { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? + { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } +}; + + + +static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct iforce* iforce = (struct iforce*)(dev->private); + unsigned char data[3]; + + if (type != EV_FF) + return -1; + + switch (code) { + + case FF_GAIN: + + data[0] = value >> 9; + iforce_send_packet(iforce, FF_CMD_GAIN, data); + + return 0; + + case FF_AUTOCENTER: + + data[0] = 0x03; + data[1] = value >> 9; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + + data[0] = 0x04; + data[1] = 0x01; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + + return 0; + + default: /* Play or stop an effect */ + + if (!CHECK_OWNERSHIP(code, iforce)) { + return -1; + } + if (value > 0) { + set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); + } + else { + clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); + } + + iforce_control_playback(iforce, code, value); + return 0; + } + + return -1; +} + +/* + * Function called when an ioctl is performed on the event dev entry. + * It uploads an effect to the device + */ +static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect) +{ + struct iforce* iforce = (struct iforce*)(dev->private); + int id; + int ret; + int is_update; + +/* Check this effect type is supported by this device */ + if (!test_bit(effect->type, iforce->dev.ffbit)) + return -EINVAL; + +/* + * If we want to create a new effect, get a free id + */ + if (effect->id == -1) { + + for (id=0; id < FF_EFFECTS_MAX; ++id) + if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break; + + if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max) + return -ENOMEM; + + effect->id = id; + iforce->core_effects[id].owner = current->pid; + iforce->core_effects[id].flags[0] = (1<id, iforce)) return -EACCES; + + /* Parameter type cannot be updated */ + if (effect->type != iforce->core_effects[effect->id].effect.type) + return -EINVAL; + + /* Check the effect is not already being updated */ + if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) { + return -EAGAIN; + } + + is_update = TRUE; + } + +/* + * Upload the effect + */ + switch (effect->type) { + + case FF_PERIODIC: + ret = iforce_upload_periodic(iforce, effect, is_update); + break; + + case FF_CONSTANT: + ret = iforce_upload_constant(iforce, effect, is_update); + break; + + case FF_SPRING: + case FF_DAMPER: + ret = iforce_upload_condition(iforce, effect, is_update); + break; + + default: + return -EINVAL; + } + if (ret == 0) { + /* A packet was sent, forbid new updates until we are notified + * that the packet was updated + */ + set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags); + } + iforce->core_effects[effect->id].effect = *effect; + return ret; +} + +/* + * Erases an effect: it frees the effect id and mark as unused the memory + * allocated for the parameters + */ +static int iforce_erase_effect(struct input_dev *dev, int effect_id) +{ + struct iforce* iforce = (struct iforce*)(dev->private); + int err = 0; + struct iforce_core_effect* core_effect; + + /* Check who is trying to erase this effect */ + if (iforce->core_effects[effect_id].owner != current->pid) { + printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner); + return -EACCES; + } + + if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX) + return -EINVAL; + + core_effect = iforce->core_effects + effect_id; + + if (test_bit(FF_MOD1_IS_USED, core_effect->flags)) + err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk)); + + if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags)) + err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk)); + + /*TODO: remember to change that if more FF_MOD* bits are added */ + core_effect->flags[0] = 0; + + return err; +} + +static int iforce_open(struct input_dev *dev) +{ + struct iforce *iforce = dev->private; + + switch (iforce->bus) { +#ifdef IFORCE_USB + case IFORCE_USB: + iforce->irq->dev = iforce->usbdev; + if (usb_submit_urb(iforce->irq, GFP_KERNEL)) + return -EIO; + break; +#endif + } + + /* Enable force feedback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); + + return 0; +} + +static int iforce_flush(struct input_dev *dev, struct file *file) +{ + struct iforce *iforce = dev->private; + int i; + + /* Erase all effects this process owns */ + for (i=0; iff_effects_max; ++i) { + + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && + current->pid == iforce->core_effects[i].owner) { + + /* Stop effect */ + input_report_ff(dev, i, 0); + + /* Free ressources assigned to effect */ + if (iforce_erase_effect(dev, i)) { + printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i); + } + } + + } + return 0; +} + +static void iforce_release(struct input_dev *dev) +{ + struct iforce *iforce = dev->private; + int i; + + /* Check: no effect should be present in memory */ + for (i=0; iff_effects_max; ++i) { + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) + break; + } + if (iff_effects_max) { + printk(KERN_WARNING "iforce_release: Device still owns effects\n"); + } + + /* Disable force feedback playback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); + + switch (iforce->bus) { +#ifdef IFORCE_USB + case IFORCE_USB: + usb_unlink_urb(iforce->irq); + + /* The device was unplugged before the file + * was released */ + if (iforce->usbdev == NULL) { + iforce_delete_device(iforce); + kfree(iforce); + } + break; +#endif + } +} + +void iforce_delete_device(struct iforce *iforce) +{ + switch (iforce->bus) { +#ifdef IFORCE_USB + case IFORCE_USB: + iforce_usb_delete(iforce); + break; +#endif +#ifdef IFORCE_232 + case IFORCE_232: + //TODO: Wait for the last packets to be sent + break; +#endif + } +} + +int iforce_init_device(struct iforce *iforce) +{ + unsigned char c[] = "CEOV"; + int i; + + init_waitqueue_head(&iforce->wait); + spin_lock_init(&iforce->xmit_lock); + init_MUTEX(&iforce->mem_mutex); + iforce->xmit.buf = iforce->xmit_data; + + iforce->dev.ff_effects_max = 10; + +/* + * Input device fields. + */ + + iforce->dev.idbus = BUS_USB; + iforce->dev.private = iforce; + iforce->dev.name = "Unknown I-Force device"; + iforce->dev.open = iforce_open; + iforce->dev.close = iforce_release; + iforce->dev.flush = iforce_flush; + iforce->dev.event = iforce_input_event; + iforce->dev.upload_effect = iforce_upload_effect; + iforce->dev.erase_effect = iforce_erase_effect; + +/* + * On-device memory allocation. + */ + + iforce->device_memory.name = "I-Force device effect memory"; + iforce->device_memory.start = 0; + iforce->device_memory.end = 200; + iforce->device_memory.flags = IORESOURCE_MEM; + iforce->device_memory.parent = NULL; + iforce->device_memory.child = NULL; + iforce->device_memory.sibling = NULL; + +/* + * Wait until device ready - until it sends its first response. + */ + + for (i = 0; i < 20; i++) + if (!iforce_get_id_packet(iforce, "O")) + break; + + if (i == 20) { /* 5 seconds */ + printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n"); + return -1; + } + +/* + * Get device info. + */ + + if (!iforce_get_id_packet(iforce, "M")) + iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1]; + else + printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n"); + + if (!iforce_get_id_packet(iforce, "P")) + iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1]; + else + printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n"); + + if (!iforce_get_id_packet(iforce, "B")) + iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; + else + printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n"); + + if (!iforce_get_id_packet(iforce, "N")) + iforce->dev.ff_effects_max = iforce->edata[1]; + else + printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n"); + + /* Check if the device can store more effects than the driver can really handle */ + if (iforce->dev.ff_effects_max > FF_EFFECTS_MAX) { + printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n", + iforce->dev.ff_effects_max, FF_EFFECTS_MAX); + iforce->dev.ff_effects_max = FF_EFFECTS_MAX; + } + +/* + * Display additional info. + */ + + for (i = 0; c[i]; i++) + if (!iforce_get_id_packet(iforce, c + i)) + iforce_dump_packet("info", iforce->ecmd, iforce->edata); + +/* + * Disable spring, enable force feedback. + * FIXME: We should use iforce_set_autocenter() et al here. + */ + + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000"); + +/* + * Find appropriate device entry + */ + + for (i = 0; iforce_device[i].idvendor; i++) + if (iforce_device[i].idvendor == iforce->dev.idvendor && + iforce_device[i].idproduct == iforce->dev.idproduct) + break; + + iforce->type = iforce_device + i; + iforce->dev.name = iforce->type->name; + +/* + * Set input device bitfields and ranges. + */ + + iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS); + + for (i = 0; iforce->type->btn[i] >= 0; i++) { + signed short t = iforce->type->btn[i]; + set_bit(t, iforce->dev.keybit); + } + set_bit(BTN_DEAD, iforce->dev.keybit); + + for (i = 0; iforce->type->abs[i] >= 0; i++) { + + signed short t = iforce->type->abs[i]; + set_bit(t, iforce->dev.absbit); + + switch (t) { + + case ABS_X: + case ABS_Y: + case ABS_WHEEL: + + iforce->dev.absmax[t] = 1920; + iforce->dev.absmin[t] = -1920; + iforce->dev.absflat[t] = 128; + iforce->dev.absfuzz[t] = 16; + + set_bit(t, iforce->dev.ffbit); + break; + + case ABS_THROTTLE: + case ABS_GAS: + case ABS_BRAKE: + + iforce->dev.absmax[t] = 255; + iforce->dev.absmin[t] = 0; + break; + + case ABS_RUDDER: + + iforce->dev.absmax[t] = 127; + iforce->dev.absmin[t] = -128; + break; + + case ABS_HAT0X: + case ABS_HAT0Y: + case ABS_HAT1X: + case ABS_HAT1Y: + iforce->dev.absmax[t] = 1; + iforce->dev.absmin[t] = -1; + break; + } + } + + for (i = 0; iforce->type->ff[i] >= 0; i++) + set_bit(iforce->type->ff[i], iforce->dev.ffbit); + +/* + * Register input device. + */ + + input_register_device(&iforce->dev); + + printk(KERN_DEBUG "iforce->dev.open = %p\n", iforce->dev.open); + + printk(KERN_INFO "input: %s [%d effects, %ld bytes memory]\n", + iforce->dev.name, iforce->dev.ff_effects_max, + iforce->device_memory.end); + + return 0; +} + +static int __init iforce_init(void) +{ +#ifdef IFORCE_USB + usb_register(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_register_device(&iforce_serio_dev); +#endif + return 0; +} + +static void __exit iforce_exit(void) +{ +#ifdef IFORCE_USB + usb_deregister(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_unregister_device(&iforce_serio_dev); +#endif +} + +module_init(iforce_init); +module_exit(iforce_exit); diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c new file mode 100644 index 000000000000..922894bb13ea --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -0,0 +1,314 @@ +/* + * $Id: iforce-packets.c,v 1.15 2002/06/09 11:08:04 jdeneux Exp $ + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +static struct { + __s32 x; + __s32 y; +} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + + +void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) +{ + int i; + + printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd); + for (i = 0; i < LO(cmd); i++) + printk("%02x ", data[i]); + printk(")\n"); +} + +/* + * Send a packet of bytes to the device + */ +int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) +{ + /* Copy data to buffer */ + int n = LO(cmd); + int c; + int empty; + int head, tail; + unsigned long flags; + +/* + * Update head and tail of xmit buffer + */ + spin_lock_irqsave(&iforce->xmit_lock, flags); + + head = iforce->xmit.head; + tail = iforce->xmit.tail; + + if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) { + printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n"); + spin_unlock_irqrestore(&iforce->xmit_lock, flags); + return -1; + } + + empty = head == tail; + XMIT_INC(iforce->xmit.head, n+2); + +/* + * Store packet in xmit buffer + */ + iforce->xmit.buf[head] = HI(cmd); + XMIT_INC(head, 1); + iforce->xmit.buf[head] = LO(cmd); + XMIT_INC(head, 1); + + c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE); + if (n < c) c=n; + + memcpy(&iforce->xmit.buf[head], + data, + c); + if (n != c) { + memcpy(&iforce->xmit.buf[0], + data + c, + n - c); + } + XMIT_INC(head, n); + + spin_unlock_irqrestore(&iforce->xmit_lock, flags); +/* + * If necessary, start the transmission + */ + switch (iforce->bus) { + +#ifdef IFORCE_232 + case IFORCE_232: + if (empty) + iforce_serial_xmit(iforce); + break; +#endif +#ifdef IFORCE_USB + case IFORCE_USB: + + if (iforce->usbdev && empty && + !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { + + iforce_usb_xmit(iforce); + } + break; +#endif + } + return 0; +} + +/* Start or stop an effect */ +int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value) +{ + unsigned char data[3]; + +printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value); + + data[0] = LO(id); + data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0; + data[2] = LO(value); + return iforce_send_packet(iforce, FF_CMD_PLAY, data); +} + +/* Mark an effect that was being updated as ready. That means it can be updated + * again */ +static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) +{ + int i; + for (i=0; idev.ff_effects_max; ++i) { + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && + (iforce->core_effects[i].mod1_chunk.start == addr || + iforce->core_effects[i].mod2_chunk.start == addr)) { + clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags); + return 0; + } + } + printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr); + return -1; +} + +void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) +{ + struct input_dev *dev = &iforce->dev; + int i; + static int being_used = 0; + + if (being_used) + printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used); + being_used++; + +#ifdef IFORCE_232 + if (HI(iforce->expect_packet) == HI(cmd)) { + iforce->expect_packet = 0; + iforce->ecmd = cmd; + memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); + if (waitqueue_active(&iforce->wait)) + wake_up(&iforce->wait); + } +#endif + + if (!iforce->type) { + being_used--; + return; + } + + switch (HI(cmd)) { + + case 0x01: /* joystick position data */ + case 0x03: /* wheel position data */ + + if (HI(cmd) == 1) { + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); + input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); + if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) + input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); + } else { + input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_GAS, 255 - data[2]); + input_report_abs(dev, ABS_BRAKE, 255 - data[3]); + } + + input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); + + for (i = 0; iforce->type->btn[i] >= 0; i++) + input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); + + /* If there are untouched bits left, interpret them as the second hat */ + if (i <= 8) { + int btns = data[6]; + if (test_bit(ABS_HAT1X, dev->absbit)) { + if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1); + else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1); + else input_report_abs(dev, ABS_HAT1X, 0); + } + if (test_bit(ABS_HAT1Y, dev->absbit)) { + if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1); + else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1); + else input_report_abs(dev, ABS_HAT1Y, 0); + } + } + + break; + + case 0x02: /* status report */ + input_report_key(dev, BTN_DEAD, data[0] & 0x02); + + /* Check if an effect was just started or stopped */ + i = data[1] & 0x7f; + if (data[1] & 0x80) { + if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); + } + } + else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { + /* Report stop event */ + input_report_ff_status(dev, i, FF_STATUS_STOPPED); + } + if (LO(cmd) > 3) { + int j; + for (j=3; jbus) { + + case IFORCE_USB: + +#ifdef IFORCE_USB + iforce->cr.bRequest = packet[0]; + iforce->ctrl->dev = iforce->usbdev; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&iforce->wait, &wait); + + if (usb_submit_urb(iforce->ctrl, GFP_KERNEL)) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&iforce->wait, &wait); + return -1; + } + + while (timeout && iforce->ctrl->status == -EINPROGRESS) + timeout = schedule_timeout(timeout); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&iforce->wait, &wait); + + if (!timeout) { + usb_unlink_urb(iforce->ctrl); + return -1; + } +#else + printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n"); +#endif + break; + + case IFORCE_232: + +#ifdef IFORCE_232 + iforce->expect_packet = FF_CMD_QUERY; + iforce_send_packet(iforce, FF_CMD_QUERY, packet); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&iforce->wait, &wait); + + while (timeout && iforce->expect_packet) + timeout = schedule_timeout(timeout); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&iforce->wait, &wait); + + if (!timeout) { + iforce->expect_packet = 0; + return -1; + } +#else + printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n"); +#endif + break; + + default: + printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n", + iforce->bus); + break; + } + + return -(iforce->edata[0] != packet[0]); +} + diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c new file mode 100644 index 000000000000..31e21fb431f6 --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -0,0 +1,166 @@ +/* + * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $ + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2001 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +void iforce_serial_xmit(struct iforce *iforce) +{ + unsigned char cs; + int i; + unsigned long flags; + + if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { + set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags); + return; + } + + spin_lock_irqsave(&iforce->xmit_lock, flags); + +again: + if (iforce->xmit.head == iforce->xmit.tail) { + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + spin_unlock_irqrestore(&iforce->xmit_lock, flags); + return; + } + + cs = 0x2b; + + serio_write(iforce->serio, 0x2b); + + serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); + cs ^= iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + + for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { + serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); + cs ^= iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + } + + serio_write(iforce->serio, cs); + + if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)) + goto again; + + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + + spin_unlock_irqrestore(&iforce->xmit_lock, flags); +} + +static void iforce_serio_write_wakeup(struct serio *serio) +{ + iforce_serial_xmit((struct iforce *)serio->private); +} + +static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct iforce* iforce = serio->private; + + if (!iforce->pkt) { + if (data != 0x2b) { + return; + } + iforce->pkt = 1; + return; + } + + if (!iforce->id) { + if (data > 3 && data != 0xff) { + iforce->pkt = 0; + return; + } + iforce->id = data; + return; + } + + if (!iforce->len) { + if (data > IFORCE_MAX_LENGTH) { + iforce->pkt = 0; + iforce->id = 0; + return; + } + iforce->len = data; + return; + } + + if (iforce->idx < iforce->len) { + iforce->csum += iforce->data[iforce->idx++] = data; + return; + } + + if (iforce->idx == iforce->len) { + iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); + iforce->pkt = 0; + iforce->id = 0; + iforce->len = 0; + iforce->idx = 0; + iforce->csum = 0; + } +} + +static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) +{ + struct iforce *iforce; + if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) + return; + + if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return; + memset(iforce, 0, sizeof(struct iforce)); + + iforce->bus = IFORCE_232; + iforce->serio = serio; + serio->private = iforce; + + if (serio_open(serio, dev)) { + kfree(iforce); + return; + } + + if (iforce_init_device(iforce)) { + serio_close(serio); + kfree(iforce); + return; + } +} + +static void iforce_serio_disconnect(struct serio *serio) +{ + struct iforce* iforce = serio->private; + + input_unregister_device(&iforce->dev); + serio_close(serio); + kfree(iforce); +} + +struct serio_dev iforce_serio_dev = { + write_wakeup: iforce_serio_write_wakeup, + interrupt: iforce_serio_irq, + connect: iforce_serio_connect, + disconnect: iforce_serio_disconnect, +}; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c new file mode 100644 index 000000000000..760534a89f72 --- /dev/null +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -0,0 +1,214 @@ + /* + * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $ + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include "iforce.h" + +void iforce_usb_xmit(struct iforce *iforce) +{ + int n, c; + unsigned long flags; + + spin_lock_irqsave(&iforce->xmit_lock, flags); + + if (iforce->xmit.head == iforce->xmit.tail) { + clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags); + spin_unlock_irqrestore(&iforce->xmit_lock, flags); + return; + } + + ((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + n = iforce->xmit.buf[iforce->xmit.tail]; + XMIT_INC(iforce->xmit.tail, 1); + + iforce->out->transfer_buffer_length = n + 1; + iforce->out->dev = iforce->usbdev; + + /* Copy rest of data then */ + c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE); + if (n < c) c=n; + + memcpy(iforce->out->transfer_buffer + 1, + &iforce->xmit.buf[iforce->xmit.tail], + c); + if (n != c) { + memcpy(iforce->out->transfer_buffer + 1 + c, + &iforce->xmit.buf[0], + n-c); + } + XMIT_INC(iforce->xmit.tail, n); + + if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) { + printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n); + } + + /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended. + * As long as the urb completion handler is not called, the transmiting + * is considered to be running */ + spin_unlock_irqrestore(&iforce->xmit_lock, flags); +} + +static void iforce_usb_irq(struct urb *urb) +{ + struct iforce *iforce = urb->context; + if (urb->status) return; + iforce_process_packet(iforce, + (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1); +} + +static void iforce_usb_out(struct urb *urb) +{ + struct iforce *iforce = urb->context; + + if (urb->status) { + printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status); + return; + } + + iforce_usb_xmit(iforce); + + if (waitqueue_active(&iforce->wait)) + wake_up(&iforce->wait); +} + +static void iforce_usb_ctrl(struct urb *urb) +{ + struct iforce *iforce = urb->context; + if (urb->status) return; + iforce->ecmd = 0xff00 | urb->actual_length; + if (waitqueue_active(&iforce->wait)) + wake_up(&iforce->wait); +} + +static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *epirq, *epout; + struct iforce *iforce; + + epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1; + + if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) + goto fail; + + memset(iforce, 0, sizeof(struct iforce)); + + if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL))) { + goto fail; + } + + if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) { + goto fail; + } + + if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) { + goto fail; + } + + iforce->bus = IFORCE_USB; + iforce->usbdev = dev; + + iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE; + iforce->cr.wIndex = 0; + iforce->cr.wLength = 16; + + usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress), + iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval); + + usb_fill_bulk_urb(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress), + iforce + 1, 32, iforce_usb_out, iforce); + + usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0), + (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce); + + if (iforce_init_device(iforce)) goto fail; + + return iforce; + +fail: + if (iforce) { + if (iforce->irq) usb_free_urb(iforce->irq); + if (iforce->out) usb_free_urb(iforce->out); + if (iforce->ctrl) usb_free_urb(iforce->ctrl); + kfree(iforce); + } + + return NULL; +} + +/* Called by iforce_delete() */ +void iforce_usb_delete(struct iforce* iforce) +{ + usb_unlink_urb(iforce->irq); +/* Is it ok to unlink those ? */ + usb_unlink_urb(iforce->out); + usb_unlink_urb(iforce->ctrl); + + usb_free_urb(iforce->irq); + usb_free_urb(iforce->out); + usb_free_urb(iforce->ctrl); +} + +static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) +{ + struct iforce *iforce = ptr; + int open = iforce->dev.handle->open; + + iforce->usbdev = NULL; + input_unregister_device(&iforce->dev); + + if (!open) { + iforce_delete_device(iforce); + kfree(iforce); + } +} + +static struct usb_device_id iforce_usb_ids [] = { + { USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */ + { USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */ + { USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */ + { USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */ + { USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */ + { USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */ + { USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */ + { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */ + { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, iforce_usb_ids); + +struct usb_driver iforce_usb_driver = { + owner: THIS_MODULE, + name: "iforce", + probe: iforce_usb_probe, + disconnect: iforce_usb_disconnect, + id_table: iforce_usb_ids, +}; diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h new file mode 100644 index 000000000000..fec1b258225c --- /dev/null +++ b/drivers/input/joystick/iforce/iforce.h @@ -0,0 +1,194 @@ +/* + * $Id: iforce.h,v 1.12 2002/06/09 11:08:04 jdeneux Exp $ + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2001-2002 Johann Deneux + * + * USB/RS232 I-Force joysticks and wheels. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FF: This module provides arbitrary resource management routines. + * I use it to manage the device's memory. + * Despite the name of this module, I am *not* going to access the ioports. + */ +#include + +#define IFORCE_MAX_LENGTH 16 + +#if defined(CONFIG_JOYSTICK_IFORCE_232) +#define IFORCE_232 1 +#endif +#if defined(CONFIG_JOYSTICK_IFORCE_USB) +#define IFORCE_USB 2 +#endif + +#define FALSE 0 +#define TRUE 1 + +#define FF_EFFECTS_MAX 32 + +/* Each force feedback effect is made of one core effect, which can be + * associated to at most to effect modifiers + */ +#define FF_MOD1_IS_USED 0 +#define FF_MOD2_IS_USED 1 +#define FF_CORE_IS_USED 2 +#define FF_CORE_IS_PLAYED 3 /* Effect is currently being played */ +#define FF_CORE_SHOULD_PLAY 4 /* User wants the effect to be played */ +#define FF_CORE_UPDATE 5 /* Effect is being updated */ +#define FF_MODCORE_MAX 5 + +#define CHECK_OWNERSHIP(i, iforce) \ + ((i) < FF_EFFECTS_MAX && i >= 0 && \ + test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \ + (current->pid == 0 || \ + (iforce)->core_effects[(i)].owner == current->pid)) + +struct iforce_core_effect { + /* Information about where modifiers are stored in the device's memory */ + struct resource mod1_chunk; + struct resource mod2_chunk; + unsigned long flags[NBITS(FF_MODCORE_MAX)]; + pid_t owner; + /* Used to keep track of parameters of an effect. They are needed + * to know what parts of an effect changed in an update operation. + * We try to send only parameter packets if possible, as sending + * effect parameter requires the effect to be stoped and restarted + */ + struct ff_effect effect; +}; + +#define FF_CMD_EFFECT 0x010e +#define FF_CMD_ENVELOPE 0x0208 +#define FF_CMD_MAGNITUDE 0x0303 +#define FF_CMD_PERIOD 0x0407 +#define FF_CMD_CONDITION 0x050a + +#define FF_CMD_AUTOCENTER 0x4002 +#define FF_CMD_PLAY 0x4103 +#define FF_CMD_ENABLE 0x4201 +#define FF_CMD_GAIN 0x4301 + +#define FF_CMD_QUERY 0xff01 + +/* Buffer for async write */ +#define XMIT_SIZE 256 +#define XMIT_INC(var, n) (var)+=n; (var)&= XMIT_SIZE -1 +/* iforce::xmit_flags */ +#define IFORCE_XMIT_RUNNING 0 +#define IFORCE_XMIT_AGAIN 1 + +struct iforce_device { + u16 idvendor; + u16 idproduct; + char *name; + signed short *btn; + signed short *abs; + signed short *ff; +}; + +struct iforce { + struct input_dev dev; /* Input device interface */ + struct iforce_device *type; + int bus; + + unsigned char data[IFORCE_MAX_LENGTH]; + unsigned char edata[IFORCE_MAX_LENGTH]; + u16 ecmd; + u16 expect_packet; + +#ifdef IFORCE_232 + struct serio *serio; /* RS232 transfer */ + int idx, pkt, len, id; + unsigned char csum; +#endif +#ifdef IFORCE_USB + struct usb_device *usbdev; /* USB transfer */ + struct urb *irq, *out, *ctrl; + struct usb_ctrlrequest cr; +#endif + spinlock_t xmit_lock; + /* Buffer used for asynchronous sending of bytes to the device */ + struct circ_buf xmit; + unsigned char xmit_data[XMIT_SIZE]; + long xmit_flags[1]; + + /* Force Feedback */ + wait_queue_head_t wait; + struct resource device_memory; + struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; + struct semaphore mem_mutex; +}; + +/* Get hi and low bytes of a 16-bits int */ +#define HI(a) ((unsigned char)((a) >> 8)) +#define LO(a) ((unsigned char)((a) & 0xff)) + +/* For many parameters, it seems that 0x80 is a special value that should + * be avoided. Instead, we replace this value by 0x7f + */ +#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8)) + +/* Encode a time value */ +#define TIME_SCALE(a) (a) + + +/* Public functions */ +/* iforce-serio.c */ +void iforce_serial_xmit(struct iforce *iforce); + +/* iforce-usb.c */ +void iforce_usb_xmit(struct iforce *iforce); +void iforce_usb_delete(struct iforce *iforce); + +/* iforce-main.c */ +int iforce_init_device(struct iforce *iforce); +void iforce_delete_device(struct iforce *iforce); + +/* iforce-packets.c */ +int iforce_control_playback(struct iforce*, u16 id, unsigned int); +void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data); +int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data); +void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ; +int iforce_get_id_packet(struct iforce *iforce, char *packet); + +/* iforce-ff.c */ +int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update); +int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update); +int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update); + +/* Public variables */ +extern struct serio_dev iforce_serio_dev; +extern struct usb_driver iforce_usb_driver; diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c new file mode 100644 index 000000000000..70da80e89404 --- /dev/null +++ b/drivers/input/joystick/twidjoy.c @@ -0,0 +1,264 @@ +/* + * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $ + * + * derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp" + * + * Copyright (c) 2001 Arndt Schoenewald + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2000 Mark Fletcher + * + * Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany + */ + +/* + * Driver to use Handykey's Twiddler (the first edition, i.e. the one with + * the RS232 interface) as a joystick under Linux + * + * The Twiddler is a one-handed chording keyboard featuring twelve buttons on + * the front, six buttons on the top, and a built-in tilt sensor. The buttons + * on the front, which are grouped as four rows of three buttons, are pressed + * by the four fingers (this implies only one button per row can be held down + * at the same time) and the buttons on the top are for the thumb. The tilt + * sensor delivers X and Y axis data depending on how the Twiddler is held. + * Additional information can be found at http://www.handykey.com. + * + * This driver does not use the Twiddler for its intended purpose, i.e. as + * a chording keyboard, but as a joystick: pressing and releasing a button + * immediately sends a corresponding button event, and tilting it generates + * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game + * controller with amazing 18 buttons :-) + * + * Note: The Twiddler2 (the successor of the Twiddler that connects directly + * to the PS/2 keyboard and mouse ports) is NOT supported by this driver! + * + * For questions or feedback regarding this driver module please contact: + * Arndt Schoenewald + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +/* + * Constants. + */ + +#define TWIDJOY_MAX_LENGTH 5 + +static char *twidjoy_name = "Handykey Twiddler"; + +static struct twidjoy_button_spec { + int bitshift; + int bitmask; + int buttons[3]; +} +twidjoy_buttons[] = { + { 0, 3, { BTN_A, BTN_B, BTN_C } }, + { 2, 3, { BTN_X, BTN_Y, BTN_Z } }, + { 4, 3, { BTN_TL, BTN_TR, BTN_TR2 } }, + { 6, 3, { BTN_SELECT, BTN_START, BTN_MODE } }, + { 8, 1, { BTN_BASE5 } }, + { 9, 1, { BTN_BASE } }, + { 10, 1, { BTN_BASE3 } }, + { 11, 1, { BTN_BASE4 } }, + { 12, 1, { BTN_BASE2 } }, + { 13, 1, { BTN_BASE6 } }, + { 0, 0, { 0 } } +}; + +/* + * Per-Twiddler data. + */ + +struct twidjoy { + struct input_dev dev; + int idx; + unsigned char data[TWIDJOY_MAX_LENGTH]; + char phys[32]; +}; + +/* + * twidjoy_process_packet() decodes packets the driver receives from the + * Twiddler. It updates the data accordingly. + */ + +static void twidjoy_process_packet(struct twidjoy *twidjoy) +{ + if (twidjoy->idx == TWIDJOY_MAX_LENGTH) { + struct input_dev *dev = &twidjoy->dev; + unsigned char *data = twidjoy->data; + struct twidjoy_button_spec *bp; + int button_bits, abs_x, abs_y; + + button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f); + + for (bp = twidjoy_buttons; bp->bitmask; bp++) { + int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift; + int i; + + for (i = 0; i < bp->bitmask; i++) + input_report_key(dev, bp->buttons[i], i+1 == value); + } + + abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2); + if (data[4] & 0x08) abs_x -= 256; + + abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0); + if (data[3] & 0x02) abs_y -= 256; + + input_report_abs(dev, ABS_X, -abs_x); + input_report_abs(dev, ABS_Y, +abs_y); + } + + return; +} + +/* + * twidjoy_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct twidjoy *twidjoy = serio->private; + + /* All Twiddler packets are 5 bytes. The fact that the first byte + * has a MSB of 0 and all other bytes have a MSB of 1 can be used + * to check and regain sync. */ + + if ((data & 0x80) == 0) + twidjoy->idx = 0; /* this byte starts a new packet */ + else if (twidjoy->idx == 0) + return; /* wrong MSB -- ignore this byte */ + + if (twidjoy->idx < TWIDJOY_MAX_LENGTH) + twidjoy->data[twidjoy->idx++] = data; + + if (twidjoy->idx == TWIDJOY_MAX_LENGTH) { + twidjoy_process_packet(twidjoy); + twidjoy->idx = 0; + } + + return; +} + +/* + * twidjoy_disconnect() is the opposite of twidjoy_connect() + */ + +static void twidjoy_disconnect(struct serio *serio) +{ + struct twidjoy *twidjoy = serio->private; + input_unregister_device(&twidjoy->dev); + serio_close(serio); + kfree(twidjoy); +} + +/* + * twidjoy_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Twiddler, and if found, registers + * it as an input device. + */ + +static void twidjoy_connect(struct serio *serio, struct serio_dev *dev) +{ + struct twidjoy_button_spec *bp; + struct twidjoy *twidjoy; + int i; + + if (serio->type != (SERIO_RS232 | SERIO_TWIDJOY)) + return; + + if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL))) + return; + + memset(twidjoy, 0, sizeof(struct twidjoy)); + + sprintf(twidjoy->phys, "%s/input0", serio->phys); + + twidjoy->dev.name = twidjoy_name; + twidjoy->dev.phys = twidjoy->phys; + twidjoy->dev.idbus = BUS_RS232; + twidjoy->dev.idvendor = SERIO_TWIDJOY; + twidjoy->dev.idproduct = 0x0001; + twidjoy->dev.idversion = 0x0100; + + twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (bp = twidjoy_buttons; bp->bitmask; bp++) { + for (i = 0; i < bp->bitmask; i++) + set_bit(bp->buttons[i], twidjoy->dev.keybit); + } + + twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (i = 0; i < 2; i++) { + twidjoy->dev.absmax[ABS_X+i] = 50; + twidjoy->dev.absmin[ABS_X+i] = -50; + + /* TODO: arndt 20010708: Are these values appropriate? */ + twidjoy->dev.absfuzz[ABS_X+i] = 4; + twidjoy->dev.absflat[ABS_X+i] = 4; + } + + twidjoy->dev.private = twidjoy; + + serio->private = twidjoy; + + if (serio_open(serio, dev)) { + kfree(twidjoy); + return; + } + + input_register_device(&twidjoy->dev); + + printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys); +} + +/* + * The serio device structure. + */ + +static struct serio_dev twidjoy_dev = { + interrupt: twidjoy_interrupt, + connect: twidjoy_connect, + disconnect: twidjoy_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init twidjoy_init(void) +{ + serio_register_device(&twidjoy_dev); + return 0; +} + +void __exit twidjoy_exit(void) +{ + serio_unregister_device(&twidjoy_dev); +} + +module_init(twidjoy_init); +module_exit(twidjoy_exit); -- 2.39.5