2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) 1998 - 2001 Vojtech Pavlik
*/
/*
* Genius Flight 2000 joystick 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/delay.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/gameport.h>
2005-10-31 02:03:48 +03:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
# define DRIVER_DESC "Genius Flight 2000 joystick driver"
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
# define GF2K_START 400 /* The time we wait for the first bit [400 us] */
# define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */
# define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */
# define GF2K_LENGTH 80 /* Max number of triplets in a packet */
/*
* Genius joystick ids . . .
*/
# define GF2K_ID_G09 1
# define GF2K_ID_F30D 2
# define GF2K_ID_F30 3
# define GF2K_ID_F31D 4
# define GF2K_ID_F305 5
# define GF2K_ID_F23P 6
# define GF2K_ID_F31 7
# define GF2K_ID_MAX 7
static char gf2k_length [ ] = { 40 , 40 , 40 , 40 , 40 , 40 , 40 , 40 } ;
static char gf2k_hat_to_axis [ ] [ 2 ] = { { 0 , 0 } , { 0 , - 1 } , { 1 , - 1 } , { 1 , 0 } , { 1 , 1 } , { 0 , 1 } , { - 1 , 1 } , { - 1 , 0 } , { - 1 , - 1 } } ;
static char * gf2k_names [ ] = { " " , " Genius G-09D " , " Genius F-30D " , " Genius F-30 " , " Genius MaxFighter F-31D " ,
" Genius F-30-5 " , " Genius Flight2000 F-23 " , " Genius F-31 " } ;
static unsigned char gf2k_hats [ ] = { 0 , 2 , 0 , 0 , 2 , 0 , 2 , 0 } ;
static unsigned char gf2k_axes [ ] = { 0 , 2 , 0 , 0 , 4 , 0 , 4 , 0 } ;
static unsigned char gf2k_joys [ ] = { 0 , 0 , 0 , 0 , 10 , 0 , 8 , 0 } ;
static unsigned char gf2k_pads [ ] = { 0 , 6 , 0 , 0 , 0 , 0 , 0 , 0 } ;
static unsigned char gf2k_lens [ ] = { 0 , 18 , 0 , 0 , 18 , 0 , 18 , 0 } ;
static unsigned char gf2k_abs [ ] = { ABS_X , ABS_Y , ABS_THROTTLE , ABS_RUDDER , ABS_GAS , ABS_BRAKE } ;
static short gf2k_btn_joy [ ] = { BTN_TRIGGER , BTN_THUMB , BTN_TOP , BTN_TOP2 , BTN_BASE , BTN_BASE2 , BTN_BASE3 , BTN_BASE4 } ;
static short gf2k_btn_pad [ ] = { BTN_A , BTN_B , BTN_C , BTN_X , BTN_Y , BTN_Z , BTN_TL , BTN_TR , BTN_TL2 , BTN_TR2 , BTN_START , BTN_SELECT } ;
static short gf2k_seq_reset [ ] = { 240 , 340 , 0 } ;
static short gf2k_seq_digital [ ] = { 590 , 320 , 860 , 0 } ;
struct gf2k {
struct gameport * gameport ;
2005-09-15 11:01:52 +04:00
struct input_dev * dev ;
2005-04-17 02:20:36 +04:00
int reads ;
int bads ;
unsigned char id ;
unsigned char length ;
char phys [ 32 ] ;
} ;
/*
* gf2k_read_packet ( ) reads a Genius Flight2000 packet .
*/
static int gf2k_read_packet ( struct gameport * gameport , int length , char * data )
{
unsigned char u , v ;
int i ;
unsigned int t , p ;
unsigned long flags ;
t = gameport_time ( gameport , GF2K_START ) ;
p = gameport_time ( gameport , GF2K_STROBE ) ;
i = 0 ;
local_irq_save ( flags ) ;
gameport_trigger ( gameport ) ;
v = gameport_read ( gameport ) ;
while ( t > 0 & & i < length ) {
t - - ; u = v ;
v = gameport_read ( gameport ) ;
if ( v & ~ u & 0x10 ) {
data [ i + + ] = v > > 5 ;
t = p ;
}
}
local_irq_restore ( flags ) ;
return i ;
}
/*
* gf2k_trigger_seq ( ) initializes a Genius Flight2000 joystick
* into digital mode .
*/
static void gf2k_trigger_seq ( struct gameport * gameport , short * seq )
{
unsigned long flags ;
int i , t ;
local_irq_save ( flags ) ;
i = 0 ;
do {
gameport_trigger ( gameport ) ;
t = gameport_time ( gameport , GF2K_TIMEOUT * 1000 ) ;
while ( ( gameport_read ( gameport ) & 1 ) & & t ) t - - ;
udelay ( seq [ i ] ) ;
} while ( seq [ + + i ] ) ;
gameport_trigger ( gameport ) ;
local_irq_restore ( flags ) ;
}
/*
* js_sw_get_bits ( ) composes bits from the triplet buffer into a __u64 .
* Parameter ' pos ' is bit number inside packet where to start at , ' num ' is number
* of bits to be read , ' shift ' is offset in the resulting __u64 to start at , bits
* is number of bits per triplet .
*/
# define GB(p,n,s) gf2k_get_bits(data, p, n, s)
static int gf2k_get_bits ( unsigned char * buf , int pos , int num , int shift )
{
__u64 data = 0 ;
int i ;
for ( i = 0 ; i < num / 3 + 2 ; i + + )
data | = buf [ pos / 3 + i ] < < ( i * 3 ) ;
data > > = pos % 3 ;
data & = ( 1 < < num ) - 1 ;
data < < = shift ;
return data ;
}
static void gf2k_read ( struct gf2k * gf2k , unsigned char * data )
{
2005-09-15 11:01:52 +04:00
struct input_dev * dev = gf2k - > dev ;
2005-04-17 02:20:36 +04:00
int i , t ;
for ( i = 0 ; i < 4 & & i < gf2k_axes [ gf2k - > id ] ; i + + )
input_report_abs ( dev , gf2k_abs [ i ] , GB ( i < < 3 , 8 , 0 ) | GB ( i + 46 , 1 , 8 ) | GB ( i + 50 , 1 , 9 ) ) ;
for ( i = 0 ; i < 2 & & i < gf2k_axes [ gf2k - > id ] - 4 ; i + + )
input_report_abs ( dev , gf2k_abs [ i ] , GB ( i * 9 + 60 , 8 , 0 ) | GB ( i + 54 , 1 , 9 ) ) ;
t = GB ( 40 , 4 , 0 ) ;
for ( i = 0 ; i < gf2k_hats [ gf2k - > id ] ; i + + )
input_report_abs ( dev , ABS_HAT0X + i , gf2k_hat_to_axis [ t ] [ i ] ) ;
t = GB ( 44 , 2 , 0 ) | GB ( 32 , 8 , 2 ) | GB ( 78 , 2 , 10 ) ;
for ( i = 0 ; i < gf2k_joys [ gf2k - > id ] ; i + + )
input_report_key ( dev , gf2k_btn_joy [ i ] , ( t > > i ) & 1 ) ;
for ( i = 0 ; i < gf2k_pads [ gf2k - > id ] ; i + + )
input_report_key ( dev , gf2k_btn_pad [ i ] , ( t > > i ) & 1 ) ;
input_sync ( dev ) ;
}
/*
* gf2k_poll ( ) reads and analyzes Genius joystick data .
*/
static void gf2k_poll ( struct gameport * gameport )
{
struct gf2k * gf2k = gameport_get_drvdata ( gameport ) ;
unsigned char data [ GF2K_LENGTH ] ;
gf2k - > reads + + ;
if ( gf2k_read_packet ( gf2k - > gameport , gf2k_length [ gf2k - > id ] , data ) < gf2k_length [ gf2k - > id ] )
gf2k - > bads + + ;
else
gf2k_read ( gf2k , data ) ;
}
static int gf2k_open ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct gf2k * gf2k = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_start_polling ( gf2k - > gameport ) ;
return 0 ;
}
static void gf2k_close ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct gf2k * gf2k = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_stop_polling ( gf2k - > gameport ) ;
}
/*
* gf2k_connect ( ) probes for Genius id joysticks .
*/
static int gf2k_connect ( struct gameport * gameport , struct gameport_driver * drv )
{
struct gf2k * gf2k ;
2005-09-15 11:01:52 +04:00
struct input_dev * input_dev ;
2005-04-17 02:20:36 +04:00
unsigned char data [ GF2K_LENGTH ] ;
int i , err ;
2005-09-15 11:01:52 +04:00
gf2k = kzalloc ( sizeof ( struct gf2k ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! gf2k | | ! input_dev ) {
err = - ENOMEM ;
goto fail1 ;
}
2005-04-17 02:20:36 +04:00
gf2k - > gameport = gameport ;
2005-09-15 11:01:52 +04:00
gf2k - > dev = input_dev ;
2005-04-17 02:20:36 +04:00
gameport_set_drvdata ( gameport , gf2k ) ;
err = gameport_open ( gameport , drv , GAMEPORT_MODE_RAW ) ;
if ( err )
goto fail1 ;
gf2k_trigger_seq ( gameport , gf2k_seq_reset ) ;
msleep ( GF2K_TIMEOUT ) ;
gf2k_trigger_seq ( gameport , gf2k_seq_digital ) ;
msleep ( GF2K_TIMEOUT ) ;
if ( gf2k_read_packet ( gameport , GF2K_LENGTH , data ) < 12 ) {
err = - ENODEV ;
goto fail2 ;
}
if ( ! ( gf2k - > id = GB ( 7 , 2 , 0 ) | GB ( 3 , 3 , 2 ) | GB ( 0 , 3 , 5 ) ) ) {
err = - ENODEV ;
goto fail2 ;
}
# ifdef RESET_WORKS
2010-01-02 05:35:11 +03:00
if ( ( gf2k - > id ! = ( GB ( 19 , 2 , 0 ) | GB ( 15 , 3 , 2 ) | GB ( 12 , 3 , 5 ) ) ) & &
2005-04-17 02:20:36 +04:00
( gf2k - > id ! = ( GB ( 31 , 2 , 0 ) | GB ( 27 , 3 , 2 ) | GB ( 24 , 3 , 5 ) ) ) ) {
err = - ENODEV ;
goto fail2 ;
}
# else
gf2k - > id = 6 ;
# endif
if ( gf2k - > id > GF2K_ID_MAX | | ! gf2k_axes [ gf2k - > id ] ) {
printk ( KERN_WARNING " gf2k.c: Not yet supported joystick on %s. [id: %d type:%s] \n " ,
gameport - > phys , gf2k - > id , gf2k - > id > GF2K_ID_MAX ? " Unknown " : gf2k_names [ gf2k - > id ] ) ;
err = - ENODEV ;
goto fail2 ;
}
gameport_set_poll_handler ( gameport , gf2k_poll ) ;
gameport_set_poll_interval ( gameport , 20 ) ;
2006-06-26 09:45:48 +04:00
snprintf ( gf2k - > phys , sizeof ( gf2k - > phys ) , " %s/input0 " , gameport - > phys ) ;
2005-04-17 02:20:36 +04:00
gf2k - > length = gf2k_lens [ gf2k - > id ] ;
2005-09-15 11:01:52 +04:00
input_dev - > name = gf2k_names [ gf2k - > id ] ;
input_dev - > phys = gf2k - > phys ;
input_dev - > id . bustype = BUS_GAMEPORT ;
input_dev - > id . vendor = GAMEPORT_ID_VENDOR_GENIUS ;
input_dev - > id . product = gf2k - > id ;
input_dev - > id . version = 0x0100 ;
2007-04-12 09:35:26 +04:00
input_dev - > dev . parent = & gameport - > dev ;
2007-04-12 09:34:14 +04:00
input_set_drvdata ( input_dev , gf2k ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
input_dev - > open = gf2k_open ;
input_dev - > close = gf2k_close ;
2007-04-12 09:34:14 +04:00
2007-10-19 10:40:32 +04:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_ABS ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < gf2k_axes [ gf2k - > id ] ; i + + )
2005-09-15 11:01:52 +04:00
set_bit ( gf2k_abs [ i ] , input_dev - > absbit ) ;
2005-04-17 02:20:36 +04:00
2010-08-03 07:15:17 +04:00
for ( i = 0 ; i < gf2k_hats [ gf2k - > id ] ; i + + )
input_set_abs_params ( input_dev , ABS_HAT0X + i , - 1 , 1 , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < gf2k_joys [ gf2k - > id ] ; i + + )
2005-09-15 11:01:52 +04:00
set_bit ( gf2k_btn_joy [ i ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < gf2k_pads [ gf2k - > id ] ; i + + )
2005-09-15 11:01:52 +04:00
set_bit ( gf2k_btn_pad [ i ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
gf2k_read_packet ( gameport , gf2k - > length , data ) ;
gf2k_read ( gf2k , data ) ;
for ( i = 0 ; i < gf2k_axes [ gf2k - > id ] ; i + + ) {
2010-08-03 07:15:17 +04:00
int max = i < 2 ?
input_abs_get_val ( input_dev , gf2k_abs [ i ] ) * 2 :
input_abs_get_val ( input_dev , gf2k_abs [ 0 ] ) +
input_abs_get_val ( input_dev , gf2k_abs [ 1 ] ) ;
int flat = i < 2 ? 24 : 0 ;
input_set_abs_params ( input_dev , gf2k_abs [ i ] ,
32 , max - 32 , 8 , flat ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-06 06:40:09 +03:00
err = input_register_device ( gf2k - > dev ) ;
if ( err )
goto fail2 ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-15 11:01:52 +04:00
fail2 : gameport_close ( gameport ) ;
fail1 : gameport_set_drvdata ( gameport , NULL ) ;
input_free_device ( input_dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( gf2k ) ;
return err ;
}
static void gf2k_disconnect ( struct gameport * gameport )
{
struct gf2k * gf2k = gameport_get_drvdata ( gameport ) ;
2005-09-15 11:01:52 +04:00
input_unregister_device ( gf2k - > dev ) ;
2005-04-17 02:20:36 +04:00
gameport_close ( gameport ) ;
gameport_set_drvdata ( gameport , NULL ) ;
kfree ( gf2k ) ;
}
static struct gameport_driver gf2k_drv = {
. driver = {
. name = " gf2k " ,
} ,
. description = DRIVER_DESC ,
. connect = gf2k_connect ,
. disconnect = gf2k_disconnect ,
} ;
2012-04-04 10:52:27 +04:00
module_gameport_driver ( gf2k_drv ) ;