Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
07b8fb25
Commit
07b8fb25
authored
Jul 03, 2002
by
Vojtech Pavlik
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update the iforce driver to the latest revision (it now lives in a
separate directory), add twiddler and guillemot drivers.
parent
7f125092
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
2611 additions
and
1243 deletions
+2611
-1243
drivers/input/joystick/Config.help
drivers/input/joystick/Config.help
+18
-0
drivers/input/joystick/Config.in
drivers/input/joystick/Config.in
+2
-0
drivers/input/joystick/Makefile
drivers/input/joystick/Makefile
+27
-19
drivers/input/joystick/guillemot.c
drivers/input/joystick/guillemot.c
+285
-0
drivers/input/joystick/iforce.c
drivers/input/joystick/iforce.c
+0
-1224
drivers/input/joystick/iforce/Makefile
drivers/input/joystick/iforce/Makefile
+41
-0
drivers/input/joystick/iforce/iforce-ff.c
drivers/input/joystick/iforce/iforce-ff.c
+543
-0
drivers/input/joystick/iforce/iforce-main.c
drivers/input/joystick/iforce/iforce-main.c
+543
-0
drivers/input/joystick/iforce/iforce-packets.c
drivers/input/joystick/iforce/iforce-packets.c
+314
-0
drivers/input/joystick/iforce/iforce-serio.c
drivers/input/joystick/iforce/iforce-serio.c
+166
-0
drivers/input/joystick/iforce/iforce-usb.c
drivers/input/joystick/iforce/iforce-usb.c
+214
-0
drivers/input/joystick/iforce/iforce.h
drivers/input/joystick/iforce/iforce.h
+194
-0
drivers/input/joystick/twidjoy.c
drivers/input/joystick/twidjoy.c
+264
-0
No files found.
drivers/input/joystick/Config.help
View file @
07b8fb25
...
...
@@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP
The module will be called grip.o. If you want to compile it as a
module, say M here and read <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
Say Y here if you have an InterAct gameport or joystick
communicating digitally over the gameport.
...
...
@@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER
The module will be called stinger.o. If you want to compile it as a
module, say M here and read <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
Say Y here if you have a Sega Master System gamepad, Sega Genesis
gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
...
...
drivers/input/joystick/Config.in
View file @
07b8fb25
...
...
@@ -10,6 +10,7 @@ dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_JOYSTICK_ADI
dep_tristate ' Creative Labs Blaster Cobra gamepad' CONFIG_JOYSTICK_COBRA $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Genius Flight2000 Digital joysticks and gamepads' CONFIG_JOYSTICK_GF2K $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOYSTICK_GRIP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Guillemot joysticks and gamepads' CONFIG_JOYSTICK_GUILLEMOT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' InterAct digital joysticks and gamepads' CONFIG_JOYSTICK_INTERACT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' Microsoft SideWinder digital joysticks and gamepads' CONFIG_JOYSTICK_SIDEWINDER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOYSTICK_TMDC $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
...
...
@@ -21,6 +22,7 @@ dep_tristate ' LogiCad3d Magellan/SpaceMouse 6dof controllers' CONFIG_JOYSTICK_
dep_tristate ' SpaceTec SpaceOrb/Avenger 6dof controllers' CONFIG_JOYSTICK_SPACEORB $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' SpaceTec SpaceBall 6dof controllers' CONFIG_JOYSTICK_SPACEBALL $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' Gravis Stinger gamepad' CONFIG_JOYSTICK_STINGER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' Twiddler as as joystick' CONFIG_JOYSTICK_TWIDDLER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
dep_tristate ' Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_JOYSTICK_DB9 $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_JOYSTICK_GAMECON $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
...
...
drivers/input/joystick/Makefile
View file @
07b8fb25
...
...
@@ -2,24 +2,6 @@
# Makefile for the input core drivers.
#
# I-Force may need both USB and RS-232
CONFIG_JOYSTICK_IFORCE
:=
n
ifeq
($(CONFIG_JOYSTICK_IFORCE_232),y)
ifeq
($(CONFIG_JOYSTICK_IFORCE_USB),y)
CONFIG_JOYSTICK_IFORCE
:=
y
endif
endif
ifeq
($(CONFIG_JOYSTICK_IFORCE_232),m)
CONFIG_JOYSTICK_IFORCE
:=
m
endif
ifeq
($(CONFIG_JOYSTICK_IFORCE_USB),m)
CONFIG_JOYSTICK_IFORCE
:=
m
endif
# Each configuration option enables a list of files.
obj-$(CONFIG_JOYSTICK_A3D)
+=
a3d.o
...
...
@@ -31,8 +13,9 @@ obj-$(CONFIG_JOYSTICK_DB9) += db9.o
obj-$(CONFIG_JOYSTICK_GAMECON)
+=
gamecon.o
obj-$(CONFIG_JOYSTICK_GF2K)
+=
gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP)
+=
grip.o
obj-$(CONFIG_JOYSTICK_
IFORCE)
+=
iforce
.o
obj-$(CONFIG_JOYSTICK_
GUILLEMOT)
+=
guillemot
.o
obj-$(CONFIG_JOYSTICK_INTERACT)
+=
interact.o
obj-$(CONFIG_JOYSTICK_JOYDUMP)
+=
joydump.o
obj-$(CONFIG_JOYSTICK_MAGELLAN)
+=
magellan.o
obj-$(CONFIG_JOYSTICK_SIDEWINDER)
+=
sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL)
+=
spaceball.o
...
...
@@ -40,8 +23,33 @@ obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
obj-$(CONFIG_JOYSTICK_STINGER)
+=
stinger.o
obj-$(CONFIG_JOYSTICK_TMDC)
+=
tmdc.o
obj-$(CONFIG_JOYSTICK_TURBOGRAFX)
+=
turbografx.o
obj-$(CONFIG_JOYSTICK_TWIDJOY)
+=
twidjoy.o
obj-$(CONFIG_JOYSTICK_WARRIOR)
+=
warrior.o
# I-Force may need both USB and RS-232
CONFIG_JOYSTICK_IFORCE
:=
n
ifeq
($(CONFIG_JOYSTICK_IFORCE_232),y)
ifeq
($(CONFIG_JOYSTICK_IFORCE_USB),y)
CONFIG_JOYSTICK_IFORCE
:=
y
endif
endif
ifeq
($(CONFIG_JOYSTICK_IFORCE_232),m)
CONFIG_JOYSTICK_IFORCE
:=
m
endif
ifeq
($(CONFIG_JOYSTICK_IFORCE_USB),m)
CONFIG_JOYSTICK_IFORCE
:=
m
endif
subdir-$(CONFIG_JOYSTICK_IFORCE)
+=
iforce
ifeq
($(CONFIG_JOYSTICK_IFORCE),y)
obj-y
+=
iforce/iforce-drv.o
endif
# The global Rules.make.
include
$(TOPDIR)/Rules.make
drivers/input/joystick/guillemot.c
0 → 100644
View file @
07b8fb25
/*
* $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
);
drivers/input/joystick/iforce.c
deleted
100644 → 0
View file @
7f125092
/*
* $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
);
drivers/input/joystick/iforce/Makefile
0 → 100644
View file @
07b8fb25
#
# 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
$@
drivers/input/joystick/iforce/iforce-ff.c
0 → 100644
View file @
07b8fb25
/*
* $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
;
}
drivers/input/joystick/iforce/iforce-main.c
0 → 100644
View file @
07b8fb25
/*
* $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
);
drivers/input/joystick/iforce/iforce-packets.c
0 → 100644
View file @
07b8fb25
/*
* $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
]);
}
drivers/input/joystick/iforce/iforce-serio.c
0 → 100644
View file @
07b8fb25
/*
* $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
,
};
drivers/input/joystick/iforce/iforce-usb.c
0 → 100644
View file @
07b8fb25
/*
* $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
,
};
drivers/input/joystick/iforce/iforce.h
0 → 100644
View file @
07b8fb25
/*
* $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
;
drivers/input/joystick/twidjoy.c
0 → 100644
View file @
07b8fb25
/*
* $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
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment