2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* Copyright ( c ) 1999 - 2001 Vojtech Pavlik
* Copyright ( c ) 1999 Brian Gerst
*/
/*
* NS558 based standard IBM game port driver for Linux
*/
/*
*/
# include <asm/io.h>
# include <linux/module.h>
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/gameport.h>
# include <linux/slab.h>
# include <linux/pnp.h>
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " Classic gameport (ISA/PnP) driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int ns558_isa_portlist [ ] = { 0x201 , 0x200 , 0x202 , 0x203 , 0x204 , 0x205 , 0x207 , 0x209 ,
0x20b , 0x20c , 0x20e , 0x20f , 0x211 , 0x219 , 0x101 , 0 } ;
struct ns558 {
int type ;
int io ;
int size ;
struct pnp_dev * dev ;
struct gameport * gameport ;
struct list_head node ;
} ;
static LIST_HEAD ( ns558_list ) ;
/*
* ns558_isa_probe ( ) tries to find an isa gameport at the
* specified address , and also checks for mirrors .
* A joystick must be attached for this to work .
*/
static int ns558_isa_probe ( int io )
{
int i , j , b ;
unsigned char c , u , v ;
struct ns558 * ns558 ;
struct gameport * port ;
/*
* No one should be using this address .
*/
if ( ! request_region ( io , 1 , " ns558-isa " ) )
return - EBUSY ;
/*
* We must not be able to write arbitrary values to the port .
* The lower two axis bits must be 1 after a write .
*/
c = inb ( io ) ;
outb ( ~ c & ~ 3 , io ) ;
if ( ~ ( u = v = inb ( io ) ) & 3 ) {
outb ( c , io ) ;
release_region ( io , 1 ) ;
return - ENODEV ;
}
/*
* After a trigger , there must be at least some bits changing .
*/
for ( i = 0 ; i < 1000 ; i + + ) v & = inb ( io ) ;
if ( u = = v ) {
outb ( c , io ) ;
release_region ( io , 1 ) ;
return - ENODEV ;
}
msleep ( 3 ) ;
/*
* After some time ( 4 ms ) the axes shouldn ' t change anymore .
*/
u = inb ( io ) ;
for ( i = 0 ; i < 1000 ; i + + )
if ( ( u ^ inb ( io ) ) & 0xf ) {
outb ( c , io ) ;
release_region ( io , 1 ) ;
return - ENODEV ;
}
/*
* And now find the number of mirrors of the port .
*/
for ( i = 1 ; i < 5 ; i + + ) {
release_region ( io & ( - 1 < < ( i - 1 ) ) , ( 1 < < ( i - 1 ) ) ) ;
if ( ! request_region ( io & ( - 1 < < i ) , ( 1 < < i ) , " ns558-isa " ) )
break ; /* Don't disturb anyone */
outb ( 0xff , io & ( - 1 < < i ) ) ;
for ( j = b = 0 ; j < 1000 ; j + + )
if ( inb ( io & ( - 1 < < i ) ) ! = inb ( ( io & ( - 1 < < i ) ) + ( 1 < < i ) - 1 ) ) b + + ;
msleep ( 3 ) ;
if ( b > 300 ) { /* We allow 30% difference */
release_region ( io & ( - 1 < < i ) , ( 1 < < i ) ) ;
break ;
}
}
i - - ;
if ( i ! = 4 ) {
if ( ! request_region ( io & ( - 1 < < i ) , ( 1 < < i ) , " ns558-isa " ) )
return - EBUSY ;
}
2005-09-06 15:18:33 -07:00
ns558 = kzalloc ( sizeof ( struct ns558 ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
port = gameport_allocate_port ( ) ;
if ( ! ns558 | | ! port ) {
printk ( KERN_ERR " ns558: Memory allocation failed. \n " ) ;
release_region ( io & ( - 1 < < i ) , ( 1 < < i ) ) ;
kfree ( ns558 ) ;
gameport_free_port ( port ) ;
return - ENOMEM ;
}
ns558 - > io = io ;
ns558 - > size = 1 < < i ;
ns558 - > gameport = port ;
port - > io = io ;
gameport_set_name ( port , " NS558 ISA Gameport " ) ;
gameport_set_phys ( port , " isa%04x/gameport0 " , io & ( - 1 < < i ) ) ;
gameport_register_port ( port ) ;
list_add ( & ns558 - > node , & ns558_list ) ;
return 0 ;
}
# ifdef CONFIG_PNP
2010-01-09 23:24:48 -08:00
static const struct pnp_device_id pnp_devids [ ] = {
2005-04-16 15:20:36 -07:00
{ . id = " @P@0001 " , . driver_data = 0 } , /* ALS 100 */
{ . id = " @P@0020 " , . driver_data = 0 } , /* ALS 200 */
{ . id = " @P@1001 " , . driver_data = 0 } , /* ALS 100+ */
{ . id = " @P@2001 " , . driver_data = 0 } , /* ALS 120 */
{ . id = " ASB16fd " , . driver_data = 0 } , /* AdLib NSC16 */
{ . id = " AZT3001 " , . driver_data = 0 } , /* AZT1008 */
{ . id = " CDC0001 " , . driver_data = 0 } , /* Opl3-SAx */
{ . id = " CSC0001 " , . driver_data = 0 } , /* CS4232 */
{ . id = " CSC000f " , . driver_data = 0 } , /* CS4236 */
{ . id = " CSC0101 " , . driver_data = 0 } , /* CS4327 */
{ . id = " CTL7001 " , . driver_data = 0 } , /* SB16 */
{ . id = " CTL7002 " , . driver_data = 0 } , /* AWE64 */
{ . id = " CTL7005 " , . driver_data = 0 } , /* Vibra16 */
{ . id = " ENS2020 " , . driver_data = 0 } , /* SoundscapeVIVO */
{ . id = " ESS0001 " , . driver_data = 0 } , /* ES1869 */
{ . id = " ESS0005 " , . driver_data = 0 } , /* ES1878 */
{ . id = " ESS6880 " , . driver_data = 0 } , /* ES688 */
{ . id = " IBM0012 " , . driver_data = 0 } , /* CS4232 */
{ . id = " OPT0001 " , . driver_data = 0 } , /* OPTi Audio16 */
{ . id = " YMH0006 " , . driver_data = 0 } , /* Opl3-SA */
{ . id = " YMH0022 " , . driver_data = 0 } , /* Opl3-SAx */
{ . id = " PNPb02f " , . driver_data = 0 } , /* Generic */
{ . id = " " , } ,
} ;
MODULE_DEVICE_TABLE ( pnp , pnp_devids ) ;
static int ns558_pnp_probe ( struct pnp_dev * dev , const struct pnp_device_id * did )
{
int ioport , iolen ;
struct ns558 * ns558 ;
struct gameport * port ;
if ( ! pnp_port_valid ( dev , 0 ) ) {
printk ( KERN_WARNING " ns558: No i/o ports on a gameport? Weird \n " ) ;
return - ENODEV ;
}
ioport = pnp_port_start ( dev , 0 ) ;
iolen = pnp_port_len ( dev , 0 ) ;
if ( ! request_region ( ioport , iolen , " ns558-pnp " ) )
return - EBUSY ;
2005-09-06 15:18:33 -07:00
ns558 = kzalloc ( sizeof ( struct ns558 ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
port = gameport_allocate_port ( ) ;
if ( ! ns558 | | ! port ) {
printk ( KERN_ERR " ns558: Memory allocation failed \n " ) ;
kfree ( ns558 ) ;
gameport_free_port ( port ) ;
return - ENOMEM ;
}
ns558 - > io = ioport ;
ns558 - > size = iolen ;
ns558 - > dev = dev ;
ns558 - > gameport = port ;
gameport_set_name ( port , " NS558 PnP Gameport " ) ;
2008-10-30 00:07:50 -04:00
gameport_set_phys ( port , " pnp%s/gameport0 " , dev_name ( & dev - > dev ) ) ;
2005-04-16 15:20:36 -07:00
port - > dev . parent = & dev - > dev ;
port - > io = ioport ;
gameport_register_port ( port ) ;
list_add_tail ( & ns558 - > node , & ns558_list ) ;
return 0 ;
}
static struct pnp_driver ns558_pnp_driver = {
. name = " ns558 " ,
. id_table = pnp_devids ,
. probe = ns558_pnp_probe ,
} ;
# else
static struct pnp_driver ns558_pnp_driver ;
# endif
static int __init ns558_init ( void )
{
int i = 0 ;
2006-03-14 00:12:08 -05:00
int error ;
2005-04-16 15:20:36 -07:00
2006-03-14 00:12:08 -05:00
error = pnp_register_driver ( & ns558_pnp_driver ) ;
if ( error & & error ! = - ENODEV ) /* should be ENOSYS really */
return error ;
2005-05-29 02:25:01 -05:00
2005-04-16 15:20:36 -07:00
/*
2005-05-29 02:25:01 -05:00
* Probe ISA ports after PnP , so that PnP ports that are already
* enabled get detected as PnP . This may be suboptimal in multi - device
* configurations , but saves hassle with simple setups .
2005-04-16 15:20:36 -07:00
*/
while ( ns558_isa_portlist [ i ] )
ns558_isa_probe ( ns558_isa_portlist [ i + + ] ) ;
2006-03-14 00:12:08 -05:00
return list_empty ( & ns558_list ) & & error ? - ENODEV : 0 ;
2005-04-16 15:20:36 -07:00
}
static void __exit ns558_exit ( void )
{
2005-08-10 10:11:36 -07:00
struct ns558 * ns558 , * safe ;
2005-04-16 15:20:36 -07:00
2005-08-10 10:11:36 -07:00
list_for_each_entry_safe ( ns558 , safe , & ns558_list , node ) {
2005-04-16 15:20:36 -07:00
gameport_unregister_port ( ns558 - > gameport ) ;
release_region ( ns558 - > io & ~ ( ns558 - > size - 1 ) , ns558 - > size ) ;
kfree ( ns558 ) ;
}
2006-03-14 00:12:08 -05:00
pnp_unregister_driver ( & ns558_pnp_driver ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( ns558_init ) ;
module_exit ( ns558_exit ) ;