2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) 1999 - 2001 Vojtech Pavlik
*/
/*
* Creative Labs Blaster GamePad Cobra 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/module.h>
# include <linux/slab.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 "Creative Labs Blaster GamePad Cobra driver"
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
# define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */
# define COBRA_LENGTH 36
static int cobra_btn [ ] = { BTN_START , BTN_SELECT , BTN_TL , BTN_TR , BTN_X , BTN_Y , BTN_Z , BTN_A , BTN_B , BTN_C , BTN_TL2 , BTN_TR2 , 0 } ;
struct cobra {
struct gameport * gameport ;
2005-09-15 11:01:52 +04:00
struct input_dev * dev [ 2 ] ;
2005-04-17 02:20:36 +04:00
int reads ;
int bads ;
unsigned char exists ;
char phys [ 2 ] [ 32 ] ;
} ;
static unsigned char cobra_read_packet ( struct gameport * gameport , unsigned int * data )
{
unsigned long flags ;
unsigned char u , v , w ;
__u64 buf [ 2 ] ;
int r [ 2 ] , t [ 2 ] ;
int i , j , ret ;
int strobe = gameport_time ( gameport , COBRA_MAX_STROBE ) ;
for ( i = 0 ; i < 2 ; i + + ) {
r [ i ] = buf [ i ] = 0 ;
t [ i ] = COBRA_MAX_STROBE ;
}
local_irq_save ( flags ) ;
u = gameport_read ( gameport ) ;
do {
t [ 0 ] - - ; t [ 1 ] - - ;
v = gameport_read ( gameport ) ;
for ( i = 0 , w = u ^ v ; i < 2 & & w ; i + + , w > > = 2 )
if ( w & 0x30 ) {
if ( ( w & 0x30 ) < 0x30 & & r [ i ] < COBRA_LENGTH & & t [ i ] > 0 ) {
buf [ i ] | = ( __u64 ) ( ( w > > 5 ) & 1 ) < < r [ i ] + + ;
t [ i ] = strobe ;
u = v ;
} else t [ i ] = 0 ;
}
} while ( t [ 0 ] > 0 | | t [ 1 ] > 0 ) ;
local_irq_restore ( flags ) ;
ret = 0 ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( r [ i ] ! = COBRA_LENGTH ) continue ;
for ( j = 0 ; j < COBRA_LENGTH & & ( buf [ i ] & 0x04104107f ) ^ 0x041041040 ; j + + )
buf [ i ] = ( buf [ i ] > > 1 ) | ( ( __u64 ) ( buf [ i ] & 1 ) < < ( COBRA_LENGTH - 1 ) ) ;
if ( j < COBRA_LENGTH ) ret | = ( 1 < < i ) ;
data [ i ] = ( ( buf [ i ] > > 7 ) & 0x000001f ) | ( ( buf [ i ] > > 8 ) & 0x00003e0 )
| ( ( buf [ i ] > > 9 ) & 0x0007c00 ) | ( ( buf [ i ] > > 10 ) & 0x00f8000 )
| ( ( buf [ i ] > > 11 ) & 0x1f00000 ) ;
}
return ret ;
}
static void cobra_poll ( struct gameport * gameport )
{
struct cobra * cobra = gameport_get_drvdata ( gameport ) ;
struct input_dev * dev ;
unsigned int data [ 2 ] ;
int i , j , r ;
cobra - > reads + + ;
if ( ( r = cobra_read_packet ( gameport , data ) ) ! = cobra - > exists ) {
cobra - > bads + + ;
return ;
}
for ( i = 0 ; i < 2 ; i + + )
if ( cobra - > exists & r & ( 1 < < i ) ) {
2005-09-15 11:01:52 +04:00
dev = cobra - > dev [ i ] ;
2005-04-17 02:20:36 +04:00
input_report_abs ( dev , ABS_X , ( ( data [ i ] > > 4 ) & 1 ) - ( ( data [ i ] > > 3 ) & 1 ) ) ;
input_report_abs ( dev , ABS_Y , ( ( data [ i ] > > 2 ) & 1 ) - ( ( data [ i ] > > 1 ) & 1 ) ) ;
for ( j = 0 ; cobra_btn [ j ] ; j + + )
input_report_key ( dev , cobra_btn [ j ] , data [ i ] & ( 0x20 < < j ) ) ;
input_sync ( dev ) ;
}
}
static int cobra_open ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct cobra * cobra = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_start_polling ( cobra - > gameport ) ;
return 0 ;
}
static void cobra_close ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct cobra * cobra = input_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
gameport_stop_polling ( cobra - > gameport ) ;
}
static int cobra_connect ( struct gameport * gameport , struct gameport_driver * drv )
{
struct cobra * cobra ;
2005-09-15 11:01:52 +04:00
struct input_dev * input_dev ;
2005-04-17 02:20:36 +04:00
unsigned int data [ 2 ] ;
int i , j ;
int err ;
2005-09-15 11:01:52 +04:00
cobra = kzalloc ( sizeof ( struct cobra ) , GFP_KERNEL ) ;
if ( ! cobra )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
cobra - > gameport = gameport ;
gameport_set_drvdata ( gameport , cobra ) ;
err = gameport_open ( gameport , drv , GAMEPORT_MODE_RAW ) ;
if ( err )
goto fail1 ;
cobra - > exists = cobra_read_packet ( gameport , data ) ;
for ( i = 0 ; i < 2 ; i + + )
if ( ( cobra - > exists > > i ) & data [ i ] & 1 ) {
printk ( KERN_WARNING " cobra.c: Device %d on %s has the Ext bit set. ID is: %d "
" Contact vojtech@ucw.cz \n " , i , gameport - > phys , ( data [ i ] > > 2 ) & 7 ) ;
cobra - > exists & = ~ ( 1 < < i ) ;
}
if ( ! cobra - > exists ) {
err = - ENODEV ;
goto fail2 ;
}
gameport_set_poll_handler ( gameport , cobra_poll ) ;
gameport_set_poll_interval ( gameport , 20 ) ;
2005-09-15 11:01:52 +04:00
for ( i = 0 ; i < 2 ; i + + ) {
if ( ~ ( cobra - > exists > > i ) & 1 )
continue ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
cobra - > dev [ i ] = input_dev = input_allocate_device ( ) ;
if ( ! input_dev ) {
err = - ENOMEM ;
goto fail3 ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 09:45:48 +04:00
snprintf ( cobra - > phys [ i ] , sizeof ( cobra - > phys [ i ] ) ,
" %s/input%d " , gameport - > phys , i ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
input_dev - > name = " Creative Labs Blaster GamePad Cobra " ;
input_dev - > phys = cobra - > phys [ i ] ;
input_dev - > id . bustype = BUS_GAMEPORT ;
input_dev - > id . vendor = GAMEPORT_ID_VENDOR_CREATIVE ;
input_dev - > id . product = 0x0008 ;
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 , cobra ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
input_dev - > open = cobra_open ;
input_dev - > close = cobra_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-09-15 11:01:52 +04:00
input_set_abs_params ( input_dev , ABS_X , - 1 , 1 , 0 , 0 ) ;
input_set_abs_params ( input_dev , ABS_Y , - 1 , 1 , 0 , 0 ) ;
for ( j = 0 ; cobra_btn [ j ] ; j + + )
set_bit ( cobra_btn [ j ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
2006-11-06 06:40:09 +03:00
err = input_register_device ( cobra - > dev [ i ] ) ;
if ( err )
goto fail4 ;
2005-09-15 11:01:52 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
2006-11-06 06:40:09 +03:00
fail4 : input_free_device ( cobra - > dev [ i ] ) ;
fail3 : while ( - - i > = 0 )
2005-09-15 11:01:52 +04:00
if ( cobra - > dev [ i ] )
input_unregister_device ( cobra - > dev [ i ] ) ;
fail2 : gameport_close ( gameport ) ;
fail1 : gameport_set_drvdata ( gameport , NULL ) ;
2005-04-17 02:20:36 +04:00
kfree ( cobra ) ;
return err ;
}
static void cobra_disconnect ( struct gameport * gameport )
{
struct cobra * cobra = gameport_get_drvdata ( gameport ) ;
int i ;
for ( i = 0 ; i < 2 ; i + + )
if ( ( cobra - > exists > > i ) & 1 )
2005-09-15 11:01:52 +04:00
input_unregister_device ( cobra - > dev [ i ] ) ;
2005-04-17 02:20:36 +04:00
gameport_close ( gameport ) ;
gameport_set_drvdata ( gameport , NULL ) ;
kfree ( cobra ) ;
}
static struct gameport_driver cobra_drv = {
. driver = {
. name = " cobra " ,
} ,
. description = DRIVER_DESC ,
. connect = cobra_connect ,
. disconnect = cobra_disconnect ,
} ;
2012-04-04 10:52:27 +04:00
module_gameport_driver ( cobra_drv ) ;