2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) 1996 - 2001 Vojtech Pavlik
*/
/*
* Analog joystick and gamepad 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/module.h>
# include <linux/slab.h>
# include <linux/bitops.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>
2009-06-17 02:31:12 +04:00
# include <linux/timex.h>
2005-04-17 02:20:36 +04:00
# define DRIVER_DESC "Analog joystick and gamepad driver"
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Option parsing .
*/
# define ANALOG_PORTS 16
static char * js [ ANALOG_PORTS ] ;
2007-05-03 08:52:51 +04:00
static unsigned int js_nargs ;
2005-04-17 02:20:36 +04:00
static int analog_options [ ANALOG_PORTS ] ;
module_param_array_named ( map , js , charp , & js_nargs , 0 ) ;
MODULE_PARM_DESC ( map , " Describes analog joysticks type/capabilities " ) ;
/*
* Times , feature definitions .
*/
# define ANALOG_RUDDER 0x00004
# define ANALOG_THROTTLE 0x00008
# define ANALOG_AXES_STD 0x0000f
# define ANALOG_BTNS_STD 0x000f0
# define ANALOG_BTNS_CHF 0x00100
# define ANALOG_HAT1_CHF 0x00200
# define ANALOG_HAT2_CHF 0x00400
# define ANALOG_HAT_FCS 0x00800
# define ANALOG_HATS_ALL 0x00e00
# define ANALOG_BTN_TL 0x01000
# define ANALOG_BTN_TR 0x02000
# define ANALOG_BTN_TL2 0x04000
# define ANALOG_BTN_TR2 0x08000
# define ANALOG_BTNS_TLR 0x03000
# define ANALOG_BTNS_TLR2 0x0c000
# define ANALOG_BTNS_GAMEPAD 0x0f000
# define ANALOG_HBTN_CHF 0x10000
# define ANALOG_ANY_CHF 0x10700
# define ANALOG_SAITEK 0x20000
# define ANALOG_EXTENSIONS 0x7ff00
# define ANALOG_GAMEPAD 0x80000
# define ANALOG_MAX_TIME 3 /* 3 ms */
# define ANALOG_LOOP_TIME 2000 /* 2 * loop */
# define ANALOG_SAITEK_DELAY 200 /* 200 us */
# define ANALOG_SAITEK_TIME 2000 /* 2000 us */
# define ANALOG_AXIS_TIME 2 /* 2 * refresh */
# define ANALOG_INIT_RETRIES 8 /* 8 times */
# define ANALOG_FUZZ_BITS 2 /* 2 bit more */
# define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */
# define ANALOG_MAX_NAME_LENGTH 128
# define ANALOG_MAX_PHYS_LENGTH 32
static short analog_axes [ ] = { ABS_X , ABS_Y , ABS_RUDDER , ABS_THROTTLE } ;
static short analog_hats [ ] = { ABS_HAT0X , ABS_HAT0Y , ABS_HAT1X , ABS_HAT1Y , ABS_HAT2X , ABS_HAT2Y } ;
static short analog_pads [ ] = { BTN_Y , BTN_Z , BTN_TL , BTN_TR } ;
static short analog_exts [ ] = { ANALOG_HAT1_CHF , ANALOG_HAT2_CHF , ANALOG_HAT_FCS } ;
static short analog_pad_btn [ ] = { BTN_A , BTN_B , BTN_C , BTN_X , BTN_TL2 , BTN_TR2 , BTN_SELECT , BTN_START , BTN_MODE , BTN_BASE } ;
static short analog_joy_btn [ ] = { BTN_TRIGGER , BTN_THUMB , BTN_TOP , BTN_TOP2 , BTN_BASE , BTN_BASE2 ,
BTN_BASE3 , BTN_BASE4 , BTN_BASE5 , BTN_BASE6 } ;
static unsigned char analog_chf [ ] = { 0xf , 0x0 , 0x1 , 0x9 , 0x2 , 0x4 , 0xc , 0x8 , 0x3 , 0x5 , 0xb , 0x7 , 0xd , 0xe , 0xa , 0x6 } ;
struct analog {
2005-09-15 11:01:52 +04:00
struct input_dev * dev ;
2005-04-17 02:20:36 +04:00
int mask ;
short * buttons ;
char name [ ANALOG_MAX_NAME_LENGTH ] ;
char phys [ ANALOG_MAX_PHYS_LENGTH ] ;
} ;
struct analog_port {
struct gameport * gameport ;
struct analog analog [ 2 ] ;
unsigned char mask ;
char saitek ;
char cooked ;
int bads ;
int reads ;
int speed ;
int loop ;
int fuzz ;
int axes [ 4 ] ;
int buttons ;
int initial [ 4 ] ;
int axtime ;
} ;
/*
* Time macros .
*/
# ifdef __i386__
2005-06-30 13:58:55 +04:00
2011-06-01 22:04:57 +04:00
# include <linux/i8253.h>
2005-06-30 13:58:55 +04:00
2005-04-17 02:20:36 +04:00
# define GET_TIME(x) do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
2011-08-05 10:39:58 +04:00
# define DELTA(x,y) (cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0)))
2005-04-17 02:20:36 +04:00
# define TIME_NAME (cpu_has_tsc?"TSC":"PIT")
static unsigned int get_time_pit ( void )
{
unsigned long flags ;
unsigned int count ;
2010-02-17 19:47:10 +03:00
raw_spin_lock_irqsave ( & i8253_lock , flags ) ;
2005-04-17 02:20:36 +04:00
outb_p ( 0x00 , 0x43 ) ;
count = inb_p ( 0x40 ) ;
count | = inb_p ( 0x40 ) < < 8 ;
2010-02-17 19:47:10 +03:00
raw_spin_unlock_irqrestore ( & i8253_lock , flags ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
# elif defined(__x86_64__)
# define GET_TIME(x) rdtscl(x)
# define DELTA(x,y) ((y)-(x))
# define TIME_NAME "TSC"
2013-02-13 20:11:09 +04:00
# elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_TILE)
2005-04-17 02:20:36 +04:00
# define GET_TIME(x) do { x = get_cycles(); } while (0)
# define DELTA(x,y) ((y)-(x))
2013-02-13 20:11:09 +04:00
# define TIME_NAME "get_cycles"
2005-04-17 02:20:36 +04:00
# else
# define FAKE_TIME
static unsigned long analog_faketime = 0 ;
# define GET_TIME(x) do { x = analog_faketime++; } while(0)
# define DELTA(x,y) ((y)-(x))
# define TIME_NAME "Unreliable"
# warning Precise timer not defined for this architecture.
# endif
/*
* analog_decode ( ) decodes analog joystick data and reports input events .
*/
static void analog_decode ( struct analog * analog , int * axes , int * initial , int buttons )
{
2005-09-15 11:01:52 +04:00
struct input_dev * dev = analog - > dev ;
2005-04-17 02:20:36 +04:00
int i , j ;
if ( analog - > mask & ANALOG_HAT_FCS )
for ( i = 0 ; i < 4 ; i + + )
if ( axes [ 3 ] < ( ( initial [ 3 ] * ( ( i < < 1 ) + 1 ) ) > > 3 ) ) {
buttons | = 1 < < ( i + 14 ) ;
break ;
}
for ( i = j = 0 ; i < 6 ; i + + )
if ( analog - > mask & ( 0x10 < < i ) )
input_report_key ( dev , analog - > buttons [ j + + ] , ( buttons > > i ) & 1 ) ;
if ( analog - > mask & ANALOG_HBTN_CHF )
for ( i = 0 ; i < 4 ; i + + )
input_report_key ( dev , analog - > buttons [ j + + ] , ( buttons > > ( i + 10 ) ) & 1 ) ;
if ( analog - > mask & ANALOG_BTN_TL )
input_report_key ( dev , analog_pads [ 0 ] , axes [ 2 ] < ( initial [ 2 ] > > 1 ) ) ;
if ( analog - > mask & ANALOG_BTN_TR )
input_report_key ( dev , analog_pads [ 1 ] , axes [ 3 ] < ( initial [ 3 ] > > 1 ) ) ;
if ( analog - > mask & ANALOG_BTN_TL2 )
input_report_key ( dev , analog_pads [ 2 ] , axes [ 2 ] > ( initial [ 2 ] + ( initial [ 2 ] > > 1 ) ) ) ;
if ( analog - > mask & ANALOG_BTN_TR2 )
input_report_key ( dev , analog_pads [ 3 ] , axes [ 3 ] > ( initial [ 3 ] + ( initial [ 3 ] > > 1 ) ) ) ;
for ( i = j = 0 ; i < 4 ; i + + )
if ( analog - > mask & ( 1 < < i ) )
input_report_abs ( dev , analog_axes [ j + + ] , axes [ i ] ) ;
for ( i = j = 0 ; i < 3 ; i + + )
if ( analog - > mask & analog_exts [ i ] ) {
input_report_abs ( dev , analog_hats [ j + + ] ,
( ( buttons > > ( ( i < < 2 ) + 7 ) ) & 1 ) - ( ( buttons > > ( ( i < < 2 ) + 9 ) ) & 1 ) ) ;
input_report_abs ( dev , analog_hats [ j + + ] ,
( ( buttons > > ( ( i < < 2 ) + 8 ) ) & 1 ) - ( ( buttons > > ( ( i < < 2 ) + 6 ) ) & 1 ) ) ;
}
input_sync ( dev ) ;
}
/*
* analog_cooked_read ( ) reads analog joystick data .
*/
static int analog_cooked_read ( struct analog_port * port )
{
struct gameport * gameport = port - > gameport ;
unsigned int time [ 4 ] , start , loop , now , loopout , timeout ;
unsigned char data [ 4 ] , this , last ;
unsigned long flags ;
int i , j ;
loopout = ( ANALOG_LOOP_TIME * port - > loop ) / 1000 ;
timeout = ANALOG_MAX_TIME * port - > speed ;
local_irq_save ( flags ) ;
gameport_trigger ( gameport ) ;
GET_TIME ( now ) ;
local_irq_restore ( flags ) ;
start = now ;
this = port - > mask ;
i = 0 ;
do {
loop = now ;
last = this ;
local_irq_disable ( ) ;
this = gameport_read ( gameport ) & port - > mask ;
GET_TIME ( now ) ;
local_irq_restore ( flags ) ;
if ( ( last ^ this ) & & ( DELTA ( loop , now ) < loopout ) ) {
data [ i ] = last ^ this ;
time [ i ] = now ;
i + + ;
}
} while ( this & & ( i < 4 ) & & ( DELTA ( start , now ) < timeout ) ) ;
this < < = 4 ;
for ( - - i ; i > = 0 ; i - - ) {
this | = data [ i ] ;
for ( j = 0 ; j < 4 ; j + + )
if ( data [ i ] & ( 1 < < j ) )
port - > axes [ j ] = ( DELTA ( start , time [ i ] ) < < ANALOG_FUZZ_BITS ) / port - > loop ;
}
return - ( this ! = port - > mask ) ;
}
static int analog_button_read ( struct analog_port * port , char saitek , char chf )
{
unsigned char u ;
int t = 1 , i = 0 ;
int strobe = gameport_time ( port - > gameport , ANALOG_SAITEK_TIME ) ;
u = gameport_read ( port - > gameport ) ;
if ( ! chf ) {
port - > buttons = ( ~ u > > 4 ) & 0xf ;
return 0 ;
}
port - > buttons = 0 ;
while ( ( ~ u & 0xf0 ) & & ( i < 16 ) & & t ) {
port - > buttons | = 1 < < analog_chf [ ( ~ u > > 4 ) & 0xf ] ;
if ( ! saitek ) return 0 ;
udelay ( ANALOG_SAITEK_DELAY ) ;
t = strobe ;
gameport_trigger ( port - > gameport ) ;
while ( ( ( u = gameport_read ( port - > gameport ) ) & port - > mask ) & & t ) t - - ;
i + + ;
}
return - ( ! t | | ( i = = 16 ) ) ;
}
/*
* analog_poll ( ) repeatedly polls the Analog joysticks .
*/
static void analog_poll ( struct gameport * gameport )
{
struct analog_port * port = gameport_get_drvdata ( gameport ) ;
int i ;
char saitek = ! ! ( port - > analog [ 0 ] . mask & ANALOG_SAITEK ) ;
char chf = ! ! ( port - > analog [ 0 ] . mask & ANALOG_ANY_CHF ) ;
if ( port - > cooked ) {
port - > bads - = gameport_cooked_read ( port - > gameport , port - > axes , & port - > buttons ) ;
if ( chf )
port - > buttons = port - > buttons ? ( 1 < < analog_chf [ port - > buttons ] ) : 0 ;
port - > reads + + ;
} else {
if ( ! port - > axtime - - ) {
port - > bads - = analog_cooked_read ( port ) ;
port - > bads - = analog_button_read ( port , saitek , chf ) ;
port - > reads + + ;
port - > axtime = ANALOG_AXIS_TIME - 1 ;
} else {
if ( ! saitek )
analog_button_read ( port , saitek , chf ) ;
}
}
for ( i = 0 ; i < 2 ; i + + )
if ( port - > analog [ i ] . mask )
analog_decode ( port - > analog + i , port - > axes , port - > initial , port - > buttons ) ;
}
/*
* analog_open ( ) is a callback from the input open routine .
*/
static int analog_open ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct analog_port * port = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_start_polling ( port - > gameport ) ;
return 0 ;
}
/*
* analog_close ( ) is a callback from the input close routine .
*/
static void analog_close ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct analog_port * port = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_stop_polling ( port - > gameport ) ;
}
/*
* analog_calibrate_timer ( ) calibrates the timer and computes loop
* and timeout values for a joystick port .
*/
static void analog_calibrate_timer ( struct analog_port * port )
{
struct gameport * gameport = port - > gameport ;
unsigned int i , t , tx , t1 , t2 , t3 ;
unsigned long flags ;
local_irq_save ( flags ) ;
GET_TIME ( t1 ) ;
# ifdef FAKE_TIME
analog_faketime + = 830 ;
# endif
mdelay ( 1 ) ;
GET_TIME ( t2 ) ;
GET_TIME ( t3 ) ;
local_irq_restore ( flags ) ;
port - > speed = DELTA ( t1 , t2 ) - DELTA ( t2 , t3 ) ;
tx = ~ 0 ;
for ( i = 0 ; i < 50 ; i + + ) {
local_irq_save ( flags ) ;
GET_TIME ( t1 ) ;
for ( t = 0 ; t < 50 ; t + + ) { gameport_read ( gameport ) ; GET_TIME ( t2 ) ; }
GET_TIME ( t3 ) ;
local_irq_restore ( flags ) ;
udelay ( i ) ;
t = DELTA ( t1 , t2 ) - DELTA ( t2 , t3 ) ;
if ( t < tx ) tx = t ;
}
port - > loop = tx / 50 ;
}
/*
* analog_name ( ) constructs a name for an analog joystick .
*/
static void analog_name ( struct analog * analog )
{
2006-06-26 09:45:48 +04:00
snprintf ( analog - > name , sizeof ( analog - > name ) , " Analog %d-axis %d-button " ,
hweight8 ( analog - > mask & ANALOG_AXES_STD ) ,
hweight8 ( analog - > mask & ANALOG_BTNS_STD ) + ! ! ( analog - > mask & ANALOG_BTNS_CHF ) * 2 +
hweight16 ( analog - > mask & ANALOG_BTNS_GAMEPAD ) + ! ! ( analog - > mask & ANALOG_HBTN_CHF ) * 4 ) ;
2005-04-17 02:20:36 +04:00
if ( analog - > mask & ANALOG_HATS_ALL )
2006-06-26 09:45:48 +04:00
snprintf ( analog - > name , sizeof ( analog - > name ) , " %s %d-hat " ,
analog - > name , hweight16 ( analog - > mask & ANALOG_HATS_ALL ) ) ;
2005-04-17 02:20:36 +04:00
if ( analog - > mask & ANALOG_HAT_FCS )
2006-06-26 09:45:48 +04:00
strlcat ( analog - > name , " FCS " , sizeof ( analog - > name ) ) ;
2005-04-17 02:20:36 +04:00
if ( analog - > mask & ANALOG_ANY_CHF )
2006-06-26 09:45:48 +04:00
strlcat ( analog - > name , ( analog - > mask & ANALOG_SAITEK ) ? " Saitek " : " CHF " ,
sizeof ( analog - > name ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 09:45:48 +04:00
strlcat ( analog - > name , ( analog - > mask & ANALOG_GAMEPAD ) ? " gamepad " : " joystick " ,
sizeof ( analog - > name ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* analog_init_device ( )
*/
2005-09-15 11:01:52 +04:00
static int analog_init_device ( struct analog_port * port , struct analog * analog , int index )
2005-04-17 02:20:36 +04:00
{
2005-09-15 11:01:52 +04:00
struct input_dev * input_dev ;
2005-04-17 02:20:36 +04:00
int i , j , t , v , w , x , y , z ;
2006-11-06 06:40:09 +03:00
int error ;
2005-04-17 02:20:36 +04:00
analog_name ( analog ) ;
2006-06-26 09:45:48 +04:00
snprintf ( analog - > phys , sizeof ( analog - > phys ) ,
" %s/input%d " , port - > gameport - > phys , index ) ;
2005-04-17 02:20:36 +04:00
analog - > buttons = ( analog - > mask & ANALOG_GAMEPAD ) ? analog_pad_btn : analog_joy_btn ;
2005-09-15 11:01:52 +04:00
analog - > dev = input_dev = input_allocate_device ( ) ;
if ( ! input_dev )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
input_dev - > name = analog - > name ;
input_dev - > phys = analog - > phys ;
input_dev - > id . bustype = BUS_GAMEPORT ;
input_dev - > id . vendor = GAMEPORT_ID_VENDOR_ANALOG ;
input_dev - > id . product = analog - > mask > > 4 ;
input_dev - > id . version = 0x0100 ;
2007-04-12 09:35:26 +04:00
input_dev - > dev . parent = & port - > gameport - > dev ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:34:14 +04:00
input_set_drvdata ( input_dev , port ) ;
2005-09-15 11:01:52 +04:00
input_dev - > open = analog_open ;
input_dev - > close = analog_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 = j = 0 ; i < 4 ; i + + )
if ( analog - > mask & ( 1 < < i ) ) {
t = analog_axes [ j ] ;
x = port - > axes [ i ] ;
y = ( port - > axes [ 0 ] + port - > axes [ 1 ] ) > > 1 ;
z = y - port - > axes [ i ] ;
z = z > 0 ? z : - z ;
v = ( x > > 3 ) ;
w = ( x > > 3 ) ;
if ( ( i = = 2 | | i = = 3 ) & & ( j = = 2 | | j = = 3 ) & & ( z > ( y > > 3 ) ) )
x = y ;
if ( analog - > mask & ANALOG_SAITEK ) {
if ( i = = 2 ) x = port - > axes [ i ] ;
v = x - ( x > > 2 ) ;
w = ( x > > 4 ) ;
}
2005-09-15 11:01:52 +04:00
input_set_abs_params ( input_dev , t , v , ( x < < 1 ) - v , port - > fuzz , w ) ;
2005-04-17 02:20:36 +04:00
j + + ;
}
for ( i = j = 0 ; i < 3 ; i + + )
if ( analog - > mask & analog_exts [ i ] )
for ( x = 0 ; x < 2 ; x + + ) {
t = analog_hats [ j + + ] ;
2005-09-15 11:01:52 +04:00
input_set_abs_params ( input_dev , t , - 1 , 1 , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
}
for ( i = j = 0 ; i < 4 ; i + + )
if ( analog - > mask & ( 0x10 < < i ) )
2005-09-15 11:01:52 +04:00
set_bit ( analog - > buttons [ j + + ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
if ( analog - > mask & ANALOG_BTNS_CHF )
for ( i = 0 ; i < 2 ; i + + )
2005-09-15 11:01:52 +04:00
set_bit ( analog - > buttons [ j + + ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
if ( analog - > mask & ANALOG_HBTN_CHF )
for ( i = 0 ; i < 4 ; i + + )
2005-09-15 11:01:52 +04:00
set_bit ( analog - > buttons [ j + + ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 4 ; i + + )
if ( analog - > mask & ( ANALOG_BTN_TL < < i ) )
2005-09-15 11:01:52 +04:00
set_bit ( analog_pads [ i ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
analog_decode ( analog , port - > axes , port - > initial , port - > buttons ) ;
2006-11-06 06:40:09 +03:00
error = input_register_device ( analog - > dev ) ;
if ( error ) {
input_free_device ( analog - > dev ) ;
return error ;
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* analog_init_devices ( ) sets up device - specific values and registers the input devices .
*/
static int analog_init_masks ( struct analog_port * port )
{
int i ;
struct analog * analog = port - > analog ;
int max [ 4 ] ;
if ( ! port - > mask )
return - 1 ;
if ( ( port - > mask & 3 ) ! = 3 & & port - > mask ! = 0xc ) {
printk ( KERN_WARNING " analog.c: Unknown joystick device found "
" (data=%#x, %s), probably not analog joystick. \n " ,
port - > mask , port - > gameport - > phys ) ;
return - 1 ;
}
i = analog_options [ 0 ] ; /* FIXME !!! - need to specify options for different ports */
analog [ 0 ] . mask = i & 0xfffff ;
analog [ 0 ] . mask & = ~ ( ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD )
| port - > mask | ( ( port - > mask < < 8 ) & ANALOG_HAT_FCS )
| ( ( port - > mask < < 10 ) & ANALOG_BTNS_TLR ) | ( ( port - > mask < < 12 ) & ANALOG_BTNS_TLR2 ) ;
analog [ 0 ] . mask & = ~ ( ANALOG_HAT2_CHF )
| ( ( analog [ 0 ] . mask & ANALOG_HBTN_CHF ) ? 0 : ANALOG_HAT2_CHF ) ;
analog [ 0 ] . mask & = ~ ( ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2 )
| ( ( ~ analog [ 0 ] . mask & ANALOG_HAT_FCS ) > > 8 )
| ( ( ~ analog [ 0 ] . mask & ANALOG_HAT_FCS ) < < 2 )
| ( ( ~ analog [ 0 ] . mask & ANALOG_HAT_FCS ) < < 4 ) ;
analog [ 0 ] . mask & = ~ ( ANALOG_THROTTLE | ANALOG_RUDDER )
| ( ( ( ~ analog [ 0 ] . mask & ANALOG_BTNS_TLR ) > > 10 )
& ( ( ~ analog [ 0 ] . mask & ANALOG_BTNS_TLR2 ) > > 12 ) ) ;
analog [ 1 ] . mask = ( ( i > > 20 ) & 0xff ) | ( ( i > > 12 ) & 0xf0000 ) ;
analog [ 1 ] . mask & = ( analog [ 0 ] . mask & ANALOG_EXTENSIONS ) ? ANALOG_GAMEPAD
: ( ( ( ANALOG_BTNS_STD | port - > mask ) & ~ analog [ 0 ] . mask ) | ANALOG_GAMEPAD ) ;
if ( port - > cooked ) {
for ( i = 0 ; i < 4 ; i + + ) max [ i ] = port - > axes [ i ] < < 1 ;
if ( ( analog [ 0 ] . mask & 0x7 ) = = 0x7 ) max [ 2 ] = ( max [ 0 ] + max [ 1 ] ) > > 1 ;
if ( ( analog [ 0 ] . mask & 0xb ) = = 0xb ) max [ 3 ] = ( max [ 0 ] + max [ 1 ] ) > > 1 ;
if ( ( analog [ 0 ] . mask & ANALOG_BTN_TL ) & & ! ( analog [ 0 ] . mask & ANALOG_BTN_TL2 ) ) max [ 2 ] > > = 1 ;
if ( ( analog [ 0 ] . mask & ANALOG_BTN_TR ) & & ! ( analog [ 0 ] . mask & ANALOG_BTN_TR2 ) ) max [ 3 ] > > = 1 ;
if ( ( analog [ 0 ] . mask & ANALOG_HAT_FCS ) ) max [ 3 ] > > = 1 ;
gameport_calibrate ( port - > gameport , port - > axes , max ) ;
}
for ( i = 0 ; i < 4 ; i + + )
port - > initial [ i ] = port - > axes [ i ] ;
return - ! ( analog [ 0 ] . mask | | analog [ 1 ] . mask ) ;
}
static int analog_init_port ( struct gameport * gameport , struct gameport_driver * drv , struct analog_port * port )
{
int i , t , u , v ;
port - > gameport = gameport ;
gameport_set_drvdata ( gameport , port ) ;
if ( ! gameport_open ( gameport , drv , GAMEPORT_MODE_RAW ) ) {
analog_calibrate_timer ( port ) ;
gameport_trigger ( gameport ) ;
t = gameport_read ( gameport ) ;
msleep ( ANALOG_MAX_TIME ) ;
port - > mask = ( gameport_read ( gameport ) ^ t ) & t & 0xf ;
port - > fuzz = ( port - > speed * ANALOG_FUZZ_MAGIC ) / port - > loop / 1000 + ANALOG_FUZZ_BITS ;
for ( i = 0 ; i < ANALOG_INIT_RETRIES ; i + + ) {
if ( ! analog_cooked_read ( port ) )
break ;
msleep ( ANALOG_MAX_TIME ) ;
}
u = v = 0 ;
msleep ( ANALOG_MAX_TIME ) ;
t = gameport_time ( gameport , ANALOG_MAX_TIME * 1000 ) ;
gameport_trigger ( gameport ) ;
while ( ( gameport_read ( port - > gameport ) & port - > mask ) & & ( u < t ) )
u + + ;
udelay ( ANALOG_SAITEK_DELAY ) ;
t = gameport_time ( gameport , ANALOG_SAITEK_TIME ) ;
gameport_trigger ( gameport ) ;
while ( ( gameport_read ( port - > gameport ) & port - > mask ) & & ( v < t ) )
v + + ;
if ( v < ( u > > 1 ) ) { /* FIXME - more than one port */
analog_options [ 0 ] | = /* FIXME - more than one port */
ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF ;
return 0 ;
}
gameport_close ( gameport ) ;
}
if ( ! gameport_open ( gameport , drv , GAMEPORT_MODE_COOKED ) ) {
for ( i = 0 ; i < ANALOG_INIT_RETRIES ; i + + )
if ( ! gameport_cooked_read ( gameport , port - > axes , & port - > buttons ) )
break ;
for ( i = 0 ; i < 4 ; i + + )
if ( port - > axes [ i ] ! = - 1 )
port - > mask | = 1 < < i ;
port - > fuzz = gameport - > fuzz ;
port - > cooked = 1 ;
return 0 ;
}
return gameport_open ( gameport , drv , GAMEPORT_MODE_RAW ) ;
}
static int analog_connect ( struct gameport * gameport , struct gameport_driver * drv )
{
struct analog_port * port ;
int i ;
int err ;
2005-09-07 02:18:33 +04:00
if ( ! ( port = kzalloc ( sizeof ( struct analog_port ) , GFP_KERNEL ) ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
err = analog_init_port ( gameport , drv , port ) ;
2005-09-15 11:01:52 +04:00
if ( err )
goto fail1 ;
2005-04-17 02:20:36 +04:00
err = analog_init_masks ( port ) ;
2005-09-15 11:01:52 +04:00
if ( err )
goto fail2 ;
2005-04-17 02:20:36 +04:00
gameport_set_poll_handler ( gameport , analog_poll ) ;
gameport_set_poll_interval ( gameport , 10 ) ;
for ( i = 0 ; i < 2 ; i + + )
2005-09-15 11:01:52 +04:00
if ( port - > analog [ i ] . mask ) {
err = analog_init_device ( port , port - > analog + i , i ) ;
if ( err )
goto fail3 ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-15 11:01:52 +04:00
fail3 : while ( - - i > = 0 )
2006-11-06 06:40:09 +03:00
if ( port - > analog [ i ] . mask )
input_unregister_device ( port - > analog [ i ] . dev ) ;
2005-09-15 11:01:52 +04:00
fail2 : gameport_close ( gameport ) ;
fail1 : gameport_set_drvdata ( gameport , NULL ) ;
kfree ( port ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
static void analog_disconnect ( struct gameport * gameport )
{
struct analog_port * port = gameport_get_drvdata ( gameport ) ;
2005-09-15 11:01:52 +04:00
int i ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 2 ; i + + )
if ( port - > analog [ i ] . mask )
2005-09-15 11:01:52 +04:00
input_unregister_device ( port - > analog [ i ] . dev ) ;
2005-04-17 02:20:36 +04:00
gameport_close ( gameport ) ;
gameport_set_drvdata ( gameport , NULL ) ;
printk ( KERN_INFO " analog.c: %d out of %d reads (%d%%) on %s failed \n " ,
port - > bads , port - > reads , port - > reads ? ( port - > bads * 100 / port - > reads ) : 0 ,
port - > gameport - > phys ) ;
kfree ( port ) ;
}
struct analog_types {
char * name ;
int value ;
} ;
static struct analog_types analog_types [ ] = {
{ " none " , 0x00000000 } ,
{ " auto " , 0x000000ff } ,
{ " 2btn " , 0x0000003f } ,
{ " y-joy " , 0x0cc00033 } ,
{ " y-pad " , 0x8cc80033 } ,
{ " fcs " , 0x000008f7 } ,
{ " chf " , 0x000002ff } ,
{ " fullchf " , 0x000007ff } ,
{ " gamepad " , 0x000830f3 } ,
{ " gamepad8 " , 0x0008f0f3 } ,
{ NULL , 0 }
} ;
static void analog_parse_options ( void )
{
int i , j ;
char * end ;
for ( i = 0 ; i < js_nargs ; i + + ) {
for ( j = 0 ; analog_types [ j ] . name ; j + + )
if ( ! strcmp ( analog_types [ j ] . name , js [ i ] ) ) {
analog_options [ i ] = analog_types [ j ] . value ;
break ;
}
if ( analog_types [ j ] . name ) continue ;
analog_options [ i ] = simple_strtoul ( js [ i ] , & end , 0 ) ;
if ( end ! = js [ i ] ) continue ;
analog_options [ i ] = 0xff ;
if ( ! strlen ( js [ i ] ) ) continue ;
printk ( KERN_WARNING " analog.c: Bad config for port %d - \" %s \" \n " , i , js [ i ] ) ;
}
for ( ; i < ANALOG_PORTS ; i + + )
analog_options [ i ] = 0xff ;
}
/*
* The gameport device structure .
*/
static struct gameport_driver analog_drv = {
. driver = {
. name = " analog " ,
} ,
. description = DRIVER_DESC ,
. connect = analog_connect ,
. disconnect = analog_disconnect ,
} ;
static int __init analog_init ( void )
{
analog_parse_options ( ) ;
2008-06-06 09:33:37 +04:00
return gameport_register_driver ( & analog_drv ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit analog_exit ( void )
{
gameport_unregister_driver ( & analog_drv ) ;
}
module_init ( analog_init ) ;
module_exit ( analog_exit ) ;