2005-04-17 02:20:36 +04:00
/*
* NES , SNES , N64 , MultiSystem , PSX gamepad driver for Linux
*
2005-05-29 11:28:55 +04:00
* Copyright ( c ) 1999 - 2004 Vojtech Pavlik < vojtech @ suse . cz >
* Copyright ( c ) 2004 Peter Nelson < rufus - kernel @ hackish . org >
2005-04-17 02:20:36 +04:00
*
* Based on the work of :
2005-05-29 11:28:55 +04:00
* Andree Borrmann John Dahlstrom
* David Kuder Nathan Hand
2006-04-02 09:10:05 +04:00
* Raphael Assenat
2005-04-17 02:20:36 +04:00
*/
/*
* 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/delay.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/parport.h>
# include <linux/input.h>
2006-02-19 08:22:30 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " NES, SNES, N64, MultiSystem, PSX gamepad driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-09-15 11:01:52 +04:00
# define GC_MAX_PORTS 3
# define GC_MAX_DEVICES 5
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
struct gc_config {
int args [ GC_MAX_DEVICES + 1 ] ;
2007-05-03 08:52:51 +04:00
unsigned int nargs ;
2005-09-15 11:01:52 +04:00
} ;
2007-05-03 08:52:51 +04:00
static struct gc_config gc_cfg [ GC_MAX_PORTS ] __initdata ;
2005-04-17 02:20:36 +04:00
2007-05-03 08:52:51 +04:00
module_param_array_named ( map , gc_cfg [ 0 ] . args , int , & gc_cfg [ 0 ] . nargs , 0 ) ;
2005-09-15 11:01:52 +04:00
MODULE_PARM_DESC ( map , " Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>) " ) ;
2007-05-03 08:52:51 +04:00
module_param_array_named ( map2 , gc_cfg [ 1 ] . args , int , & gc_cfg [ 1 ] . nargs , 0 ) ;
2005-09-15 11:01:52 +04:00
MODULE_PARM_DESC ( map2 , " Describes second set of devices " ) ;
2007-05-03 08:52:51 +04:00
module_param_array_named ( map3 , gc_cfg [ 2 ] . args , int , & gc_cfg [ 2 ] . nargs , 0 ) ;
2005-09-15 11:01:52 +04:00
MODULE_PARM_DESC ( map3 , " Describes third set of devices " ) ;
2005-04-17 02:20:36 +04:00
/* see also gs_psx_delay parameter in PSX support section */
# define GC_SNES 1
# define GC_NES 2
# define GC_NES4 3
# define GC_MULTI 4
# define GC_MULTI2 5
# define GC_N64 6
# define GC_PSX 7
# define GC_DDR 8
2006-04-02 09:10:05 +04:00
# define GC_SNESMOUSE 9
2005-04-17 02:20:36 +04:00
2006-04-02 09:10:05 +04:00
# define GC_MAX 9
2005-04-17 02:20:36 +04:00
# define GC_REFRESH_TIME HZ / 100
struct gc {
struct pardevice * pd ;
2005-09-15 11:01:52 +04:00
struct input_dev * dev [ GC_MAX_DEVICES ] ;
2005-04-17 02:20:36 +04:00
struct timer_list timer ;
unsigned char pads [ GC_MAX + 1 ] ;
int used ;
2006-02-19 08:22:30 +03:00
struct mutex mutex ;
2005-09-15 11:01:52 +04:00
char phys [ GC_MAX_DEVICES ] [ 32 ] ;
2005-04-17 02:20:36 +04:00
} ;
2010-02-22 07:53:55 +03:00
struct gc_subdev {
unsigned int idx ;
} ;
2005-04-17 02:20:36 +04:00
static struct gc * gc_base [ 3 ] ;
static int gc_status_bit [ ] = { 0x40 , 0x80 , 0x20 , 0x10 , 0x08 } ;
2010-02-22 07:54:28 +03:00
static char * gc_names [ ] = {
NULL , " SNES pad " , " NES pad " , " NES FourPort " , " Multisystem joystick " ,
" Multisystem 2-button joystick " , " N64 controller " , " PSX controller " ,
" PSX DDR controller " , " SNES mouse "
} ;
2005-04-17 02:20:36 +04:00
/*
* N64 support .
*/
static unsigned char gc_n64_bytes [ ] = { 0 , 1 , 13 , 15 , 14 , 12 , 10 , 11 , 2 , 3 } ;
2010-02-22 07:54:28 +03:00
static short gc_n64_btn [ ] = {
BTN_A , BTN_B , BTN_C , BTN_X , BTN_Y , BTN_Z ,
BTN_TL , BTN_TR , BTN_TRIGGER , BTN_START
} ;
2005-04-17 02:20:36 +04:00
# define GC_N64_LENGTH 32 /* N64 bit length, not including stop bit */
2010-02-22 07:53:55 +03:00
# define GC_N64_STOP_LENGTH 5 /* Length of encoded stop bit */
# define GC_N64_CMD_00 0x11111111UL
# define GC_N64_CMD_01 0xd1111111UL
# define GC_N64_CMD_03 0xdd111111UL
# define GC_N64_CMD_1b 0xdd1dd111UL
# define GC_N64_CMD_c0 0x111111ddUL
# define GC_N64_CMD_80 0x1111111dUL
# define GC_N64_STOP_BIT 0x1d /* Encoded stop bit */
# define GC_N64_REQUEST_DATA GC_N64_CMD_01 /* the request data command */
2005-04-17 02:20:36 +04:00
# define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */
# define GC_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */
/* GC_N64_DWS > 24 is known to fail */
# define GC_N64_POWER_W 0xe2 /* power during write (transmit request) */
# define GC_N64_POWER_R 0xfd /* power during read */
# define GC_N64_OUT 0x1d /* output bits to the 4 pads */
/* Reading the main axes of any N64 pad is known to fail if the corresponding bit */
/* in GC_N64_OUT is pulled low on the output port (by any routine) for more */
/* than 123 us */
# define GC_N64_CLOCK 0x02 /* clock bits for read */
2010-02-22 07:53:55 +03:00
/*
* Used for rumble code .
*/
/* Send encoded command */
static void gc_n64_send_command ( struct gc * gc , unsigned long cmd ,
unsigned char target )
{
struct parport * port = gc - > pd - > port ;
int i ;
for ( i = 0 ; i < GC_N64_LENGTH ; i + + ) {
unsigned char data = ( cmd > > i ) & 1 ? target : 0 ;
parport_write_data ( port , GC_N64_POWER_W | data ) ;
udelay ( GC_N64_DWS ) ;
}
}
/* Send stop bit */
static void gc_n64_send_stop_bit ( struct gc * gc , unsigned char target )
{
struct parport * port = gc - > pd - > port ;
int i ;
for ( i = 0 ; i < GC_N64_STOP_LENGTH ; i + + ) {
unsigned char data = ( GC_N64_STOP_BIT > > i ) & 1 ? target : 0 ;
parport_write_data ( port , GC_N64_POWER_W | data ) ;
udelay ( GC_N64_DWS ) ;
}
}
2005-04-17 02:20:36 +04:00
/*
* gc_n64_read_packet ( ) reads an N64 packet .
2010-02-22 07:54:28 +03:00
* Each pad uses one bit per byte . So all pads connected to this port
* are read in parallel .
2005-04-17 02:20:36 +04:00
*/
static void gc_n64_read_packet ( struct gc * gc , unsigned char * data )
{
int i ;
unsigned long flags ;
/*
* Request the pad to transmit data
*/
local_irq_save ( flags ) ;
2010-02-22 07:53:55 +03:00
gc_n64_send_command ( gc , GC_N64_REQUEST_DATA , GC_N64_OUT ) ;
gc_n64_send_stop_bit ( gc , GC_N64_OUT ) ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
/*
2010-02-22 07:54:28 +03:00
* Wait for the pad response to be loaded into the 33 - bit register
* of the adapter .
2005-04-17 02:20:36 +04:00
*/
udelay ( GC_N64_DELAY ) ;
/*
* Grab data ( ignoring the last bit , which is a stop bit )
*/
for ( i = 0 ; i < GC_N64_LENGTH ; i + + ) {
parport_write_data ( gc - > pd - > port , GC_N64_POWER_R ) ;
2010-02-22 07:53:55 +03:00
udelay ( 2 ) ;
2005-04-17 02:20:36 +04:00
data [ i ] = parport_read_status ( gc - > pd - > port ) ;
parport_write_data ( gc - > pd - > port , GC_N64_POWER_R | GC_N64_CLOCK ) ;
}
/*
2010-02-22 07:54:28 +03:00
* We must wait 200 ms here for the controller to reinitialize before
* the next read request . No worries as long as gc_read is polled less
* frequently than this .
2005-04-17 02:20:36 +04:00
*/
}
2006-01-30 05:52:04 +03:00
static void gc_n64_process_packet ( struct gc * gc )
{
unsigned char data [ GC_N64_LENGTH ] ;
struct input_dev * dev ;
int i , j , s ;
2010-02-22 07:54:28 +03:00
signed char x , y ;
2006-01-30 05:52:04 +03:00
gc_n64_read_packet ( gc , data ) ;
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + ) {
dev = gc - > dev [ i ] ;
if ( ! dev )
continue ;
s = gc_status_bit [ i ] ;
if ( s & gc - > pads [ GC_N64 ] & ~ ( data [ 8 ] | data [ 9 ] ) ) {
2010-02-22 07:54:28 +03:00
x = y = 0 ;
2006-01-30 05:52:04 +03:00
for ( j = 0 ; j < 8 ; j + + ) {
if ( data [ 23 - j ] & s )
2010-02-22 07:54:28 +03:00
x | = 1 < < j ;
2006-01-30 05:52:04 +03:00
if ( data [ 31 - j ] & s )
2010-02-22 07:54:28 +03:00
y | = 1 < < j ;
2006-01-30 05:52:04 +03:00
}
2010-02-22 07:54:28 +03:00
input_report_abs ( dev , ABS_X , x ) ;
input_report_abs ( dev , ABS_Y , - y ) ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
input_report_abs ( dev , ABS_HAT0X ,
! ( s & data [ 6 ] ) - ! ( s & data [ 7 ] ) ) ;
input_report_abs ( dev , ABS_HAT0Y ,
! ( s & data [ 4 ] ) - ! ( s & data [ 5 ] ) ) ;
2006-01-30 05:52:04 +03:00
for ( j = 0 ; j < 10 ; j + + )
2010-02-22 07:54:28 +03:00
input_report_key ( dev , gc_n64_btn [ j ] ,
s & data [ gc_n64_bytes [ j ] ] ) ;
2006-01-30 05:52:04 +03:00
input_sync ( dev ) ;
}
}
}
2010-02-22 07:53:55 +03:00
static int gc_n64_play_effect ( struct input_dev * dev , void * data ,
struct ff_effect * effect )
{
int i ;
unsigned long flags ;
struct gc * gc = input_get_drvdata ( dev ) ;
struct gc_subdev * sdev = data ;
unsigned char target = 1 < < sdev - > idx ; /* select desired pin */
if ( effect - > type = = FF_RUMBLE ) {
struct ff_rumble_effect * rumble = & effect - > u . rumble ;
unsigned int cmd =
rumble - > strong_magnitude | | rumble - > weak_magnitude ?
GC_N64_CMD_01 : GC_N64_CMD_00 ;
local_irq_save ( flags ) ;
/* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */
gc_n64_send_command ( gc , GC_N64_CMD_03 , target ) ;
gc_n64_send_command ( gc , GC_N64_CMD_80 , target ) ;
gc_n64_send_command ( gc , GC_N64_CMD_01 , target ) ;
for ( i = 0 ; i < 32 ; i + + )
gc_n64_send_command ( gc , GC_N64_CMD_80 , target ) ;
gc_n64_send_stop_bit ( gc , target ) ;
udelay ( GC_N64_DELAY ) ;
/* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */
gc_n64_send_command ( gc , GC_N64_CMD_03 , target ) ;
gc_n64_send_command ( gc , GC_N64_CMD_c0 , target ) ;
gc_n64_send_command ( gc , GC_N64_CMD_1b , target ) ;
for ( i = 0 ; i < 32 ; i + + )
gc_n64_send_command ( gc , cmd , target ) ;
gc_n64_send_stop_bit ( gc , target ) ;
local_irq_restore ( flags ) ;
}
return 0 ;
}
static int __init gc_n64_init_ff ( struct input_dev * dev , int i )
{
struct gc_subdev * sdev ;
int err ;
sdev = kmalloc ( sizeof ( * sdev ) , GFP_KERNEL ) ;
if ( ! sdev )
return - ENOMEM ;
sdev - > idx = i ;
input_set_capability ( dev , EV_FF , FF_RUMBLE ) ;
err = input_ff_create_memless ( dev , sdev , gc_n64_play_effect ) ;
if ( err ) {
kfree ( sdev ) ;
return err ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* NES / SNES support .
*/
2006-04-02 09:10:05 +04:00
# define GC_NES_DELAY 6 /* Delay between bits - 6us */
# define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */
# define GC_SNES_LENGTH 12 / * The SNES true length is 16, but the
last 4 bits are unused */
# define GC_SNESMOUSE_LENGTH 32 / * The SNES mouse uses 32 bits, the first
16 bits are equivalent to a gamepad */
2005-04-17 02:20:36 +04:00
# define GC_NES_POWER 0xfc
# define GC_NES_CLOCK 0x01
# define GC_NES_LATCH 0x02
static unsigned char gc_nes_bytes [ ] = { 0 , 1 , 2 , 3 } ;
static unsigned char gc_snes_bytes [ ] = { 8 , 0 , 2 , 3 , 9 , 1 , 10 , 11 } ;
2010-02-22 07:54:28 +03:00
static short gc_snes_btn [ ] = {
BTN_A , BTN_B , BTN_SELECT , BTN_START , BTN_X , BTN_Y , BTN_TL , BTN_TR
} ;
2005-04-17 02:20:36 +04:00
/*
* gc_nes_read_packet ( ) reads a NES / SNES packet .
* Each pad uses one bit per byte . So all pads connected to
* this port are read in parallel .
*/
static void gc_nes_read_packet ( struct gc * gc , int length , unsigned char * data )
{
int i ;
parport_write_data ( gc - > pd - > port , GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH ) ;
udelay ( GC_NES_DELAY * 2 ) ;
parport_write_data ( gc - > pd - > port , GC_NES_POWER | GC_NES_CLOCK ) ;
for ( i = 0 ; i < length ; i + + ) {
udelay ( GC_NES_DELAY ) ;
parport_write_data ( gc - > pd - > port , GC_NES_POWER ) ;
data [ i ] = parport_read_status ( gc - > pd - > port ) ^ 0x7f ;
udelay ( GC_NES_DELAY ) ;
parport_write_data ( gc - > pd - > port , GC_NES_POWER | GC_NES_CLOCK ) ;
}
}
2006-01-30 05:52:04 +03:00
static void gc_nes_process_packet ( struct gc * gc )
{
2006-04-02 09:10:05 +04:00
unsigned char data [ GC_SNESMOUSE_LENGTH ] ;
2006-01-30 05:52:04 +03:00
struct input_dev * dev ;
2006-04-02 09:10:05 +04:00
int i , j , s , len ;
char x_rel , y_rel ;
len = gc - > pads [ GC_SNESMOUSE ] ? GC_SNESMOUSE_LENGTH :
( gc - > pads [ GC_SNES ] ? GC_SNES_LENGTH : GC_NES_LENGTH ) ;
2006-01-30 05:52:04 +03:00
2006-04-02 09:10:05 +04:00
gc_nes_read_packet ( gc , len , data ) ;
2006-01-30 05:52:04 +03:00
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + ) {
dev = gc - > dev [ i ] ;
if ( ! dev )
continue ;
s = gc_status_bit [ i ] ;
if ( s & ( gc - > pads [ GC_NES ] | gc - > pads [ GC_SNES ] ) ) {
input_report_abs ( dev , ABS_X , ! ( s & data [ 6 ] ) - ! ( s & data [ 7 ] ) ) ;
input_report_abs ( dev , ABS_Y , ! ( s & data [ 4 ] ) - ! ( s & data [ 5 ] ) ) ;
}
if ( s & gc - > pads [ GC_NES ] )
for ( j = 0 ; j < 4 ; j + + )
2010-02-22 07:54:28 +03:00
input_report_key ( dev , gc_snes_btn [ j ] ,
s & data [ gc_nes_bytes [ j ] ] ) ;
2006-01-30 05:52:04 +03:00
if ( s & gc - > pads [ GC_SNES ] )
for ( j = 0 ; j < 8 ; j + + )
2010-02-22 07:54:28 +03:00
input_report_key ( dev , gc_snes_btn [ j ] ,
s & data [ gc_snes_bytes [ j ] ] ) ;
2006-01-30 05:52:04 +03:00
2006-04-02 09:10:05 +04:00
if ( s & gc - > pads [ GC_SNESMOUSE ] ) {
/*
2010-02-22 07:54:28 +03:00
* The 4 unused bits from SNES controllers appear
* to be ID bits so use them to make sure we are
* dealing with a mouse .
2006-04-02 09:10:05 +04:00
* gamepad is connected . This is important since
* my SNES gamepad sends 1 ' s for bits 16 - 31 , which
* cause the mouse pointer to quickly move to the
* upper left corner of the screen .
*/
if ( ! ( s & data [ 12 ] ) & & ! ( s & data [ 13 ] ) & &
! ( s & data [ 14 ] ) & & ( s & data [ 15 ] ) ) {
input_report_key ( dev , BTN_LEFT , s & data [ 9 ] ) ;
input_report_key ( dev , BTN_RIGHT , s & data [ 8 ] ) ;
x_rel = y_rel = 0 ;
for ( j = 0 ; j < 7 ; j + + ) {
x_rel < < = 1 ;
if ( data [ 25 + j ] & s )
x_rel | = 1 ;
y_rel < < = 1 ;
if ( data [ 17 + j ] & s )
y_rel | = 1 ;
}
if ( x_rel ) {
if ( data [ 24 ] & s )
x_rel = - x_rel ;
input_report_rel ( dev , REL_X , x_rel ) ;
}
if ( y_rel ) {
if ( data [ 16 ] & s )
y_rel = - y_rel ;
input_report_rel ( dev , REL_Y , y_rel ) ;
}
}
}
2006-01-30 05:52:04 +03:00
input_sync ( dev ) ;
}
}
2005-04-17 02:20:36 +04:00
/*
* Multisystem joystick support
*/
# define GC_MULTI_LENGTH 5 /* Multi system joystick packet length is 5 */
# define GC_MULTI2_LENGTH 6 /* One more bit for one more button */
/*
* gc_multi_read_packet ( ) reads a Multisystem joystick packet .
*/
static void gc_multi_read_packet ( struct gc * gc , int length , unsigned char * data )
{
int i ;
for ( i = 0 ; i < length ; i + + ) {
parport_write_data ( gc - > pd - > port , ~ ( 1 < < i ) ) ;
data [ i ] = parport_read_status ( gc - > pd - > port ) ^ 0x7f ;
}
}
2006-01-30 05:52:04 +03:00
static void gc_multi_process_packet ( struct gc * gc )
{
unsigned char data [ GC_MULTI2_LENGTH ] ;
2010-02-22 07:54:28 +03:00
int data_len = gc - > pads [ GC_MULTI2 ] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH ;
2006-01-30 05:52:04 +03:00
struct input_dev * dev ;
int i , s ;
2010-02-22 07:54:28 +03:00
gc_multi_read_packet ( gc , data_len , data ) ;
2006-01-30 05:52:04 +03:00
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + ) {
dev = gc - > dev [ i ] ;
if ( ! dev )
continue ;
s = gc_status_bit [ i ] ;
if ( s & ( gc - > pads [ GC_MULTI ] | gc - > pads [ GC_MULTI2 ] ) ) {
2010-02-22 07:54:28 +03:00
input_report_abs ( dev , ABS_X ,
! ( s & data [ 2 ] ) - ! ( s & data [ 3 ] ) ) ;
input_report_abs ( dev , ABS_Y ,
! ( s & data [ 0 ] ) - ! ( s & data [ 1 ] ) ) ;
2006-01-30 05:52:04 +03:00
input_report_key ( dev , BTN_TRIGGER , s & data [ 4 ] ) ;
}
if ( s & gc - > pads [ GC_MULTI2 ] )
input_report_key ( dev , BTN_THUMB , s & data [ 5 ] ) ;
input_sync ( dev ) ;
}
}
2005-04-17 02:20:36 +04:00
/*
* PSX support
*
* See documentation at :
* http : //www.dim.com/~mackys/psxmemcard/ps-eng2.txt
* http : //www.gamesx.com/controldata/psxcont/psxcont.htm
* ftp : //milano.usal.es/pablo/
*
*/
# define GC_PSX_DELAY 25 /* 25 usec */
# define GC_PSX_LENGTH 8 /* talk to the controller in bits */
# define GC_PSX_BYTES 6 /* the maximum number of bytes to read off the controller */
# define GC_PSX_MOUSE 1 /* Mouse */
# define GC_PSX_NEGCON 2 /* NegCon */
# define GC_PSX_NORMAL 4 /* Digital / Analog or Rumble in Digital mode */
# define GC_PSX_ANALOG 5 /* Analog in Analog mode / Rumble in Green mode */
# define GC_PSX_RUMBLE 7 /* Rumble in Red mode */
# define GC_PSX_CLOCK 0x04 /* Pin 4 */
# define GC_PSX_COMMAND 0x01 /* Pin 2 */
# define GC_PSX_POWER 0xf8 /* Pins 5-9 */
# define GC_PSX_SELECT 0x02 /* Pin 3 */
# define GC_PSX_ID(x) ((x) >> 4) /* High nibble is device type */
# define GC_PSX_LEN(x) (((x) & 0xf) << 1) /* Low nibble is length in bytes/2 */
static int gc_psx_delay = GC_PSX_DELAY ;
module_param_named ( psx_delay , gc_psx_delay , uint , 0 ) ;
MODULE_PARM_DESC ( psx_delay , " Delay when accessing Sony PSX controller (usecs) " ) ;
2010-02-22 07:54:28 +03:00
static short gc_psx_abs [ ] = {
ABS_X , ABS_Y , ABS_RX , ABS_RY , ABS_HAT0X , ABS_HAT0Y
} ;
static short gc_psx_btn [ ] = {
BTN_TL , BTN_TR , BTN_TL2 , BTN_TR2 , BTN_A , BTN_B , BTN_X , BTN_Y ,
BTN_START , BTN_SELECT , BTN_THUMBL , BTN_THUMBR
} ;
2005-04-17 02:20:36 +04:00
static short gc_psx_ddr_btn [ ] = { BTN_0 , BTN_1 , BTN_2 , BTN_3 } ;
/*
* gc_psx_command ( ) writes 8 bit command and reads 8 bit data from
* the psx pad .
*/
2010-02-22 07:54:28 +03:00
static void gc_psx_command ( struct gc * gc , int b , unsigned char * data )
2005-04-17 02:20:36 +04:00
{
2010-02-22 07:54:28 +03:00
struct parport * port = gc - > pd - > port ;
2005-04-17 02:20:36 +04:00
int i , j , cmd , read ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
memset ( data , 0 , GC_MAX_DEVICES ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < GC_PSX_LENGTH ; i + + , b > > = 1 ) {
cmd = ( b & 1 ) ? GC_PSX_COMMAND : 0 ;
2010-02-22 07:54:28 +03:00
parport_write_data ( port , cmd | GC_PSX_POWER ) ;
2005-04-17 02:20:36 +04:00
udelay ( gc_psx_delay ) ;
2010-02-22 07:54:28 +03:00
read = parport_read_status ( port ) ^ 0x80 ;
2006-01-30 05:52:04 +03:00
for ( j = 0 ; j < GC_MAX_DEVICES ; j + + )
2005-04-17 02:20:36 +04:00
data [ j ] | = ( read & gc_status_bit [ j ] & ( gc - > pads [ GC_PSX ] | gc - > pads [ GC_DDR ] ) ) ? ( 1 < < i ) : 0 ;
parport_write_data ( gc - > pd - > port , cmd | GC_PSX_CLOCK | GC_PSX_POWER ) ;
udelay ( gc_psx_delay ) ;
}
}
/*
* gc_psx_read_packet ( ) reads a whole psx packet and returns
* device identifier code .
*/
2006-01-30 05:52:04 +03:00
static void gc_psx_read_packet ( struct gc * gc , unsigned char data [ GC_MAX_DEVICES ] [ GC_PSX_BYTES ] ,
unsigned char id [ GC_MAX_DEVICES ] )
2005-04-17 02:20:36 +04:00
{
int i , j , max_len = 0 ;
unsigned long flags ;
2006-01-30 05:52:04 +03:00
unsigned char data2 [ GC_MAX_DEVICES ] ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
/* Select pad */
parport_write_data ( gc - > pd - > port , GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER ) ;
2005-04-17 02:20:36 +04:00
udelay ( gc_psx_delay ) ;
2010-02-22 07:54:28 +03:00
/* Deselect, begin command */
parport_write_data ( gc - > pd - > port , GC_PSX_CLOCK | GC_PSX_POWER ) ;
2005-04-17 02:20:36 +04:00
udelay ( gc_psx_delay ) ;
local_irq_save ( flags ) ;
2010-02-22 07:54:28 +03:00
gc_psx_command ( gc , 0x01 , data2 ) ; /* Access pad */
gc_psx_command ( gc , 0x42 , id ) ; /* Get device ids */
gc_psx_command ( gc , 0 , data2 ) ; /* Dump status */
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
/* Find the longest pad */
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + )
if ( ( gc_status_bit [ i ] & ( gc - > pads [ GC_PSX ] | gc - > pads [ GC_DDR ] ) ) & &
GC_PSX_LEN ( id [ i ] ) > max_len & &
GC_PSX_LEN ( id [ i ] ) < = GC_PSX_BYTES ) {
2005-04-17 02:20:36 +04:00
max_len = GC_PSX_LEN ( id [ i ] ) ;
2010-02-22 07:54:28 +03:00
}
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
/* Read in all the data */
for ( i = 0 ; i < max_len ; i + + ) {
2005-04-17 02:20:36 +04:00
gc_psx_command ( gc , 0 , data2 ) ;
2006-01-30 05:52:04 +03:00
for ( j = 0 ; j < GC_MAX_DEVICES ; j + + )
2005-04-17 02:20:36 +04:00
data [ j ] [ i ] = data2 [ j ] ;
}
local_irq_restore ( flags ) ;
parport_write_data ( gc - > pd - > port , GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER ) ;
2010-02-22 07:54:28 +03:00
/* Set id's to the real value */
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + )
2005-04-17 02:20:36 +04:00
id [ i ] = GC_PSX_ID ( id [ i ] ) ;
}
2010-02-22 07:54:28 +03:00
static void gc_psx_report_one ( struct gc * gc , struct input_dev * dev ,
unsigned char pad_type , unsigned char status_bit ,
unsigned char * data )
2006-01-30 05:52:04 +03:00
{
2010-02-22 07:54:28 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
switch ( pad_type ) {
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
case GC_PSX_RUMBLE :
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
input_report_key ( dev , BTN_THUMBL , ~ data [ 0 ] & 0x04 ) ;
input_report_key ( dev , BTN_THUMBR , ~ data [ 0 ] & 0x02 ) ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
case GC_PSX_NEGCON :
case GC_PSX_ANALOG :
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
if ( gc - > pads [ GC_DDR ] & status_bit ) {
for ( i = 0 ; i < 4 ; i + + )
input_report_key ( dev , gc_psx_ddr_btn [ i ] ,
~ data [ 0 ] & ( 0x10 < < i ) ) ;
} else {
for ( i = 0 ; i < 4 ; i + + )
input_report_abs ( dev , gc_psx_abs [ i + 2 ] ,
data [ i + 2 ] ) ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
input_report_abs ( dev , ABS_X , 128 + ! ( data [ 0 ] & 0x20 ) * 127 - ! ( data [ 0 ] & 0x80 ) * 128 ) ;
input_report_abs ( dev , ABS_Y , 128 + ! ( data [ 0 ] & 0x40 ) * 127 - ! ( data [ 0 ] & 0x10 ) * 128 ) ;
}
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
for ( i = 0 ; i < 8 ; i + + )
input_report_key ( dev , gc_psx_btn [ i ] , ~ data [ 1 ] & ( 1 < < i ) ) ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
input_report_key ( dev , BTN_START , ~ data [ 0 ] & 0x08 ) ;
input_report_key ( dev , BTN_SELECT , ~ data [ 0 ] & 0x01 ) ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
input_sync ( dev ) ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
break ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
case GC_PSX_NORMAL :
if ( gc - > pads [ GC_DDR ] & status_bit ) {
for ( i = 0 ; i < 4 ; i + + )
input_report_key ( dev , gc_psx_ddr_btn [ i ] ,
~ data [ 0 ] & ( 0x10 < < i ) ) ;
} else {
input_report_abs ( dev , ABS_X , 128 + ! ( data [ 0 ] & 0x20 ) * 127 - ! ( data [ 0 ] & 0x80 ) * 128 ) ;
input_report_abs ( dev , ABS_Y , 128 + ! ( data [ 0 ] & 0x40 ) * 127 - ! ( data [ 0 ] & 0x10 ) * 128 ) ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
/*
* For some reason if the extra axes are left unset
* they drift .
* for ( i = 0 ; i < 4 ; i + + )
input_report_abs ( dev , gc_psx_abs [ i + 2 ] , 128 ) ;
* This needs to be debugged properly ,
* maybe fuzz processing needs to be done
* in input_sync ( )
* - - vojtech
*/
}
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
for ( i = 0 ; i < 8 ; i + + )
input_report_key ( dev , gc_psx_btn [ i ] , ~ data [ 1 ] & ( 1 < < i ) ) ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
input_report_key ( dev , BTN_START , ~ data [ 0 ] & 0x08 ) ;
input_report_key ( dev , BTN_SELECT , ~ data [ 0 ] & 0x01 ) ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
input_sync ( dev ) ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
break ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
case 0 : /* not a pad, ignore */
break ;
}
}
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
static void gc_psx_process_packet ( struct gc * gc )
{
unsigned char data [ GC_MAX_DEVICES ] [ GC_PSX_BYTES ] ;
unsigned char id [ GC_MAX_DEVICES ] ;
int i ;
2005-04-17 02:20:36 +04:00
2010-02-22 07:54:28 +03:00
gc_psx_read_packet ( gc , data , id ) ;
2006-01-30 05:52:04 +03:00
2010-02-22 07:54:28 +03:00
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + ) {
if ( gc - > dev [ i ] )
gc_psx_report_one ( gc , gc - > dev [ i ] ,
id [ i ] , gc_status_bit [ i ] , data [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-30 05:52:04 +03:00
}
2005-04-17 02:20:36 +04:00
/*
2006-01-30 05:52:04 +03:00
* gc_timer ( ) initiates reads of console pads data .
2005-04-17 02:20:36 +04:00
*/
2006-01-30 05:52:04 +03:00
static void gc_timer ( unsigned long private )
{
struct gc * gc = ( void * ) private ;
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:04 +03:00
/*
* N64 pads - must be read first , any read confuses them for 200 us
*/
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:04 +03:00
if ( gc - > pads [ GC_N64 ] )
gc_n64_process_packet ( gc ) ;
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:04 +03:00
/*
2006-04-02 09:10:05 +04:00
* NES and SNES pads or mouse
2006-01-30 05:52:04 +03:00
*/
2005-04-17 02:20:36 +04:00
2006-04-02 09:10:05 +04:00
if ( gc - > pads [ GC_NES ] | | gc - > pads [ GC_SNES ] | | gc - > pads [ GC_SNESMOUSE ] )
2006-01-30 05:52:04 +03:00
gc_nes_process_packet ( gc ) ;
2005-04-17 02:20:36 +04:00
/*
* Multi and Multi2 joysticks
*/
2006-01-30 05:52:04 +03:00
if ( gc - > pads [ GC_MULTI ] | | gc - > pads [ GC_MULTI2 ] )
gc_multi_process_packet ( gc ) ;
2005-04-17 02:20:36 +04:00
/*
* PSX controllers
*/
2006-01-30 05:52:04 +03:00
if ( gc - > pads [ GC_PSX ] | | gc - > pads [ GC_DDR ] )
gc_psx_process_packet ( gc ) ;
2005-04-17 02:20:36 +04:00
mod_timer ( & gc - > timer , jiffies + GC_REFRESH_TIME ) ;
}
static int gc_open ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct gc * gc = input_get_drvdata ( dev ) ;
2005-05-29 11:29:52 +04:00
int err ;
2006-02-19 08:22:30 +03:00
err = mutex_lock_interruptible ( & gc - > mutex ) ;
2005-05-29 11:29:52 +04:00
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
if ( ! gc - > used + + ) {
parport_claim ( gc - > pd ) ;
parport_write_control ( gc - > pd - > port , 0x04 ) ;
mod_timer ( & gc - > timer , jiffies + GC_REFRESH_TIME ) ;
}
2005-05-29 11:29:52 +04:00
2006-02-19 08:22:30 +03:00
mutex_unlock ( & gc - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void gc_close ( struct input_dev * dev )
{
2007-04-12 09:34:14 +04:00
struct gc * gc = input_get_drvdata ( dev ) ;
2005-05-29 11:29:52 +04:00
2006-02-19 08:22:30 +03:00
mutex_lock ( & gc - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ! - - gc - > used ) {
2005-05-29 11:29:52 +04:00
del_timer_sync ( & gc - > timer ) ;
2005-04-17 02:20:36 +04:00
parport_write_control ( gc - > pd - > port , 0x00 ) ;
parport_release ( gc - > pd ) ;
}
2006-02-19 08:22:30 +03:00
mutex_unlock ( & gc - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:52 +04:00
static int __init gc_setup_pad ( struct gc * gc , int idx , int pad_type )
2005-04-17 02:20:36 +04:00
{
2005-09-15 11:01:52 +04:00
struct input_dev * input_dev ;
int i ;
2010-02-22 07:53:55 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
if ( ! pad_type )
return 0 ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
if ( pad_type < 1 | | pad_type > GC_MAX ) {
printk ( KERN_WARNING " gamecon.c: Pad type %d unknown \n " , pad_type ) ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:52 +04:00
gc - > dev [ idx ] = input_dev = input_allocate_device ( ) ;
if ( ! input_dev ) {
printk ( KERN_ERR " gamecon.c: Not enough memory for input device \n " ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:52 +04:00
input_dev - > name = gc_names [ pad_type ] ;
input_dev - > phys = gc - > phys [ idx ] ;
input_dev - > id . bustype = BUS_PARPORT ;
input_dev - > id . vendor = 0x0001 ;
input_dev - > id . product = pad_type ;
input_dev - > id . version = 0x0100 ;
2007-04-12 09:34:14 +04:00
input_set_drvdata ( input_dev , gc ) ;
2005-09-15 11:01:52 +04:00
input_dev - > open = gc_open ;
input_dev - > close = gc_close ;
2006-04-02 09:10:05 +04:00
if ( pad_type ! = GC_SNESMOUSE ) {
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
2006-04-02 09:10:05 +04:00
for ( i = 0 ; i < 2 ; i + + )
input_set_abs_params ( input_dev , ABS_X + i , - 1 , 1 , 0 , 0 ) ;
} else
2007-10-19 10:40:32 +04:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REL ) ;
2005-09-15 11:01:52 +04:00
gc - > pads [ 0 ] | = gc_status_bit [ idx ] ;
gc - > pads [ pad_type ] | = gc_status_bit [ idx ] ;
switch ( pad_type ) {
2010-02-22 07:54:28 +03:00
case GC_N64 :
for ( i = 0 ; i < 10 ; i + + )
__set_bit ( gc_n64_btn [ i ] , input_dev - > keybit ) ;
2006-04-02 09:10:05 +04:00
2010-02-22 07:54:28 +03:00
for ( i = 0 ; i < 2 ; i + + ) {
input_set_abs_params ( input_dev , ABS_X + i , - 127 , 126 , 0 , 2 ) ;
input_set_abs_params ( input_dev , ABS_HAT0X + i , - 1 , 1 , 0 , 0 ) ;
}
2005-09-15 11:01:52 +04:00
2010-02-22 07:54:28 +03:00
err = gc_n64_init_ff ( input_dev , idx ) ;
if ( err ) {
printk ( KERN_WARNING " gamecon.c: Failed to initiate rumble for N64 device %d \n " , idx ) ;
input_free_device ( input_dev ) ;
return err ;
}
2005-09-15 11:01:52 +04:00
2010-02-22 07:54:28 +03:00
break ;
case GC_SNESMOUSE :
__set_bit ( BTN_LEFT , input_dev - > keybit ) ;
__set_bit ( BTN_RIGHT , input_dev - > keybit ) ;
__set_bit ( REL_X , input_dev - > relbit ) ;
__set_bit ( REL_Y , input_dev - > relbit ) ;
break ;
case GC_SNES :
for ( i = 4 ; i < 8 ; i + + )
__set_bit ( gc_snes_btn [ i ] , input_dev - > keybit ) ;
case GC_NES :
for ( i = 0 ; i < 4 ; i + + )
__set_bit ( gc_snes_btn [ i ] , input_dev - > keybit ) ;
break ;
case GC_MULTI2 :
__set_bit ( BTN_THUMB , input_dev - > keybit ) ;
case GC_MULTI :
__set_bit ( BTN_TRIGGER , input_dev - > keybit ) ;
break ;
case GC_PSX :
for ( i = 0 ; i < 6 ; i + + )
input_set_abs_params ( input_dev ,
gc_psx_abs [ i ] , 4 , 252 , 0 , 2 ) ;
for ( i = 0 ; i < 12 ; i + + )
__set_bit ( gc_psx_btn [ i ] , input_dev - > keybit ) ;
break ;
case GC_DDR :
for ( i = 0 ; i < 4 ; i + + )
__set_bit ( gc_psx_ddr_btn [ i ] , input_dev - > keybit ) ;
for ( i = 0 ; i < 12 ; i + + )
__set_bit ( gc_psx_btn [ i ] , input_dev - > keybit ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2005-05-29 11:29:52 +04:00
2005-09-15 11:01:52 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
static struct gc __init * gc_probe ( int parport , int * pads , int n_pads )
{
struct gc * gc ;
struct parport * pp ;
struct pardevice * pd ;
int i ;
int err ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
pp = parport_find_number ( parport ) ;
if ( ! pp ) {
printk ( KERN_ERR " gamecon.c: no such parport \n " ) ;
err = - EINVAL ;
goto err_out ;
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
pd = parport_register_device ( pp , " gamecon " , NULL , NULL , NULL , PARPORT_DEV_EXCL , NULL ) ;
if ( ! pd ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " gamecon.c: parport busy already - lp.o loaded? \n " ) ;
2005-09-15 11:01:52 +04:00
err = - EBUSY ;
goto err_put_pp ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:52 +04:00
gc = kzalloc ( sizeof ( struct gc ) , GFP_KERNEL ) ;
if ( ! gc ) {
printk ( KERN_ERR " gamecon.c: Not enough memory \n " ) ;
err = - ENOMEM ;
goto err_unreg_pardev ;
}
2005-04-17 02:20:36 +04:00
2006-02-19 08:22:30 +03:00
mutex_init ( & gc - > mutex ) ;
2005-09-15 11:01:52 +04:00
gc - > pd = pd ;
2010-02-22 07:54:28 +03:00
setup_timer ( & gc - > timer , gc_timer , ( long ) gc ) ;
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:04 +03:00
for ( i = 0 ; i < n_pads & & i < GC_MAX_DEVICES ; i + + ) {
2005-09-15 11:01:52 +04:00
if ( ! pads [ i ] )
2005-04-17 02:20:36 +04:00
continue ;
2006-06-26 09:45:48 +04:00
snprintf ( gc - > phys [ i ] , sizeof ( gc - > phys [ i ] ) ,
" %s/input%d " , gc - > pd - > port - > name , i ) ;
2005-09-15 11:01:52 +04:00
err = gc_setup_pad ( gc , i , pads [ i ] ) ;
if ( err )
2006-01-30 05:52:11 +03:00
goto err_unreg_devs ;
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:11 +03:00
err = input_register_device ( gc - > dev [ i ] ) ;
if ( err )
goto err_free_dev ;
2005-09-15 11:01:52 +04:00
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
if ( ! gc - > pads [ 0 ] ) {
printk ( KERN_ERR " gamecon.c: No valid devices specified \n " ) ;
err = - EINVAL ;
goto err_free_gc ;
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
parport_put_port ( pp ) ;
return gc ;
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:11 +03:00
err_free_dev :
input_free_device ( gc - > dev [ i ] ) ;
err_unreg_devs :
2005-09-15 11:01:52 +04:00
while ( - - i > = 0 )
2006-01-30 05:52:11 +03:00
if ( gc - > dev [ i ] )
input_unregister_device ( gc - > dev [ i ] ) ;
2005-09-15 11:01:52 +04:00
err_free_gc :
kfree ( gc ) ;
err_unreg_pardev :
parport_unregister_device ( pd ) ;
err_put_pp :
parport_put_port ( pp ) ;
err_out :
return ERR_PTR ( err ) ;
}
2005-04-17 02:20:36 +04:00
2006-01-30 05:52:11 +03:00
static void gc_remove ( struct gc * gc )
2005-09-15 11:01:52 +04:00
{
int i ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
for ( i = 0 ; i < GC_MAX_DEVICES ; i + + )
if ( gc - > dev [ i ] )
input_unregister_device ( gc - > dev [ i ] ) ;
parport_unregister_device ( gc - > pd ) ;
kfree ( gc ) ;
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
static int __init gc_init ( void )
{
int i ;
int have_dev = 0 ;
int err = 0 ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
for ( i = 0 ; i < GC_MAX_PORTS ; i + + ) {
2007-05-03 08:52:51 +04:00
if ( gc_cfg [ i ] . nargs = = 0 | | gc_cfg [ i ] . args [ 0 ] < 0 )
2005-09-15 11:01:52 +04:00
continue ;
2005-04-17 02:20:36 +04:00
2007-05-03 08:52:51 +04:00
if ( gc_cfg [ i ] . nargs < 2 ) {
2005-09-15 11:01:52 +04:00
printk ( KERN_ERR " gamecon.c: at least one device must be specified \n " ) ;
err = - EINVAL ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-05-03 08:52:51 +04:00
gc_base [ i ] = gc_probe ( gc_cfg [ i ] . args [ 0 ] ,
gc_cfg [ i ] . args + 1 , gc_cfg [ i ] . nargs - 1 ) ;
2005-09-15 11:01:52 +04:00
if ( IS_ERR ( gc_base [ i ] ) ) {
err = PTR_ERR ( gc_base [ i ] ) ;
break ;
}
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:52 +04:00
have_dev = 1 ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:52 +04:00
if ( err ) {
while ( - - i > = 0 )
2006-01-30 05:52:11 +03:00
if ( gc_base [ i ] )
gc_remove ( gc_base [ i ] ) ;
2005-09-15 11:01:52 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:52 +04:00
return have_dev ? 0 : - ENODEV ;
2005-04-17 02:20:36 +04:00
}
static void __exit gc_exit ( void )
{
2005-09-15 11:01:52 +04:00
int i ;
for ( i = 0 ; i < GC_MAX_PORTS ; i + + )
if ( gc_base [ i ] )
gc_remove ( gc_base [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( gc_init ) ;
module_exit ( gc_exit ) ;