Commit 07b8fb25 authored by Vojtech Pavlik's avatar Vojtech Pavlik

Update the iforce driver to the latest revision (it now lives in a

separate directory), add twiddler and guillemot drivers.
parent 7f125092
...@@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP ...@@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP
The module will be called grip.o. If you want to compile it as a The module will be called grip.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>. module, say M here and read <file:Documentation/modules.txt>.
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 <file:Documentation/modules.txt>.
CONFIG_JOYSTICK_INTERACT CONFIG_JOYSTICK_INTERACT
Say Y here if you have an InterAct gameport or joystick Say Y here if you have an InterAct gameport or joystick
communicating digitally over the gameport. communicating digitally over the gameport.
...@@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER ...@@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER
The module will be called stinger.o. If you want to compile it as a The module will be called stinger.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>. module, say M here and read <file:Documentation/modules.txt>.
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 <file:Documentation/modules.txt>.
CONFIG_JOYSTICK_DB9 CONFIG_JOYSTICK_DB9
Say Y here if you have a Sega Master System gamepad, Sega Genesis Say Y here if you have a Sega Master System gamepad, Sega Genesis
gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
......
...@@ -10,6 +10,7 @@ dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_JOYSTICK_ADI ...@@ -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 ' 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 ' 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 ' 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 ' 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 ' 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 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_ ...@@ -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 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 ' 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 ' 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, 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 dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_JOYSTICK_GAMECON $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
......
...@@ -2,24 +2,6 @@ ...@@ -2,24 +2,6 @@
# Makefile for the input core drivers. # 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. # Each configuration option enables a list of files.
obj-$(CONFIG_JOYSTICK_A3D) += a3d.o obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
...@@ -31,8 +13,9 @@ obj-$(CONFIG_JOYSTICK_DB9) += db9.o ...@@ -31,8 +13,9 @@ obj-$(CONFIG_JOYSTICK_DB9) += db9.o
obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o
obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP) += grip.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_INTERACT) += interact.o
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
...@@ -40,8 +23,33 @@ obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o ...@@ -40,8 +23,33 @@ obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o
obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o
obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o
obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.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. # The global Rules.make.
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*
* $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 <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/gameport.h>
#include <linux/input.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
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);
/*
* $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@suse.cz>, or by paper mail:
* Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/serio.h>
#include <linux/config.h>
/* 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 <linux/ioport.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>, Johann Deneux <deneux@ifrance.com>");
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);
#
# 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 $@
/*
* $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@ucw.cz>, 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;
}
/*
* $Id: iforce-main.c,v 1.18 2002/06/09 11:03:03 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include "iforce.h"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>");
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<<FF_CORE_IS_USED); /* Only IS_USED bit must be set */
is_update = FALSE;
}
else {
/* We want to update an effect */
if (!CHECK_OWNERSHIP(effect->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; i<dev->ff_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; i<dev->ff_effects_max; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
break;
}
if (i<dev->ff_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);
/*
* $Id: iforce-packets.c,v 1.15 2002/06/09 11:08:04 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@ucw.cz>, 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; i<iforce->dev.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; j<LO(cmd); j+=2) {
mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
}
}
break;
}
being_used--;
}
int iforce_get_id_packet(struct iforce *iforce, char *packet)
{
DECLARE_WAITQUEUE(wait, current);
int timeout = HZ; /* 1 second */
switch (iforce->bus) {
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]);
}
/*
* $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@ucw.cz>, 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,
};
/*
* $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@ucw.cz>, 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,
};
/*
* $Id: iforce.h,v 1.12 2002/06/09 11:08:04 jdeneux Exp $
*
* Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
* Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
*
* 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 <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/serio.h>
#include <linux/config.h>
#include <linux/circ_buf.h>
#include <asm/semaphore.h>
/* 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 <linux/ioport.h>
#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;
/*
* $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 <arndt@quelltext.com>
*/
/*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
/*
* 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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment