2005-04-17 02:20:36 +04:00
/*
* 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/gameport.h>
# include <linux/input.h>
2005-10-31 02:03:48 +03:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
# define DRIVER_DESC "Guillemot Digital joystick driver"
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
# define GUILLEMOT_MAX_START 600 /* 600 us */
# define GUILLEMOT_MAX_STROBE 60 /* 60 us */
# define GUILLEMOT_MAX_LENGTH 17 /* 17 bytes */
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 ;
2005-09-15 11:01:52 +04:00
struct input_dev * dev ;
2005-04-17 02:20:36 +04:00
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 ) ;
local_irq_save ( flags ) ;
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 ;
}
}
local_irq_restore ( flags ) ;
return i ;
}
/*
* guillemot_poll ( ) reads and analyzes Guillemot joystick data .
*/
static void guillemot_poll ( struct gameport * gameport )
{
struct guillemot * guillemot = gameport_get_drvdata ( gameport ) ;
2005-09-15 11:01:52 +04:00
struct input_dev * dev = guillemot - > dev ;
2005-04-17 02:20:36 +04:00
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 ) ;
}
input_sync ( dev ) ;
}
/*
* guillemot_open ( ) is a callback from the input open routine .
*/
static int guillemot_open ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct guillemot * guillemot = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_start_polling ( guillemot - > gameport ) ;
return 0 ;
}
/*
* guillemot_close ( ) is a callback from the input close routine .
*/
static void guillemot_close ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct guillemot * guillemot = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_stop_polling ( guillemot - > gameport ) ;
}
/*
* guillemot_connect ( ) probes for Guillemot joysticks .
*/
static int guillemot_connect ( struct gameport * gameport , struct gameport_driver * drv )
{
struct guillemot * guillemot ;
2005-09-15 11:01:52 +04:00
struct input_dev * input_dev ;
2005-04-17 02:20:36 +04:00
u8 data [ GUILLEMOT_MAX_LENGTH ] ;
int i , t ;
int err ;
2005-09-15 11:01:52 +04:00
guillemot = kzalloc ( sizeof ( struct guillemot ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! guillemot | | ! input_dev ) {
err = - ENOMEM ;
goto fail1 ;
}
2005-04-17 02:20:36 +04:00
guillemot - > gameport = gameport ;
2005-09-15 11:01:52 +04:00
guillemot - > dev = input_dev ;
2005-04-17 02:20:36 +04:00
gameport_set_drvdata ( gameport , guillemot ) ;
err = gameport_open ( gameport , drv , GAMEPORT_MODE_RAW ) ;
if ( err )
goto fail1 ;
i = guillemot_read_packet ( gameport , data ) ;
if ( i ! = GUILLEMOT_MAX_LENGTH * 8 | | data [ 0 ] ! = 0x55 | | data [ 16 ] ! = 0xaa ) {
err = - ENODEV ;
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 ] ) ;
err = - ENODEV ;
goto fail2 ;
}
gameport_set_poll_handler ( gameport , guillemot_poll ) ;
gameport_set_poll_interval ( gameport , 20 ) ;
2006-06-26 09:45:48 +04:00
snprintf ( guillemot - > phys , sizeof ( guillemot - > phys ) , " %s/input0 " , gameport - > phys ) ;
2005-04-17 02:20:36 +04:00
guillemot - > type = guillemot_type + i ;
2005-09-15 11:01:52 +04:00
input_dev - > name = guillemot_type [ i ] . name ;
input_dev - > phys = guillemot - > phys ;
input_dev - > id . bustype = BUS_GAMEPORT ;
input_dev - > id . vendor = GAMEPORT_ID_VENDOR_GUILLEMOT ;
input_dev - > id . product = guillemot_type [ i ] . id ;
input_dev - > id . version = ( int ) data [ 14 ] < < 8 | data [ 15 ] ;
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 , guillemot ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
input_dev - > open = guillemot_open ;
input_dev - > close = guillemot_close ;
2005-04-17 02:20:36 +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 ; ( t = guillemot - > type - > abs [ i ] ) > = 0 ; i + + )
2005-09-15 11:01:52 +04:00
input_set_abs_params ( input_dev , t , 0 , 255 , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( guillemot - > type - > hat ) {
2005-09-15 11:01:52 +04:00
input_set_abs_params ( input_dev , ABS_HAT0X , - 1 , 1 , 0 , 0 ) ;
input_set_abs_params ( input_dev , ABS_HAT0Y , - 1 , 1 , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
}
for ( i = 0 ; ( t = guillemot - > type - > btn [ i ] ) > = 0 ; i + + )
2005-09-15 11:01:52 +04:00
set_bit ( t , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
2006-11-06 06:40:09 +03:00
err = input_register_device ( guillemot - > dev ) ;
if ( err )
goto fail2 ;
2005-04-17 02:20:36 +04:00
return 0 ;
fail2 : gameport_close ( gameport ) ;
fail1 : gameport_set_drvdata ( gameport , NULL ) ;
2005-09-15 11:01:52 +04:00
input_free_device ( input_dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( guillemot ) ;
return err ;
}
static void guillemot_disconnect ( struct gameport * gameport )
{
struct guillemot * guillemot = gameport_get_drvdata ( gameport ) ;
printk ( KERN_INFO " guillemot.c: Failed %d reads out of %d on %s \n " , guillemot - > reads , guillemot - > bads , guillemot - > phys ) ;
2005-09-15 11:01:52 +04:00
input_unregister_device ( guillemot - > dev ) ;
2005-04-17 02:20:36 +04:00
gameport_close ( gameport ) ;
kfree ( guillemot ) ;
}
static struct gameport_driver guillemot_drv = {
. driver = {
. name = " guillemot " ,
} ,
. description = DRIVER_DESC ,
. connect = guillemot_connect ,
. disconnect = guillemot_disconnect ,
} ;
2012-04-04 10:52:27 +04:00
module_gameport_driver ( guillemot_drv ) ;