2005-04-16 15:20:36 -07:00
/*
* Copyright ( c ) 1998 - 2005 Vojtech Pavlik
*/
/*
* Microsoft SideWinder joystick family 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/init.h>
# include <linux/input.h>
# include <linux/gameport.h>
2005-10-30 15:03:48 -08:00
# include <linux/jiffies.h>
2005-04-16 15:20:36 -07:00
# define DRIVER_DESC "Microsoft SideWinder joystick family driver"
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* These are really magic values . Changing them can make a problem go away ,
* as well as break everything .
*/
# undef SW_DEBUG
# undef SW_DEBUG_DATA
# define SW_START 600 /* The time we wait for the first bit [600 us] */
# define SW_STROBE 60 /* Max time per bit [60 us] */
# define SW_TIMEOUT 6 /* Wait for everything to settle [6 ms] */
# define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */
# define SW_END 8 /* Number of bits before end of packet to kick */
# define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */
# define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */
# define SW_OK 64 /* Number of packet read successes to switch optimization back on */
# define SW_LENGTH 512 /* Max number of bits in a packet */
# ifdef SW_DEBUG
# define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
# else
# define dbg(format, arg...) do {} while (0)
# endif
/*
* SideWinder joystick types . . .
*/
# define SW_ID_3DP 0
# define SW_ID_GP 1
# define SW_ID_PP 2
# define SW_ID_FFP 3
# define SW_ID_FSP 4
# define SW_ID_FFW 5
/*
* Names , buttons , axes . . .
*/
static char * sw_name [ ] = { " 3D Pro " , " GamePad " , " Precision Pro " , " Force Feedback Pro " , " FreeStyle Pro " ,
" Force Feedback Wheel " } ;
static char sw_abs [ ] [ 7 ] = {
{ ABS_X , ABS_Y , ABS_RZ , ABS_THROTTLE , ABS_HAT0X , ABS_HAT0Y } ,
{ ABS_X , ABS_Y } ,
{ ABS_X , ABS_Y , ABS_RZ , ABS_THROTTLE , ABS_HAT0X , ABS_HAT0Y } ,
{ ABS_X , ABS_Y , ABS_RZ , ABS_THROTTLE , ABS_HAT0X , ABS_HAT0Y } ,
{ ABS_X , ABS_Y , ABS_THROTTLE , ABS_HAT0X , ABS_HAT0Y } ,
{ ABS_RX , ABS_RUDDER , ABS_THROTTLE } } ;
static char sw_bit [ ] [ 7 ] = {
{ 10 , 10 , 9 , 10 , 1 , 1 } ,
{ 1 , 1 } ,
{ 10 , 10 , 6 , 7 , 1 , 1 } ,
{ 10 , 10 , 6 , 7 , 1 , 1 } ,
{ 10 , 10 , 6 , 1 , 1 } ,
{ 10 , 7 , 7 , 1 , 1 } } ;
static short sw_btn [ ] [ 12 ] = {
{ BTN_TRIGGER , BTN_TOP , BTN_THUMB , BTN_THUMB2 , BTN_BASE , BTN_BASE2 , BTN_BASE3 , BTN_BASE4 , BTN_MODE } ,
{ BTN_A , BTN_B , BTN_C , BTN_X , BTN_Y , BTN_Z , BTN_TL , BTN_TR , BTN_START , BTN_MODE } ,
{ BTN_TRIGGER , BTN_THUMB , BTN_TOP , BTN_TOP2 , BTN_BASE , BTN_BASE2 , BTN_BASE3 , BTN_BASE4 , BTN_SELECT } ,
{ BTN_TRIGGER , BTN_THUMB , BTN_TOP , BTN_TOP2 , BTN_BASE , BTN_BASE2 , BTN_BASE3 , BTN_BASE4 , BTN_SELECT } ,
{ BTN_A , BTN_B , BTN_C , BTN_X , BTN_Y , BTN_Z , BTN_TL , BTN_TR , BTN_START , BTN_MODE , BTN_SELECT } ,
{ BTN_TRIGGER , BTN_TOP , BTN_THUMB , BTN_THUMB2 , BTN_BASE , BTN_BASE2 , BTN_BASE3 , BTN_BASE4 } } ;
static struct {
int x ;
int y ;
} sw_hat_to_axis [ ] = { { 0 , 0 } , { 0 , - 1 } , { 1 , - 1 } , { 1 , 0 } , { 1 , 1 } , { 0 , 1 } , { - 1 , 1 } , { - 1 , 0 } , { - 1 , - 1 } } ;
struct sw {
struct gameport * gameport ;
2005-09-15 02:01:52 -05:00
struct input_dev * dev [ 4 ] ;
2005-04-16 15:20:36 -07:00
char name [ 64 ] ;
char phys [ 4 ] [ 32 ] ;
int length ;
int type ;
int bits ;
int number ;
int fail ;
int ok ;
int reads ;
int bads ;
} ;
/*
* sw_read_packet ( ) is a function which reads either a data packet , or an
* identification packet from a SideWinder joystick . The protocol is very ,
* very , very braindamaged . Microsoft patented it in US patent # 5628686.
*/
static int sw_read_packet ( struct gameport * gameport , unsigned char * buf , int length , int id )
{
unsigned long flags ;
int timeout , bitout , sched , i , kick , start , strobe ;
unsigned char pending , u , v ;
i = - id ; /* Don't care about data, only want ID */
timeout = id ? gameport_time ( gameport , SW_TIMEOUT * 1000 ) : 0 ; /* Set up global timeout for ID packet */
kick = id ? gameport_time ( gameport , SW_KICK ) : 0 ; /* Set up kick timeout for ID packet */
start = gameport_time ( gameport , SW_START ) ;
strobe = gameport_time ( gameport , SW_STROBE ) ;
bitout = start ;
pending = 0 ;
sched = 0 ;
local_irq_save ( flags ) ; /* Quiet, please */
gameport_trigger ( gameport ) ; /* Trigger */
v = gameport_read ( gameport ) ;
do {
bitout - - ;
u = v ;
v = gameport_read ( gameport ) ;
} while ( ! ( ~ v & u & 0x10 ) & & ( bitout > 0 ) ) ; /* Wait for first falling edge on clock */
if ( bitout > 0 )
bitout = strobe ; /* Extend time if not timed out */
while ( ( timeout > 0 | | bitout > 0 ) & & ( i < length ) ) {
timeout - - ;
bitout - - ; /* Decrement timers */
sched - - ;
u = v ;
v = gameport_read ( gameport ) ;
if ( ( ~ u & v & 0x10 ) & & ( bitout > 0 ) ) { /* Rising edge on clock - data bit */
if ( i > = 0 ) /* Want this data */
buf [ i ] = v > > 5 ; /* Store it */
i + + ; /* Advance index */
bitout = strobe ; /* Extend timeout for next bit */
}
if ( kick & & ( ~ v & u & 0x01 ) ) { /* Falling edge on axis 0 */
sched = kick ; /* Schedule second trigger */
kick = 0 ; /* Don't schedule next time on falling edge */
pending = 1 ; /* Mark schedule */
}
if ( pending & & sched < 0 & & ( i > - SW_END ) ) { /* Second trigger time */
gameport_trigger ( gameport ) ; /* Trigger */
bitout = start ; /* Long bit timeout */
pending = 0 ; /* Unmark schedule */
timeout = 0 ; /* Switch from global to bit timeouts */
}
}
local_irq_restore ( flags ) ; /* Done - relax */
# ifdef SW_DEBUG_DATA
{
int j ;
printk ( KERN_DEBUG " sidewinder.c: Read %d triplets. [ " , i ) ;
for ( j = 0 ; j < i ; j + + ) printk ( " %d " , buf [ j ] ) ;
printk ( " ] \n " ) ;
}
# endif
return i ;
}
/*
* sw_get_bits ( ) and GB ( ) compose 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(pos,num) sw_get_bits(buf, pos, num, sw->bits)
static __u64 sw_get_bits ( unsigned char * buf , int pos , int num , char bits )
{
__u64 data = 0 ;
int tri = pos % bits ; /* Start position */
int i = pos / bits ;
int bit = 0 ;
while ( num - - ) {
data | = ( __u64 ) ( ( buf [ i ] > > tri + + ) & 1 ) < < bit + + ; /* Transfer bit */
if ( tri = = bits ) {
i + + ; /* Next triplet */
tri = 0 ;
}
}
return data ;
}
/*
* sw_init_digital ( ) initializes a SideWinder 3 D Pro joystick
* into digital mode .
*/
static void sw_init_digital ( struct gameport * gameport )
{
int seq [ ] = { 140 , 140 + 725 , 140 + 300 , 0 } ;
unsigned long flags ;
int i , t ;
local_irq_save ( flags ) ;
i = 0 ;
do {
gameport_trigger ( gameport ) ; /* Trigger */
t = gameport_time ( gameport , SW_TIMEOUT * 1000 ) ;
while ( ( gameport_read ( gameport ) & 1 ) & & t ) t - - ; /* Wait for axis to fall back to 0 */
udelay ( seq [ i ] ) ; /* Delay magic time */
} while ( seq [ + + i ] ) ;
gameport_trigger ( gameport ) ; /* Last trigger */
local_irq_restore ( flags ) ;
}
/*
* sw_parity ( ) computes parity of __u64
*/
static int sw_parity ( __u64 t )
{
int x = t ^ ( t > > 32 ) ;
x ^ = x > > 16 ;
x ^ = x > > 8 ;
x ^ = x > > 4 ;
x ^ = x > > 2 ;
x ^ = x > > 1 ;
return x & 1 ;
}
/*
* sw_ccheck ( ) checks synchronization bits and computes checksum of nibbles .
*/
static int sw_check ( __u64 t )
{
unsigned char sum = 0 ;
if ( ( t & 0x8080808080808080ULL ) ^ 0x80 ) /* Sync */
return - 1 ;
while ( t ) { /* Sum */
sum + = t & 0xf ;
t > > = 4 ;
}
return sum & 0xf ;
}
/*
* sw_parse ( ) analyzes SideWinder joystick data , and writes the results into
* the axes and buttons arrays .
*/
static int sw_parse ( unsigned char * buf , struct sw * sw )
{
int hat , i , j ;
2005-09-15 02:01:52 -05:00
struct input_dev * dev ;
2005-04-16 15:20:36 -07:00
switch ( sw - > type ) {
case SW_ID_3DP :
if ( sw_check ( GB ( 0 , 64 ) ) | | ( hat = ( GB ( 6 , 1 ) < < 3 ) | GB ( 60 , 3 ) ) > 8 )
return - 1 ;
2005-09-15 02:01:52 -05:00
dev = sw - > dev [ 0 ] ;
2005-04-16 15:20:36 -07:00
input_report_abs ( dev , ABS_X , ( GB ( 3 , 3 ) < < 7 ) | GB ( 16 , 7 ) ) ;
input_report_abs ( dev , ABS_Y , ( GB ( 0 , 3 ) < < 7 ) | GB ( 24 , 7 ) ) ;
input_report_abs ( dev , ABS_RZ , ( GB ( 35 , 2 ) < < 7 ) | GB ( 40 , 7 ) ) ;
input_report_abs ( dev , ABS_THROTTLE , ( GB ( 32 , 3 ) < < 7 ) | GB ( 48 , 7 ) ) ;
input_report_abs ( dev , ABS_HAT0X , sw_hat_to_axis [ hat ] . x ) ;
input_report_abs ( dev , ABS_HAT0Y , sw_hat_to_axis [ hat ] . y ) ;
for ( j = 0 ; j < 7 ; j + + )
input_report_key ( dev , sw_btn [ SW_ID_3DP ] [ j ] , ! GB ( j + 8 , 1 ) ) ;
input_report_key ( dev , BTN_BASE4 , ! GB ( 38 , 1 ) ) ;
input_report_key ( dev , BTN_BASE5 , ! GB ( 37 , 1 ) ) ;
input_sync ( dev ) ;
return 0 ;
case SW_ID_GP :
for ( i = 0 ; i < sw - > number ; i + + ) {
if ( sw_parity ( GB ( i * 15 , 15 ) ) )
return - 1 ;
2005-09-15 02:01:52 -05:00
input_report_abs ( sw - > dev [ i ] , ABS_X , GB ( i * 15 + 3 , 1 ) - GB ( i * 15 + 2 , 1 ) ) ;
input_report_abs ( sw - > dev [ i ] , ABS_Y , GB ( i * 15 + 0 , 1 ) - GB ( i * 15 + 1 , 1 ) ) ;
2005-04-16 15:20:36 -07:00
for ( j = 0 ; j < 10 ; j + + )
2005-09-15 02:01:52 -05:00
input_report_key ( sw - > dev [ i ] , sw_btn [ SW_ID_GP ] [ j ] , ! GB ( i * 15 + j + 4 , 1 ) ) ;
2005-04-16 15:20:36 -07:00
2005-09-15 02:01:52 -05:00
input_sync ( sw - > dev [ i ] ) ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
case SW_ID_PP :
case SW_ID_FFP :
if ( ! sw_parity ( GB ( 0 , 48 ) ) | | ( hat = GB ( 42 , 4 ) ) > 8 )
return - 1 ;
2005-09-15 02:01:52 -05:00
dev = sw - > dev [ 0 ] ;
2005-04-16 15:20:36 -07:00
input_report_abs ( dev , ABS_X , GB ( 9 , 10 ) ) ;
input_report_abs ( dev , ABS_Y , GB ( 19 , 10 ) ) ;
input_report_abs ( dev , ABS_RZ , GB ( 36 , 6 ) ) ;
input_report_abs ( dev , ABS_THROTTLE , GB ( 29 , 7 ) ) ;
input_report_abs ( dev , ABS_HAT0X , sw_hat_to_axis [ hat ] . x ) ;
input_report_abs ( dev , ABS_HAT0Y , sw_hat_to_axis [ hat ] . y ) ;
for ( j = 0 ; j < 9 ; j + + )
input_report_key ( dev , sw_btn [ SW_ID_PP ] [ j ] , ! GB ( j , 1 ) ) ;
input_sync ( dev ) ;
return 0 ;
case SW_ID_FSP :
if ( ! sw_parity ( GB ( 0 , 43 ) ) | | ( hat = GB ( 28 , 4 ) ) > 8 )
return - 1 ;
2005-09-15 02:01:52 -05:00
dev = sw - > dev [ 0 ] ;
2005-04-16 15:20:36 -07:00
input_report_abs ( dev , ABS_X , GB ( 0 , 10 ) ) ;
input_report_abs ( dev , ABS_Y , GB ( 16 , 10 ) ) ;
input_report_abs ( dev , ABS_THROTTLE , GB ( 32 , 6 ) ) ;
input_report_abs ( dev , ABS_HAT0X , sw_hat_to_axis [ hat ] . x ) ;
input_report_abs ( dev , ABS_HAT0Y , sw_hat_to_axis [ hat ] . y ) ;
for ( j = 0 ; j < 6 ; j + + )
input_report_key ( dev , sw_btn [ SW_ID_FSP ] [ j ] , ! GB ( j + 10 , 1 ) ) ;
input_report_key ( dev , BTN_TR , ! GB ( 26 , 1 ) ) ;
input_report_key ( dev , BTN_START , ! GB ( 27 , 1 ) ) ;
input_report_key ( dev , BTN_MODE , ! GB ( 38 , 1 ) ) ;
input_report_key ( dev , BTN_SELECT , ! GB ( 39 , 1 ) ) ;
input_sync ( dev ) ;
return 0 ;
case SW_ID_FFW :
if ( ! sw_parity ( GB ( 0 , 33 ) ) )
return - 1 ;
2005-09-15 02:01:52 -05:00
dev = sw - > dev [ 0 ] ;
2005-04-16 15:20:36 -07:00
input_report_abs ( dev , ABS_RX , GB ( 0 , 10 ) ) ;
input_report_abs ( dev , ABS_RUDDER , GB ( 10 , 6 ) ) ;
input_report_abs ( dev , ABS_THROTTLE , GB ( 16 , 6 ) ) ;
for ( j = 0 ; j < 8 ; j + + )
input_report_key ( dev , sw_btn [ SW_ID_FFW ] [ j ] , ! GB ( j + 22 , 1 ) ) ;
input_sync ( dev ) ;
return 0 ;
}
return - 1 ;
}
/*
* sw_read ( ) reads SideWinder joystick data , and reinitializes
* the joystick in case of persistent problems . This is the function that is
* called from the generic code to poll the joystick .
*/
static int sw_read ( struct sw * sw )
{
unsigned char buf [ SW_LENGTH ] ;
int i ;
i = sw_read_packet ( sw - > gameport , buf , sw - > length , 0 ) ;
if ( sw - > type = = SW_ID_3DP & & sw - > length = = 66 & & i ! = 66 ) { /* Broken packet, try to fix */
if ( i = = 64 & & ! sw_check ( sw_get_bits ( buf , 0 , 64 , 1 ) ) ) { /* Last init failed, 1 bit mode */
printk ( KERN_WARNING " sidewinder.c: Joystick in wrong mode on %s "
" - going to reinitialize. \n " , sw - > gameport - > phys ) ;
sw - > fail = SW_FAIL ; /* Reinitialize */
i = 128 ; /* Bogus value */
}
if ( i < 66 & & GB ( 0 , 64 ) = = GB ( i * 3 - 66 , 64 ) ) /* 1 == 3 */
i = 66 ; /* Everything is fine */
if ( i < 66 & & GB ( 0 , 64 ) = = GB ( 66 , 64 ) ) /* 1 == 2 */
i = 66 ; /* Everything is fine */
if ( i < 66 & & GB ( i * 3 - 132 , 64 ) = = GB ( i * 3 - 66 , 64 ) ) { /* 2 == 3 */
memmove ( buf , buf + i - 22 , 22 ) ; /* Move data */
i = 66 ; /* Carry on */
}
}
if ( i = = sw - > length & & ! sw_parse ( buf , sw ) ) { /* Parse data */
sw - > fail = 0 ;
sw - > ok + + ;
if ( sw - > type = = SW_ID_3DP & & sw - > length = = 66 /* Many packets OK */
& & sw - > ok > SW_OK ) {
printk ( KERN_INFO " sidewinder.c: No more trouble on %s "
" - enabling optimization again. \n " , sw - > gameport - > phys ) ;
sw - > length = 22 ;
}
return 0 ;
}
sw - > ok = 0 ;
sw - > fail + + ;
if ( sw - > type = = SW_ID_3DP & & sw - > length = = 22 & & sw - > fail > SW_BAD ) { /* Consecutive bad packets */
printk ( KERN_INFO " sidewinder.c: Many bit errors on %s "
" - disabling optimization. \n " , sw - > gameport - > phys ) ;
sw - > length = 66 ;
}
if ( sw - > fail < SW_FAIL )
return - 1 ; /* Not enough, don't reinitialize yet */
printk ( KERN_WARNING " sidewinder.c: Too many bit errors on %s "
" - reinitializing joystick. \n " , sw - > gameport - > phys ) ;
if ( ! i & & sw - > type = = SW_ID_3DP ) { /* 3D Pro can be in analog mode */
mdelay ( 3 * SW_TIMEOUT ) ;
sw_init_digital ( sw - > gameport ) ;
}
mdelay ( SW_TIMEOUT ) ;
i = sw_read_packet ( sw - > gameport , buf , SW_LENGTH , 0 ) ; /* Read normal data packet */
mdelay ( SW_TIMEOUT ) ;
sw_read_packet ( sw - > gameport , buf , SW_LENGTH , i ) ; /* Read ID packet, this initializes the stick */
sw - > fail = SW_FAIL ;
return - 1 ;
}
static void sw_poll ( struct gameport * gameport )
{
struct sw * sw = gameport_get_drvdata ( gameport ) ;
sw - > reads + + ;
if ( sw_read ( sw ) )
sw - > bads + + ;
}
static int sw_open ( struct input_dev * dev )
{
2007-04-12 01:34:14 -04:00
struct sw * sw = input_get_drvdata ( dev ) ;
2005-04-16 15:20:36 -07:00
gameport_start_polling ( sw - > gameport ) ;
return 0 ;
}
static void sw_close ( struct input_dev * dev )
{
2007-04-12 01:34:14 -04:00
struct sw * sw = input_get_drvdata ( dev ) ;
2005-04-16 15:20:36 -07:00
gameport_stop_polling ( sw - > gameport ) ;
}
/*
* sw_print_packet ( ) prints the contents of a SideWinder packet .
*/
static void sw_print_packet ( char * name , int length , unsigned char * buf , char bits )
{
int i ;
printk ( KERN_INFO " sidewinder.c: %s packet, %d bits. [ " , name , length ) ;
for ( i = ( ( ( length + 3 ) > > 2 ) - 1 ) ; i > = 0 ; i - - )
printk ( " %x " , ( int ) sw_get_bits ( buf , i < < 2 , 4 , bits ) ) ;
printk ( " ] \n " ) ;
}
/*
* sw_3dp_id ( ) translates the 3 DP id into a human legible string .
* Unfortunately I don ' t know how to do this for the other SW types .
*/
2006-06-26 01:45:48 -04:00
static void sw_3dp_id ( unsigned char * buf , char * comment , size_t size )
2005-04-16 15:20:36 -07:00
{
int i ;
char pnp [ 8 ] , rev [ 9 ] ;
for ( i = 0 ; i < 7 ; i + + ) /* ASCII PnP ID */
pnp [ i ] = sw_get_bits ( buf , 24 + 8 * i , 8 , 1 ) ;
for ( i = 0 ; i < 8 ; i + + ) /* ASCII firmware revision */
rev [ i ] = sw_get_bits ( buf , 88 + 8 * i , 8 , 1 ) ;
pnp [ 7 ] = rev [ 8 ] = 0 ;
2006-06-26 01:45:48 -04:00
snprintf ( comment , size , " [PnP %d.%02d id %s rev %s] " ,
2005-04-16 15:20:36 -07:00
( int ) ( ( sw_get_bits ( buf , 8 , 6 , 1 ) < < 6 ) | /* Two 6-bit values */
sw_get_bits ( buf , 16 , 6 , 1 ) ) / 100 ,
( int ) ( ( sw_get_bits ( buf , 8 , 6 , 1 ) < < 6 ) |
sw_get_bits ( buf , 16 , 6 , 1 ) ) % 100 ,
pnp , rev ) ;
}
/*
* sw_guess_mode ( ) checks the upper two button bits for toggling -
* indication of that the joystick is in 3 - bit mode . This is documented
* behavior for 3 DP ID packet , and for example the FSP does this in
* normal packets instead . Fun . . .
*/
static int sw_guess_mode ( unsigned char * buf , int len )
{
int i ;
unsigned char xor = 0 ;
for ( i = 1 ; i < len ; i + + )
xor | = ( buf [ i - 1 ] ^ buf [ i ] ) & 6 ;
return ! ! xor * 2 + 1 ;
}
/*
* sw_connect ( ) probes for SideWinder type joysticks .
*/
static int sw_connect ( struct gameport * gameport , struct gameport_driver * drv )
{
struct sw * sw ;
2005-09-15 02:01:52 -05:00
struct input_dev * input_dev ;
2005-04-16 15:20:36 -07:00
int i , j , k , l ;
2006-05-29 23:28:05 -04:00
int err = 0 ;
2005-04-16 15:20:36 -07:00
unsigned char * buf = NULL ; /* [SW_LENGTH] */
unsigned char * idbuf = NULL ; /* [SW_LENGTH] */
unsigned char m = 1 ;
char comment [ 40 ] ;
comment [ 0 ] = 0 ;
2005-09-06 15:18:33 -07:00
sw = kzalloc ( sizeof ( struct sw ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
buf = kmalloc ( SW_LENGTH , GFP_KERNEL ) ;
idbuf = kmalloc ( SW_LENGTH , GFP_KERNEL ) ;
if ( ! sw | | ! buf | | ! idbuf ) {
err = - ENOMEM ;
goto fail1 ;
}
sw - > gameport = gameport ;
gameport_set_drvdata ( gameport , sw ) ;
err = gameport_open ( gameport , drv , GAMEPORT_MODE_RAW ) ;
if ( err )
goto fail1 ;
dbg ( " Init 0: Opened %s, io %#x, speed %d " ,
gameport - > phys , gameport - > io , gameport - > speed ) ;
i = sw_read_packet ( gameport , buf , SW_LENGTH , 0 ) ; /* Read normal packet */
msleep ( SW_TIMEOUT ) ;
dbg ( " Init 1: Mode %d. Length %d. " , m , i ) ;
if ( ! i ) { /* No data. 3d Pro analog mode? */
sw_init_digital ( gameport ) ; /* Switch to digital */
msleep ( SW_TIMEOUT ) ;
i = sw_read_packet ( gameport , buf , SW_LENGTH , 0 ) ; /* Retry reading packet */
msleep ( SW_TIMEOUT ) ;
dbg ( " Init 1b: Length %d. " , i ) ;
if ( ! i ) { /* No data -> FAIL */
err = - ENODEV ;
goto fail2 ;
}
}
j = sw_read_packet ( gameport , idbuf , SW_LENGTH , i ) ; /* Read ID. This initializes the stick */
m | = sw_guess_mode ( idbuf , j ) ; /* ID packet should carry mode info [3DP] */
dbg ( " Init 2: Mode %d. ID Length %d. " , m , j ) ;
if ( j < = 0 ) { /* Read ID failed. Happens in 1-bit mode on PP */
msleep ( SW_TIMEOUT ) ;
i = sw_read_packet ( gameport , buf , SW_LENGTH , 0 ) ; /* Retry reading packet */
m | = sw_guess_mode ( buf , i ) ;
dbg ( " Init 2b: Mode %d. Length %d. " , m , i ) ;
if ( ! i ) {
err = - ENODEV ;
goto fail2 ;
}
msleep ( SW_TIMEOUT ) ;
j = sw_read_packet ( gameport , idbuf , SW_LENGTH , i ) ; /* Retry reading ID */
dbg ( " Init 2c: ID Length %d. " , j ) ;
}
sw - > type = - 1 ;
k = SW_FAIL ; /* Try SW_FAIL times */
l = 0 ;
do {
k - - ;
msleep ( SW_TIMEOUT ) ;
i = sw_read_packet ( gameport , buf , SW_LENGTH , 0 ) ; /* Read data packet */
dbg ( " Init 3: Mode %d. Length %d. Last %d. Tries %d. " , m , i , l , k ) ;
if ( i > l ) { /* Longer? As we can only lose bits, it makes */
/* no sense to try detection for a packet shorter */
l = i ; /* than the previous one */
sw - > number = 1 ;
sw - > gameport = gameport ;
sw - > length = i ;
sw - > bits = m ;
dbg ( " Init 3a: Case %d. \n " , i * m ) ;
switch ( i * m ) {
case 60 :
sw - > number + + ;
case 45 : /* Ambiguous packet length */
if ( j < = 40 ) { /* ID length less or eq 40 -> FSP */
case 43 :
sw - > type = SW_ID_FSP ;
break ;
}
sw - > number + + ;
case 30 :
sw - > number + + ;
case 15 :
sw - > type = SW_ID_GP ;
break ;
case 33 :
case 31 :
sw - > type = SW_ID_FFW ;
break ;
case 48 : /* Ambiguous */
if ( j = = 14 ) { /* ID length 14*3 -> FFP */
sw - > type = SW_ID_FFP ;
sprintf ( comment , " [AC %s] " , sw_get_bits ( idbuf , 38 , 1 , 3 ) ? " off " : " on " ) ;
} else
2006-06-26 01:45:48 -04:00
sw - > type = SW_ID_PP ;
2005-04-16 15:20:36 -07:00
break ;
case 66 :
sw - > bits = 3 ;
case 198 :
sw - > length = 22 ;
case 64 :
sw - > type = SW_ID_3DP ;
2006-06-26 01:45:48 -04:00
if ( j = = 160 )
sw_3dp_id ( idbuf , comment , sizeof ( comment ) ) ;
2005-04-16 15:20:36 -07:00
break ;
}
}
} while ( k & & sw - > type = = - 1 ) ;
if ( sw - > type = = - 1 ) {
printk ( KERN_WARNING " sidewinder.c: unknown joystick device detected "
" on %s, contact <vojtech@ucw.cz> \n " , gameport - > phys ) ;
sw_print_packet ( " ID " , j * 3 , idbuf , 3 ) ;
sw_print_packet ( " Data " , i * m , buf , m ) ;
err = - ENODEV ;
goto fail2 ;
}
# ifdef SW_DEBUG
sw_print_packet ( " ID " , j * 3 , idbuf , 3 ) ;
sw_print_packet ( " Data " , i * m , buf , m ) ;
# endif
gameport_set_poll_handler ( gameport , sw_poll ) ;
gameport_set_poll_interval ( gameport , 20 ) ;
k = i ;
l = j ;
for ( i = 0 ; i < sw - > number ; i + + ) {
int bits , code ;
2006-06-26 01:45:48 -04:00
snprintf ( sw - > name , sizeof ( sw - > name ) ,
" Microsoft SideWinder %s " , sw_name [ sw - > type ] ) ;
snprintf ( sw - > phys [ i ] , sizeof ( sw - > phys [ i ] ) ,
" %s/input%d " , gameport - > phys , i ) ;
2005-04-16 15:20:36 -07:00
2006-01-29 21:51:51 -05:00
sw - > dev [ i ] = input_dev = input_allocate_device ( ) ;
2005-09-15 02:01:52 -05:00
if ( ! input_dev ) {
err = - ENOMEM ;
goto fail3 ;
}
2005-04-16 15:20:36 -07:00
2005-09-15 02:01:52 -05:00
input_dev - > name = sw - > name ;
input_dev - > phys = sw - > phys [ i ] ;
input_dev - > id . bustype = BUS_GAMEPORT ;
input_dev - > id . vendor = GAMEPORT_ID_VENDOR_MICROSOFT ;
input_dev - > id . product = sw - > type ;
input_dev - > id . version = 0x0100 ;
2007-04-12 01:35:26 -04:00
input_dev - > dev . parent = & gameport - > dev ;
2007-04-12 01:34:14 -04:00
input_set_drvdata ( input_dev , sw ) ;
2005-04-16 15:20:36 -07:00
2005-09-15 02:01:52 -05:00
input_dev - > open = sw_open ;
input_dev - > close = sw_close ;
2005-04-16 15:20:36 -07:00
2007-10-18 23:40:32 -07:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_ABS ) ;
2005-04-16 15:20:36 -07:00
for ( j = 0 ; ( bits = sw_bit [ sw - > type ] [ j ] ) ; j + + ) {
2010-08-02 20:15:17 -07:00
int min , max , fuzz , flat ;
2005-04-16 15:20:36 -07:00
code = sw_abs [ sw - > type ] [ j ] ;
2010-08-02 20:15:17 -07:00
min = bits = = 1 ? - 1 : 0 ;
max = ( 1 < < bits ) - 1 ;
fuzz = ( bits > > 1 ) > = 2 ? 1 < < ( ( bits > > 1 ) - 2 ) : 0 ;
flat = code = = ABS_THROTTLE | | bits < 5 ?
0 : 1 < < ( bits - 5 ) ;
input_set_abs_params ( input_dev , code ,
min , max , fuzz , flat ) ;
2005-04-16 15:20:36 -07:00
}
for ( j = 0 ; ( code = sw_btn [ sw - > type ] [ j ] ) ; j + + )
2010-08-02 20:15:17 -07:00
__set_bit ( code , input_dev - > keybit ) ;
2005-09-15 02:01:52 -05:00
dbg ( " %s%s [%d-bit id %d data %d] \n " , sw - > name , comment , m , l , k ) ;
2005-04-16 15:20:36 -07:00
2006-01-29 21:51:56 -05:00
err = input_register_device ( sw - > dev [ i ] ) ;
if ( err )
goto fail4 ;
2005-04-16 15:20:36 -07:00
}
2006-05-29 23:28:05 -04:00
out : kfree ( buf ) ;
kfree ( idbuf ) ;
return err ;
2005-04-16 15:20:36 -07:00
2006-01-29 21:51:56 -05:00
fail4 : input_free_device ( sw - > dev [ i ] ) ;
fail3 : while ( - - i > = 0 )
2005-09-15 02:01:52 -05:00
input_unregister_device ( sw - > dev [ i ] ) ;
fail2 : gameport_close ( gameport ) ;
fail1 : gameport_set_drvdata ( gameport , NULL ) ;
2005-04-16 15:20:36 -07:00
kfree ( sw ) ;
2006-05-29 23:28:05 -04:00
goto out ;
2005-04-16 15:20:36 -07:00
}
static void sw_disconnect ( struct gameport * gameport )
{
struct sw * sw = gameport_get_drvdata ( gameport ) ;
int i ;
for ( i = 0 ; i < sw - > number ; i + + )
2005-09-15 02:01:52 -05:00
input_unregister_device ( sw - > dev [ i ] ) ;
2005-04-16 15:20:36 -07:00
gameport_close ( gameport ) ;
gameport_set_drvdata ( gameport , NULL ) ;
kfree ( sw ) ;
}
static struct gameport_driver sw_drv = {
. driver = {
. name = " sidewinder " ,
2006-01-29 21:51:56 -05:00
. owner = THIS_MODULE ,
2005-04-16 15:20:36 -07:00
} ,
. description = DRIVER_DESC ,
. connect = sw_connect ,
. disconnect = sw_disconnect ,
} ;
static int __init sw_init ( void )
{
2008-06-06 01:33:37 -04:00
return gameport_register_driver ( & sw_drv ) ;
2005-04-16 15:20:36 -07:00
}
static void __exit sw_exit ( void )
{
gameport_unregister_driver ( & sw_drv ) ;
}
module_init ( sw_init ) ;
module_exit ( sw_exit ) ;