2005-04-17 02:20:36 +04:00
/*
* Generic gameport layer
*
* Copyright ( c ) 1999 - 2002 Vojtech Pavlik
* Copyright ( c ) 2005 Dmitry Torokhov
*/
/*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*/
2010-01-06 04:56:03 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/stddef.h>
# include <linux/module.h>
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/gameport.h>
# include <linux/slab.h>
# include <linux/delay.h>
2010-11-15 12:39:57 +03:00
# include <linux/workqueue.h>
2005-10-31 02:03:48 +03:00
# include <linux/sched.h> /* HZ */
2006-02-19 08:22:03 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
/*#include <asm/io.h>*/
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " Generic gameport layer " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
2006-02-19 08:22:03 +03:00
* gameport_mutex protects entire gameport subsystem and is taken
2005-04-17 02:20:36 +04:00
* every time gameport port or driver registrered or unregistered .
*/
2006-02-19 08:22:03 +03:00
static DEFINE_MUTEX ( gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( gameport_list ) ;
2006-01-05 17:38:22 +03:00
static struct bus_type gameport_bus ;
2005-04-17 02:20:36 +04:00
static void gameport_add_port ( struct gameport * gameport ) ;
2009-04-14 02:27:49 +04:00
static void gameport_attach_driver ( struct gameport_driver * drv ) ;
2005-04-17 02:20:36 +04:00
static void gameport_reconnect_port ( struct gameport * gameport ) ;
static void gameport_disconnect_port ( struct gameport * gameport ) ;
# if defined(__i386__)
2005-06-30 13:58:55 +04:00
# include <asm/i8253.h>
2005-04-17 02:20:36 +04:00
# define DELTA(x,y) ((y)-(x)+((y)<(x)?1193182 / HZ:0))
# define GET_TIME(x) do { x = get_time_pit(); } while (0)
static unsigned int get_time_pit ( void )
{
unsigned long flags ;
unsigned int count ;
2010-02-17 19:47:10 +03:00
raw_spin_lock_irqsave ( & i8253_lock , flags ) ;
2005-04-17 02:20:36 +04:00
outb_p ( 0x00 , 0x43 ) ;
count = inb_p ( 0x40 ) ;
count | = inb_p ( 0x40 ) < < 8 ;
2010-02-17 19:47:10 +03:00
raw_spin_unlock_irqrestore ( & i8253_lock , flags ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
# endif
/*
* gameport_measure_speed ( ) measures the gameport i / o speed .
*/
static int gameport_measure_speed ( struct gameport * gameport )
{
# if defined(__i386__)
unsigned int i , t , t1 , t2 , t3 , tx ;
unsigned long flags ;
if ( gameport_open ( gameport , NULL , GAMEPORT_MODE_RAW ) )
return 0 ;
tx = 1 < < 30 ;
for ( i = 0 ; i < 50 ; i + + ) {
local_irq_save ( flags ) ;
GET_TIME ( t1 ) ;
for ( t = 0 ; t < 50 ; t + + ) gameport_read ( gameport ) ;
GET_TIME ( t2 ) ;
GET_TIME ( t3 ) ;
local_irq_restore ( flags ) ;
udelay ( i * 10 ) ;
if ( ( t = DELTA ( t2 , t1 ) - DELTA ( t3 , t2 ) ) < tx ) tx = t ;
}
gameport_close ( gameport ) ;
return 59659 / ( tx < 1 ? 1 : tx ) ;
# elif defined (__x86_64__)
unsigned int i , t ;
unsigned long tx , t1 , t2 , flags ;
if ( gameport_open ( gameport , NULL , GAMEPORT_MODE_RAW ) )
return 0 ;
tx = 1 < < 30 ;
for ( i = 0 ; i < 50 ; i + + ) {
local_irq_save ( flags ) ;
rdtscl ( t1 ) ;
for ( t = 0 ; t < 50 ; t + + ) gameport_read ( gameport ) ;
rdtscl ( t2 ) ;
local_irq_restore ( flags ) ;
udelay ( i * 10 ) ;
if ( t2 - t1 < tx ) tx = t2 - t1 ;
}
gameport_close ( gameport ) ;
2010-12-16 21:15:15 +03:00
return ( this_cpu_read ( cpu_info . loops_per_jiffy ) *
2007-10-19 22:35:04 +04:00
( unsigned long ) HZ / ( 1000 / 50 ) ) / ( tx < 1 ? 1 : tx ) ;
2005-04-17 02:20:36 +04:00
# else
unsigned int j , t = 0 ;
if ( gameport_open ( gameport , NULL , GAMEPORT_MODE_RAW ) )
return 0 ;
j = jiffies ; while ( j = = jiffies ) ;
j = jiffies ; while ( j = = jiffies ) { t + + ; gameport_read ( gameport ) ; }
gameport_close ( gameport ) ;
return t * HZ / 1000 ;
# endif
}
void gameport_start_polling ( struct gameport * gameport )
{
spin_lock ( & gameport - > timer_lock ) ;
if ( ! gameport - > poll_cnt + + ) {
BUG_ON ( ! gameport - > poll_handler ) ;
BUG_ON ( ! gameport - > poll_interval ) ;
mod_timer ( & gameport - > poll_timer , jiffies + msecs_to_jiffies ( gameport - > poll_interval ) ) ;
}
spin_unlock ( & gameport - > timer_lock ) ;
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_start_polling ) ;
2005-04-17 02:20:36 +04:00
void gameport_stop_polling ( struct gameport * gameport )
{
spin_lock ( & gameport - > timer_lock ) ;
if ( ! - - gameport - > poll_cnt )
del_timer ( & gameport - > poll_timer ) ;
spin_unlock ( & gameport - > timer_lock ) ;
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_stop_polling ) ;
2005-04-17 02:20:36 +04:00
static void gameport_run_poll_handler ( unsigned long d )
{
struct gameport * gameport = ( struct gameport * ) d ;
gameport - > poll_handler ( gameport ) ;
if ( gameport - > poll_cnt )
mod_timer ( & gameport - > poll_timer , jiffies + msecs_to_jiffies ( gameport - > poll_interval ) ) ;
}
/*
* Basic gameport - > driver core mappings
*/
2007-04-10 08:40:48 +04:00
static int gameport_bind_driver ( struct gameport * gameport , struct gameport_driver * drv )
2005-04-17 02:20:36 +04:00
{
2006-10-12 09:06:34 +04:00
int error ;
2005-04-17 02:20:36 +04:00
gameport - > dev . driver = & drv - > driver ;
if ( drv - > connect ( gameport , drv ) ) {
gameport - > dev . driver = NULL ;
2007-04-10 08:40:48 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2006-10-12 09:06:34 +04:00
error = device_bind_driver ( & gameport - > dev ) ;
if ( error ) {
2010-01-06 04:56:03 +03:00
dev_warn ( & gameport - > dev ,
" device_bind_driver() failed for %s (%s) and %s, error: %d \n " ,
2006-10-12 09:06:34 +04:00
gameport - > phys , gameport - > name ,
drv - > description , error ) ;
drv - > disconnect ( gameport ) ;
gameport - > dev . driver = NULL ;
2007-04-10 08:40:48 +04:00
return error ;
2006-10-12 09:06:34 +04:00
}
2007-04-10 08:40:48 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void gameport_find_driver ( struct gameport * gameport )
{
2006-07-19 09:14:55 +04:00
int error ;
error = device_attach ( & gameport - > dev ) ;
if ( error < 0 )
2010-01-06 04:56:03 +03:00
dev_warn ( & gameport - > dev ,
" device_attach() failed for %s (%s), error: %d \n " ,
gameport - > phys , gameport - > name , error ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Gameport event processing .
*/
enum gameport_event_type {
GAMEPORT_REGISTER_PORT ,
2008-06-06 09:33:22 +04:00
GAMEPORT_ATTACH_DRIVER ,
2005-04-17 02:20:36 +04:00
} ;
struct gameport_event {
enum gameport_event_type type ;
void * object ;
struct module * owner ;
struct list_head node ;
} ;
static DEFINE_SPINLOCK ( gameport_event_lock ) ; /* protects gameport_event_list */
static LIST_HEAD ( gameport_event_list ) ;
2010-11-15 12:39:57 +03:00
static struct gameport_event * gameport_get_event ( void )
2005-04-17 02:20:36 +04:00
{
2010-11-15 12:39:57 +03:00
struct gameport_event * event = NULL ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & gameport_event_lock , flags ) ;
2010-11-15 12:39:57 +03:00
if ( ! list_empty ( & gameport_event_list ) ) {
event = list_first_entry ( & gameport_event_list ,
struct gameport_event , node ) ;
list_del_init ( & event - > node ) ;
2005-04-17 02:20:36 +04:00
}
2008-06-06 09:33:22 +04:00
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & gameport_event_lock , flags ) ;
2010-11-15 12:39:57 +03:00
return event ;
2005-04-17 02:20:36 +04:00
}
static void gameport_free_event ( struct gameport_event * event )
{
module_put ( event - > owner ) ;
kfree ( event ) ;
}
static void gameport_remove_duplicate_events ( struct gameport_event * event )
{
2010-01-06 04:56:03 +03:00
struct gameport_event * e , * next ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & gameport_event_lock , flags ) ;
2010-01-06 04:56:03 +03:00
list_for_each_entry_safe ( e , next , & gameport_event_list , node ) {
2005-04-17 02:20:36 +04:00
if ( event - > object = = e - > object ) {
/*
* If this event is of different type we should not
* look further - we only suppress duplicate events
* that were sent back - to - back .
*/
if ( event - > type ! = e - > type )
break ;
2010-01-06 04:56:03 +03:00
list_del_init ( & e - > node ) ;
2005-04-17 02:20:36 +04:00
gameport_free_event ( e ) ;
}
}
spin_unlock_irqrestore ( & gameport_event_lock , flags ) ;
}
2010-11-15 12:39:57 +03:00
static void gameport_handle_events ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
struct gameport_event * event ;
2006-02-19 08:22:03 +03:00
mutex_lock ( & gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-20 08:56:43 +03:00
/*
* Note that we handle only one event here to give swsusp
* a chance to freeze kgameportd thread . Gameport events
* should be pretty rare so we are not concerned about
* taking performance hit .
*/
if ( ( event = gameport_get_event ( ) ) ) {
2005-04-17 02:20:36 +04:00
switch ( event - > type ) {
2010-01-06 04:56:03 +03:00
case GAMEPORT_REGISTER_PORT :
gameport_add_port ( event - > object ) ;
break ;
2005-04-17 02:20:36 +04:00
2010-01-06 04:56:03 +03:00
case GAMEPORT_ATTACH_DRIVER :
gameport_attach_driver ( event - > object ) ;
break ;
2005-04-17 02:20:36 +04:00
}
gameport_remove_duplicate_events ( event ) ;
gameport_free_event ( event ) ;
}
2006-02-19 08:22:03 +03:00
mutex_unlock ( & gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-15 12:39:57 +03:00
static DECLARE_WORK ( gameport_event_work , gameport_handle_events ) ;
static int gameport_queue_event ( void * object , struct module * owner ,
enum gameport_event_type event_type )
{
unsigned long flags ;
struct gameport_event * event ;
int retval = 0 ;
spin_lock_irqsave ( & gameport_event_lock , flags ) ;
/*
* Scan event list for the other events for the same gameport port ,
* starting with the most recent one . If event is the same we
* do not need add new one . If event is of different type we
* need to add this event and should not look further because
* we need to preserve sequence of distinct events .
*/
list_for_each_entry_reverse ( event , & gameport_event_list , node ) {
if ( event - > object = = object ) {
if ( event - > type = = event_type )
goto out ;
break ;
}
}
event = kmalloc ( sizeof ( struct gameport_event ) , GFP_ATOMIC ) ;
if ( ! event ) {
pr_err ( " Not enough memory to queue event %d \n " , event_type ) ;
retval = - ENOMEM ;
goto out ;
}
if ( ! try_module_get ( owner ) ) {
pr_warning ( " Can't get module reference, dropping event %d \n " ,
event_type ) ;
kfree ( event ) ;
retval = - EINVAL ;
goto out ;
}
event - > type = event_type ;
event - > object = object ;
event - > owner = owner ;
list_add_tail ( & event - > node , & gameport_event_list ) ;
2011-02-23 19:51:28 +03:00
queue_work ( system_long_wq , & gameport_event_work ) ;
2010-11-15 12:39:57 +03:00
out :
spin_unlock_irqrestore ( & gameport_event_lock , flags ) ;
return retval ;
}
2005-04-17 02:20:36 +04:00
/*
2008-06-06 09:33:22 +04:00
* Remove all events that have been submitted for a given object ,
* be it a gameport port or a driver .
2005-04-17 02:20:36 +04:00
*/
2008-06-06 09:33:22 +04:00
static void gameport_remove_pending_events ( void * object )
2005-04-17 02:20:36 +04:00
{
2010-01-06 04:56:03 +03:00
struct gameport_event * event , * next ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & gameport_event_lock , flags ) ;
2010-01-06 04:56:03 +03:00
list_for_each_entry_safe ( event , next , & gameport_event_list , node ) {
2008-06-06 09:33:22 +04:00
if ( event - > object = = object ) {
2010-01-06 04:56:03 +03:00
list_del_init ( & event - > node ) ;
2005-04-17 02:20:36 +04:00
gameport_free_event ( event ) ;
}
}
spin_unlock_irqrestore ( & gameport_event_lock , flags ) ;
}
/*
* Destroy child gameport port ( if any ) that has not been fully registered yet .
*
* Note that we rely on the fact that port can have only one child and therefore
* only one child registration request can be pending . Additionally , children
* are registered by driver ' s connect ( ) handler so there can ' t be a grandchild
* pending registration together with a child .
*/
static struct gameport * gameport_get_pending_child ( struct gameport * parent )
{
struct gameport_event * event ;
struct gameport * gameport , * child = NULL ;
unsigned long flags ;
spin_lock_irqsave ( & gameport_event_lock , flags ) ;
list_for_each_entry ( event , & gameport_event_list , node ) {
if ( event - > type = = GAMEPORT_REGISTER_PORT ) {
gameport = event - > object ;
if ( gameport - > parent = = parent ) {
child = gameport ;
break ;
}
}
}
spin_unlock_irqrestore ( & gameport_event_lock , flags ) ;
return child ;
}
/*
* Gameport port operations
*/
2005-05-17 14:42:58 +04:00
static ssize_t gameport_show_description ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct gameport * gameport = to_gameport_port ( dev ) ;
2010-01-06 04:56:03 +03:00
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %s \n " , gameport - > name ) ;
}
2005-05-17 14:42:58 +04:00
static ssize_t gameport_rebind_driver ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct gameport * gameport = to_gameport_port ( dev ) ;
struct device_driver * drv ;
2007-04-10 08:40:48 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2007-04-10 08:40:48 +04:00
error = mutex_lock_interruptible ( & gameport_mutex ) ;
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
if ( ! strncmp ( buf , " none " , count ) ) {
gameport_disconnect_port ( gameport ) ;
} else if ( ! strncmp ( buf , " reconnect " , count ) ) {
gameport_reconnect_port ( gameport ) ;
} else if ( ! strncmp ( buf , " rescan " , count ) ) {
gameport_disconnect_port ( gameport ) ;
gameport_find_driver ( gameport ) ;
} else if ( ( drv = driver_find ( buf , & gameport_bus ) ) ! = NULL ) {
gameport_disconnect_port ( gameport ) ;
2007-04-10 08:40:48 +04:00
error = gameport_bind_driver ( gameport , to_gameport_driver ( drv ) ) ;
2005-04-17 02:20:36 +04:00
put_driver ( drv ) ;
} else {
2007-04-10 08:40:48 +04:00
error = - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-02-19 08:22:03 +03:00
mutex_unlock ( & gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-04-10 08:40:48 +04:00
return error ? error : count ;
2005-04-17 02:20:36 +04:00
}
static struct device_attribute gameport_device_attrs [ ] = {
__ATTR ( description , S_IRUGO , gameport_show_description , NULL ) ,
__ATTR ( drvctl , S_IWUSR , NULL , gameport_rebind_driver ) ,
__ATTR_NULL
} ;
static void gameport_release_port ( struct device * dev )
{
struct gameport * gameport = to_gameport_port ( dev ) ;
kfree ( gameport ) ;
module_put ( THIS_MODULE ) ;
}
void gameport_set_phys ( struct gameport * gameport , const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
vsnprintf ( gameport - > phys , sizeof ( gameport - > phys ) , fmt , args ) ;
va_end ( args ) ;
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_set_phys ) ;
2005-04-17 02:20:36 +04:00
/*
* Prepare gameport port for registration .
*/
static void gameport_init_port ( struct gameport * gameport )
{
static atomic_t gameport_no = ATOMIC_INIT ( 0 ) ;
__module_get ( THIS_MODULE ) ;
2006-02-19 08:22:03 +03:00
mutex_init ( & gameport - > drv_mutex ) ;
2005-04-17 02:20:36 +04:00
device_initialize ( & gameport - > dev ) ;
2010-01-06 04:56:03 +03:00
dev_set_name ( & gameport - > dev , " gameport%lu " ,
( unsigned long ) atomic_inc_return ( & gameport_no ) - 1 ) ;
2005-04-17 02:20:36 +04:00
gameport - > dev . bus = & gameport_bus ;
gameport - > dev . release = gameport_release_port ;
if ( gameport - > parent )
gameport - > dev . parent = & gameport - > parent - > dev ;
2006-07-19 09:14:55 +04:00
INIT_LIST_HEAD ( & gameport - > node ) ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & gameport - > timer_lock ) ;
init_timer ( & gameport - > poll_timer ) ;
gameport - > poll_timer . function = gameport_run_poll_handler ;
gameport - > poll_timer . data = ( unsigned long ) gameport ;
}
/*
* Complete gameport port registration .
* Driver core will attempt to find appropriate driver for the port .
*/
static void gameport_add_port ( struct gameport * gameport )
{
2006-07-19 09:14:55 +04:00
int error ;
2005-04-17 02:20:36 +04:00
if ( gameport - > parent )
gameport - > parent - > child = gameport ;
gameport - > speed = gameport_measure_speed ( gameport ) ;
list_add_tail ( & gameport - > node , & gameport_list ) ;
if ( gameport - > io )
2010-01-06 04:56:03 +03:00
dev_info ( & gameport - > dev , " %s is %s, io %#x, speed %dkHz \n " ,
gameport - > name , gameport - > phys , gameport - > io , gameport - > speed ) ;
2005-04-17 02:20:36 +04:00
else
2010-01-06 04:56:03 +03:00
dev_info ( & gameport - > dev , " %s is %s, speed %dkHz \n " ,
2005-04-17 02:20:36 +04:00
gameport - > name , gameport - > phys , gameport - > speed ) ;
2006-07-19 09:14:55 +04:00
error = device_add ( & gameport - > dev ) ;
if ( error )
2010-01-06 04:56:03 +03:00
dev_err ( & gameport - > dev ,
" device_add() failed for %s (%s), error: %d \n " ,
2006-07-19 09:14:55 +04:00
gameport - > phys , gameport - > name , error ) ;
2005-04-17 02:20:36 +04:00
}
/*
* gameport_destroy_port ( ) completes deregistration process and removes
* port from the system
*/
static void gameport_destroy_port ( struct gameport * gameport )
{
struct gameport * child ;
child = gameport_get_pending_child ( gameport ) ;
if ( child ) {
gameport_remove_pending_events ( child ) ;
put_device ( & child - > dev ) ;
}
if ( gameport - > parent ) {
gameport - > parent - > child = NULL ;
gameport - > parent = NULL ;
}
2010-01-06 04:56:03 +03:00
if ( device_is_registered ( & gameport - > dev ) )
2005-04-17 02:20:36 +04:00
device_del ( & gameport - > dev ) ;
2006-07-19 09:14:55 +04:00
list_del_init ( & gameport - > node ) ;
2005-04-17 02:20:36 +04:00
gameport_remove_pending_events ( gameport ) ;
put_device ( & gameport - > dev ) ;
}
/*
* Reconnect gameport port and all its children ( re - initialize attached devices )
*/
static void gameport_reconnect_port ( struct gameport * gameport )
{
do {
if ( ! gameport - > drv | | ! gameport - > drv - > reconnect | | gameport - > drv - > reconnect ( gameport ) ) {
gameport_disconnect_port ( gameport ) ;
gameport_find_driver ( gameport ) ;
/* Ok, old children are now gone, we are done */
break ;
}
gameport = gameport - > child ;
} while ( gameport ) ;
}
/*
* gameport_disconnect_port ( ) unbinds a port from its driver . As a side effect
* all child ports are unbound and destroyed .
*/
static void gameport_disconnect_port ( struct gameport * gameport )
{
struct gameport * s , * parent ;
if ( gameport - > child ) {
/*
* Children ports should be disconnected and destroyed
* first , staring with the leaf one , since we don ' t want
* to do recursion
*/
for ( s = gameport ; s - > child ; s = s - > child )
/* empty */ ;
do {
parent = s - > parent ;
2007-04-10 08:40:48 +04:00
device_release_driver ( & s - > dev ) ;
2005-04-17 02:20:36 +04:00
gameport_destroy_port ( s ) ;
} while ( ( s = parent ) ! = gameport ) ;
}
/*
* Ok , no children left , now disconnect this port
*/
2007-04-10 08:40:48 +04:00
device_release_driver ( & gameport - > dev ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Submits register request to kgameportd for subsequent execution .
* Note that port registration is always asynchronous .
*/
void __gameport_register_port ( struct gameport * gameport , struct module * owner )
{
gameport_init_port ( gameport ) ;
gameport_queue_event ( gameport , owner , GAMEPORT_REGISTER_PORT ) ;
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( __gameport_register_port ) ;
2005-04-17 02:20:36 +04:00
/*
* Synchronously unregisters gameport port .
*/
void gameport_unregister_port ( struct gameport * gameport )
{
2006-02-19 08:22:03 +03:00
mutex_lock ( & gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
gameport_disconnect_port ( gameport ) ;
gameport_destroy_port ( gameport ) ;
2006-02-19 08:22:03 +03:00
mutex_unlock ( & gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_unregister_port ) ;
2005-04-17 02:20:36 +04:00
/*
* Gameport driver operations
*/
static ssize_t gameport_driver_show_description ( struct device_driver * drv , char * buf )
{
struct gameport_driver * driver = to_gameport_driver ( drv ) ;
return sprintf ( buf , " %s \n " , driver - > description ? driver - > description : " (none) " ) ;
}
static struct driver_attribute gameport_driver_attrs [ ] = {
__ATTR ( description , S_IRUGO , gameport_driver_show_description , NULL ) ,
__ATTR_NULL
} ;
static int gameport_driver_probe ( struct device * dev )
{
struct gameport * gameport = to_gameport_port ( dev ) ;
struct gameport_driver * drv = to_gameport_driver ( dev - > driver ) ;
drv - > connect ( gameport , drv ) ;
return gameport - > drv ? 0 : - ENODEV ;
}
static int gameport_driver_remove ( struct device * dev )
{
struct gameport * gameport = to_gameport_port ( dev ) ;
struct gameport_driver * drv = to_gameport_driver ( dev - > driver ) ;
drv - > disconnect ( gameport ) ;
return 0 ;
}
2009-04-14 02:27:49 +04:00
static void gameport_attach_driver ( struct gameport_driver * drv )
2006-07-19 09:14:55 +04:00
{
int error ;
2009-04-14 02:27:49 +04:00
error = driver_attach ( & drv - > driver ) ;
2006-07-19 09:14:55 +04:00
if ( error )
2010-01-06 04:56:03 +03:00
pr_err ( " driver_attach() failed for %s, error: %d \n " ,
2006-07-19 09:14:55 +04:00
drv - > driver . name , error ) ;
}
2008-06-06 09:33:22 +04:00
int __gameport_register_driver ( struct gameport_driver * drv , struct module * owner ,
const char * mod_name )
2005-04-17 02:20:36 +04:00
{
2008-06-06 09:33:22 +04:00
int error ;
2005-04-17 02:20:36 +04:00
drv - > driver . bus = & gameport_bus ;
2008-06-06 09:33:22 +04:00
drv - > driver . owner = owner ;
drv - > driver . mod_name = mod_name ;
/*
* Temporarily disable automatic binding because probing
* takes long time and we are better off doing it in kgameportd
*/
2009-05-10 03:08:05 +04:00
drv - > ignore = true ;
2008-06-06 09:33:22 +04:00
error = driver_register ( & drv - > driver ) ;
if ( error ) {
2010-01-06 04:56:03 +03:00
pr_err ( " driver_register() failed for %s, error: %d \n " ,
2008-06-06 09:33:22 +04:00
drv - > driver . name , error ) ;
return error ;
}
/*
* Reset ignore flag and let kgameportd bind the driver to free ports
*/
2009-05-10 03:08:05 +04:00
drv - > ignore = false ;
2008-06-06 09:33:22 +04:00
error = gameport_queue_event ( drv , NULL , GAMEPORT_ATTACH_DRIVER ) ;
if ( error ) {
driver_unregister ( & drv - > driver ) ;
return error ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( __gameport_register_driver ) ;
2005-04-17 02:20:36 +04:00
void gameport_unregister_driver ( struct gameport_driver * drv )
{
struct gameport * gameport ;
2006-02-19 08:22:03 +03:00
mutex_lock ( & gameport_mutex ) ;
2008-06-06 09:33:22 +04:00
2009-05-10 03:08:05 +04:00
drv - > ignore = true ; /* so gameport_find_driver ignores it */
2008-06-06 09:33:22 +04:00
gameport_remove_pending_events ( drv ) ;
2005-04-17 02:20:36 +04:00
start_over :
list_for_each_entry ( gameport , & gameport_list , node ) {
if ( gameport - > drv = = drv ) {
gameport_disconnect_port ( gameport ) ;
gameport_find_driver ( gameport ) ;
/* we could've deleted some ports, restart */
goto start_over ;
}
}
driver_unregister ( & drv - > driver ) ;
2008-06-06 09:33:22 +04:00
2006-02-19 08:22:03 +03:00
mutex_unlock ( & gameport_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_unregister_driver ) ;
2005-04-17 02:20:36 +04:00
static int gameport_bus_match ( struct device * dev , struct device_driver * drv )
{
struct gameport_driver * gameport_drv = to_gameport_driver ( drv ) ;
return ! gameport_drv - > ignore ;
}
2006-11-03 07:27:30 +03:00
static struct bus_type gameport_bus = {
. name = " gameport " ,
. dev_attrs = gameport_device_attrs ,
. drv_attrs = gameport_driver_attrs ,
. match = gameport_bus_match ,
. probe = gameport_driver_probe ,
. remove = gameport_driver_remove ,
} ;
2005-04-17 02:20:36 +04:00
static void gameport_set_drv ( struct gameport * gameport , struct gameport_driver * drv )
{
2006-02-19 08:22:03 +03:00
mutex_lock ( & gameport - > drv_mutex ) ;
2005-04-17 02:20:36 +04:00
gameport - > drv = drv ;
2006-02-19 08:22:03 +03:00
mutex_unlock ( & gameport - > drv_mutex ) ;
2005-04-17 02:20:36 +04:00
}
int gameport_open ( struct gameport * gameport , struct gameport_driver * drv , int mode )
{
if ( gameport - > open ) {
if ( gameport - > open ( gameport , mode ) ) {
return - 1 ;
}
} else {
if ( mode ! = GAMEPORT_MODE_RAW )
return - 1 ;
}
gameport_set_drv ( gameport , drv ) ;
return 0 ;
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_open ) ;
2005-04-17 02:20:36 +04:00
void gameport_close ( struct gameport * gameport )
{
del_timer_sync ( & gameport - > poll_timer ) ;
gameport - > poll_handler = NULL ;
gameport - > poll_interval = 0 ;
gameport_set_drv ( gameport , NULL ) ;
if ( gameport - > close )
gameport - > close ( gameport ) ;
}
2009-04-18 07:12:05 +04:00
EXPORT_SYMBOL ( gameport_close ) ;
2005-04-17 02:20:36 +04:00
static int __init gameport_init ( void )
{
2006-07-19 09:14:55 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2006-07-19 09:14:55 +04:00
error = bus_register ( & gameport_bus ) ;
if ( error ) {
2010-01-06 04:56:03 +03:00
pr_err ( " failed to register gameport bus, error: %d \n " , error ) ;
2006-07-19 09:14:55 +04:00
return error ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit gameport_exit ( void )
{
bus_unregister ( & gameport_bus ) ;
2010-11-15 12:39:57 +03:00
/*
* There should not be any outstanding events but work may
* still be scheduled so simply cancel it .
*/
cancel_work_sync ( & gameport_event_work ) ;
2005-04-17 02:20:36 +04:00
}
2006-02-19 08:22:51 +03:00
subsys_initcall ( gameport_init ) ;
2005-04-17 02:20:36 +04:00
module_exit ( gameport_exit ) ;